# Technical Documentation: metacraft-labs/codetracer

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/metacraft-labs/codetracer)

### Overview (`README.md`)

[](https://github.com/metacraft-labs/codetracer/actions/workflows/codetracer.yml)
[](https://discord.gg/aH5WTMnKHT)

# Introduction

CodeTracer is a user-friendly time-traveling debugger designed to support a wide range of programming languages.

It records the execution of a program into a sharable self-contained trace file. You can load the produced trace files in a GUI environment that allows you to move forward and backward
through the execution and to examine the history of all memory locations. They say a picture is worth a thousand words — well, a video is even better! Watch the demo below to see CodeTracer in action:

[](https://www.youtube.com/watch?v=xZsJ55JVqmU)

## The Benefits of Time-Travel

Compared to traditional debuggers, CodeTracer gives you two major superpowers:

* **Once you capture a bug in a recording, consider it squashed!**

  Bugs that are hard to reproduce can be painful to fix — you’ve surely been there. Once such a bug is captured with CodeTracer, you'll rarely need more than 30 minutes to track it down! This is largely a consequence of the next superpower:

* **Most bugs are easily revealed when you know the origin of any value in the program.**

  All programs produce output. Some examples are bytes generated as a response to a web request, pixels being drawn on your screen or perhaps a simple log line written to the terminal.

  When CodeTracer creates a recording, it captures a user-extensible set of output events relevant to the program. The GUI displays these events in a searchable chronological event log.

  Consider a misbehaving program that prints unexpected output to a log file midway through its execution. Clicking on the specific output event in CodeTracer will take you to the precise moment and code line where it was generated.

  The unexpected value must be originating from some variable that's being passed to the logging function. With CodeTracer, you can now ask the question "Where did this value come from?". CodeTracer will find another moment in the execution, potentially multiple seconds earlier, in a completely different part of the program where this particular memory location was last written to.

  This could be memory corruption or a genuine logical error. Either way, CodeTracer will report the origin. Let's say that you end up in the correct function that is responsible for computing the problematic value, but another input there leads to the issue. You can continue the search by repeating the question "Where did this input come from"? It usually takes just a few of these jumps to earlier moments in time to arrive at the root cause for the bug.

  Every time you jump to a new moment in the execution, you can fearlessly explore your surroundings by stepping forward or backwards, having access to a back button that can always get you to any previous point of interest. At every point of the journey, our novel UI shows you details about the past and future program states at a glance and you know your position in the precisely recorded call trace of the program.

  These features combined, make for a truly powerful debugging experience.

## Current state of the project and 2025 roadmap

The initial release of CodeTracer supports the Noir programming language. It has been developed in collaboration with the Blocksense team and currently requires the use of the [Blocksense Noir Compiler](https://github.com/blocksense-network/noir), which is included in the CodeTracer distribution.

CodeTracer’s open-source development is made possible by the generous support of Aztec Network, Arbitrum Foundation, and Aptos Foundation. During 2025, CodeTracer will evolve into a comprehensive Web3 development toolkit by gaining support for many additional smart contract and zero-knowledge programming languages. Most of its functionality will be packaged into reusable libraries that will power the creation of block explorers with advanced transaction tracing capabilities and omniscient debugging plugins for Visual Studio Code and other IDEs.

CodeTracer uses an [open format](https://github.com/metacraft-labs/runtime_tracing) for its trace files and we've started several community-driven projects which aim to add support for other programming languages:

* [Ruby](https://github.com/metacraft-labs/codetracer-ruby-recorder)
* [Python](https://github.com/metacraft-labs/codetracer-python-recorder)

Metacraft Labs is also developing an alternative back-end, capable of working with [RR](https://rr-project.org/) recordings, which will make CodeTracer suitable for debugging large-scale programs in a variety of system programming languages such as C/C++, Rust, Nim, D, Zig, Go, Fortran and FreePascal.

To shape our priorities and to help us understand the demographics of our user base better, please fill out [CodeTracer Developer Preferences Survey](https://form.typeform.com/to/M2Z28VFj?utm_source=Github).

To accelerate our development, please consider donating to our Open Collective campaign. Anyone who contributes more than €50 before June 2025 will get early access to our beta releases for system programming languages, forever.

[](https://opencollective.com/codetracer)

## The features of CodeTracer in more depth

Once you have time travel, you can re-imagine how a debugger works from the ground up! Here are some of the features of CodeTracer that set it apart from traditional debuggers:

### Omniscience

When CodeTracer breaks at a certain line of code, it knows not only the past but also the future. You can immediately see the values of all variables in the current function right next to the source code. This includes variables in loops where you can easily scroll through the iterations. Taken code branches are highlighted, while the non-executed code is immediately grayed out.



### Tracepoints

Many developers tend to transition from using interactive debugging early on in their careers, to mostly relying on carefully placed print statements as they start to face harder problems in distributed and real-time systems.

CodeTracer gives you the best of both worlds. A tracepoint allows you to see the effects of adding additional code to your program, without recompiling and rerecording it.

The added code can feature if statements, additional function calls and various ways to print or plot the captured data. The output is typically produced in seconds.



### Call Trace

Instead of a stack trace limited to a single moment in time, CodeTracer shows you the entire tree of function calls in the recorded execution. You can navigate and filter it in various ways and the clever collapsing and expanding algorithms still allow you to obtain a classic stack trace whenever you need it.



### State and History Explorer

Every variable in CodeTracer has a history. You can see all the values that the variable held during the entire execution of the program and for each of them you can jump to the originating expression that computed it. CodeTracer follows the path of trivial copies in assignments to save you extra time when tracking down more convoluted bugs.



### Event Log

The event log gives you a chronological view over anything of interest in your program, interleaved with the tracepoint outputs. Clicking on any event takes you to the precise moment in time when it was produced, which gives you excellent starting points for your investigations. Tracking down the origins of anomalous events and program states through the history of the involved variables is the essence of debugging with CodeTracer.



### Terminal Output

The terminal output panel renders the recorded `stdout` and `stderr` events like a standard terminal. Clicking anywhere in the output takes you to the exact moment when the specific character was generated. This allows for a fallback to a more traditional print-style debugging when the expressivity of tracepoints is not sufficient. In the future, CodeTracer will allow you to slide through the states of the terminal through time which will help for debugging highly interactive TUI apps.



### Mouse Stepping

Since CodeTracer is aware of all past and future control flow, it offers some convenient shortcuts for quickly navigating the program execution by interacting with its source code:

* Jump to a line: Middle-click on any undimmed line in the current function to quickly step to it. If you use a mouse without a middle button, you can achieve the same with `Ctrl+Click` or by pressing `Ctrl+F8`(as a special kind of `continue`) after navigating to the respective line with your keyboard.
* Jump into a call: Double middle-click on the function name in a call expression to jump into it. If you use a mouse without a middle button, you can achieve the same with `Ctrl+Alt+Click` or by pressing `Ctrl+F11`(as a special kind of `step-in`) after navigating to the respective call expression with your keyboard.

Explore the right-click context menu for additional operations.

### Scratchpad

The scratchpad provides a play area where you can pin values from different locations and moments in time. You can explore their differences both manually and algorithmically to gain quick insights into the behavior of your program.

# Installing CodeTracer

## Recommended: using the CodeTracer installer
>
> [!WARNING]
> On NixOS, this script currently installs CodeTracer as a raw AppImage, since our Nix package is not yet merged in nixpkgs.
> Nix users which want to use the package should install it manually from source.

On any system, you can use the CodeTracer installer script by running the following command:

```
user $ curl -fsSL https://get.codetracer.com/sh | sh
```

On Debian, Ubuntu, Fedora and RHEL the installer registers the Metacraft Labs package
repository (`deb.metacraft-labs.com` / `rpm.metacraft-labs.com`) and installs the `codetracer`
package, so later updates arrive with `apt upgrade` / `dnf upgrade`. On Arch it uses your AUR
helper, on Gentoo the metacraft-overlay, and on macOS the signed DMG from downloads.codetracer.com.

You may be asked for sudo access depending on your installation method.

This script is handy, because it installs CodeTracer in the best possible manner depending on your OS.

> [!CAUTION]
> CodeTracer requires ruby to be installed through [homebrew](https://brew.sh) for ruby support. It's recommended that you install homebrew if possible
> so that you can automatically get ruby support through the installer.

## Installing CodeTracer binaries manually

Click on the icons below that corresponds to your distribution or packaging model:

<a href="https://deb.metacraft-labs.com/"><img width="100px" height="100px" src="https://upload.wikimedia.org/wikipedia/commons/9/9e/UbuntuCoF.svg"></a>
<a href="https://deb.metacraft-labs.com/"><img width="100px" height="100px" src="https://upload.wikimedia.org/wikipedia/commons/6/66/Openlogo-debianV2.svg"></a>
<a href="https://rpm.metacraft-labs.com/"><img width="100px" height="100px" src="https://upload.wikimedia.org/wikipedia/commons/d/d8/Red_Hat_logo.svg"></a>
<a href="https://rpm.metacraft-labs.com/"><img width="100px" height="100px" src="https://upload.wikimedia.org/wikipedia/commons/3/3f/Fedora_logo.svg"></a>
<a href="https://github.com/metacraft-labs/metacraft-overlay"><img width="100px" height="100px" src="https://upload.wikimedia.org/wikipedia/commons/4/48/Gentoo_Linux_logo_matte.svg"></a>
<a href="https://aur.archlinux.org/packages/codetracer"><img width="100px" height="100px" src="https://upload.wikimedia.org/wikipedia/commons/1/13/Arch_Linux_%22Crystal%22_icon.svg"></a>
<a href="https://downloads.codetracer.com/CodeTracer-latest-amd64.AppImage"><img width="100px" height="100px" src="https://upload.wikimedia.org/wikipedia/commons/7/73/App-image-logo.svg"></a>
<a href="https://downloads.codetracer.com/CodeTracer-latest-arm64.dmg"><img width="75px" height="100px" src="https://upload.wikimedia.org/wikipedia/commons/1/1b/Apple_logo_grey.svg"></a>

PGP Key signatures:

[](https://downloads.codetracer.com/CodeTracer-latest-arm64.dmg.asc)
[](https://downloads.codetracer.com/CodeTracer-latest-amd64.AppImage.asc)
[](https://downloads.codetracer.com/CodeTracer.pub.asc)

> [!CAUTION]
> When launched for the first time, macOS users will see the error message "CodeTracer is damaged and can't be opened".
> To resolve this problem, please execute the command `user $ xattr -c <path/to/CodeTracer.app>`.
>
> We expect this inconvenience will be remedied soon through our enrollment in the Apple Developer program which will ensure CodeTracer is properly signed and whitelisted by Apple.
> See [this discussion](https://discussions.apple.com/thread/253714860?sortBy=rank) for more details.

> [!CAUTION]
> Recording ruby on macOS requires you to install ruby through [homebrew](https://brew.sh), otherwise trying to record ruby programs will fail
> due to the built-in ruby binary on macOS being more than 7 years old.
>
> Once homebrew is installed, simply install ruby with `user $ brew install ruby`.

## Building from source

### Linux

The Metacraft Labs team is using [Nix](https://nixos.org/) to define a reproducible development environment for working on CodeTracer. Linux is our primary development platform, while some of our team members use macOS. Building on Windows will be supported in the near future.

To enter the Nix development environment, perform the following steps:

1) [Install Nix](https://zero-to-nix.com/start/install/).
2) Clone this repository.
3) At the repository root, execute `git submodule update --init --recursive`.
4) At the repository root, execute `nix develop` (or `direnv allow` for users of `direnv`).
5) In the resulting shell, you can build all targets by running `just build-once` or `just build` if you intend to make continuous changes to the source code.

#### NixOS

On NixOS, you can install our Nix package by running `just build-nix`.

### macOS

On macOS, we do not yet have support for Nix, instead we compile by using the non-nix-build scripts:

1. Clone this repository
1. Open a terminal and run `bash non-nix-build/build.sh`
1. Wait until it finishes installing and packaging
1. The end result will be a `CodeTracer.dmg` file that can be found under the `non-nix-build` folder.
1. For quicker testing without manually unpacking CodeTracer every time you can experiment with the contents of the `non-nix-build/CodeTracer.app` folder, which is equivalent to the final app bundle

### Windows (non-Nix bootstrap)

For Windows local development without Nix, use the scripts under `non-nix-build/windows/`:

1. Inspect pinned versions and source refs (Rust, Node.js, uv, Nim, ct-remote, Cap'n Proto, Tup): `non-nix-build/windows/toolchain-versions.env`
1. In Git Bash, source the environment (auto-installs missing tools): `source env.sh`
1. In PowerShell, dot-source the environment (auto-installs missing tools): `. .\env.ps1`

This workflow bootstraps pinned tools into a shared user cache (`%LOCALAPPDATA%/codetracer/windows-diy` by default) and keeps setup deterministic for Windows contributors.
When `env.sh` is sourced, it also ensures `libs/tree-sitter-nim/src/parser.c` is generated when missing or stale.
The parser script prefers local `libs/tree-sitter-nim/node_modules/.bin/tree-sitter` and falls back to an isolated cached `tree-sitter-cli` install when needed, so generation does not require building native addons for the full grammar package.
`env.sh` also restores caller shell options (it does not leave `set -e` enabled) and auto-installs missing Node deps in `node-packages` by default so `stylus.cmd`/`webpack.cmd` are available for `tup`/`just` (`WINDOWS_DIY_SETUP_NODE_DEPS=0` disables this behavior).
For `ct-remote`, the default is `CT_REMOTE_WINDOWS_SOURCE_MODE=auto`: on Windows x64 it uses local `../codetracer-ci` source publish output when available and otherwise falls back to pinned download, while on Windows arm64 it routes to local source and does not fall back to pinned x64 download. `CT_REMOTE_WINDOWS_SOURCE_RID` defaults by architecture (`win-x64` on x64, `win-arm64` on arm64) and remains overridable. Local-source publish now prefers `CT_REMOTE_WINDOWS_PUBLISH_SCRIPT` (default `<CT_REMOTE_WINDOWS_SOURCE_REPO>/non-nix-build/windows/publish-desktop-client.ps1`) so host-global `dotnet` is not required when that helper is present. Use `CT_REMOTE_WINDOWS_SOURCE_MODE=local|download` and `CT_REMOTE_WINDOWS_SOURCE_REPO=<path>` as needed (`download` is x64-only).
For Nim, the default is `NIM_WINDOWS_SOURCE_MODE=auto`: try building from pinned Nim/csources source refs with deterministic cache reuse, and fall back to pinned `nim-<version>_x64.zip` when source bootstrap fails. Override with `NIM_WINDOWS_SOURCE_MODE=source|prebuilt`.
For Cap'n Proto, the default is `CAPNP_WINDOWS_SOURCE_MODE=auto`: use pinned prebuilt ZIP on Windows x64 and source mode on non-x64, with deterministic source-cache reuse. Override with `CAPNP_WINDOWS_SOURCE_MODE=source|prebuilt`.
For Tup, the default is `TUP_WINDOWS_SOURCE_MODE=auto`: build from pinned source (`https://github.com/zah/tup.git` ref `variants-for-windows`) with deterministic source-cache reuse to preserve Windows variant support. `auto` only falls back to prebuilt when you explicitly provide both `TUP_WINDOWS_PREBUILT_URL` and `TUP_WINDOWS_PREBUILT_SHA256`; otherwise it remains source-only by design. Override with `TUP_WINDOWS_SOURCE_MODE=source|prebuilt`.

## The CodeTracer CLI

When you launch the CodeTracer GUI, it will offer you the option to also install the CodeTracer CLI. It provides convenient ways to create and load trace files from the command-line or to integrate CodeTracer w
ith CI processes.

Run `ct --help` to see the full list of supported subcommands, but the most commonly used ones are the following:

`<application>` can be a source file or a project folder (depending on the language):

1. `ct run <application>` - Creates a recording and load it in CodeTracer with a single command.
1. `ct record <application>` - Creates a trace file that can be loaded later or shared. On Windows, recordings use the TTD backend (`ct record --backend ttd`) and require an elevated shell. For Python scripts this reuses the interpreter you would get by running `python` in the same shell (honoring the `CODETRACER_PYTHON_INTERPRETER`, `PYTHON_EXECUTABLE`, `PYTHONEXECUTABLE`, and `PYTHON` environment variables before falling back to `PATH`) and that interpreter must have the `codetracer_python_recorder` package installed (run `ct doctor python` to verify).
1. `ct replay` - Launches the CodeTracer GUI with a previously recorded trace file. Common usages are:
   * `ct replay` - Opens a simple console-based dialog to choose what recording you want to replay.
   * `ct replay <program-name>` - Opens the last trace of an application.
   * `ct replay --id=<trace-id>` - Opens a trace by its trace id.
   * `ct replay --trace-folder=<trace-folder>` - Opens a trace by its trace folder.
1. `ct` - Launches the startup screen of the CodeTracer GUI.
1. `ct help`/`ct --help` - Gives you a help message.
1. `ct version`/`ct --version` - Returns the current version of CodeTracer.

## Keyboard Shortcuts

You can learn all CodeTracer keyboard shortcuts by examining the main menu and the tooltips over all buttons in the interface.
The default configuration should be familiar to users experienced with Microsoft Visual Studio™.

Since CodeTracer provides a reverse counterpart to most traditional debugging operations, we typically use the `Shift` modifier to indicate this (e.g. `F10` is "Next Step" while `Shift+F10` is "Previous Step").

The user config file located at `~/.config/codetracer/.config.yml` allows you to specify custom shortcuts for all operations.

# Contributing

Check out our [Contributors Guide](./CONTRIBUTING.md) for more details.

## License

CodeTracer is distributed under the GNU Affero General Public License (AGPLv3).

The wasm-sysroot is copied from the tree-sitter repo: (<https://github.com/tree-sitter/tree-sitter>) : specifically <https://github.com/tree-sitter/tree-sitter/tree/master/crates/language/wasm/include>. The C headers there are a modified version of glibc headers, which are originally under LGPL license.

# Miscellaneous

## Small Language Constructs

CodeTracer includes a miniature Lisp-like language called **small** used in some of the example programs. The constructs demonstrate how a language can integrate with the [`runtime_tracing`](https://github.com/metacraft-labs/runtime_tracing) format.

### Core constructs

| Construct | Example | Description |
|-----------|---------|-------------|
| `(defun name (params...) expr...)` | `(defun add-one (x) (add x 1))` | Define a function |
| `(set var value)` | `(set x 1)` | Assign a value |
| `(set-deref ref value)` | `(set-deref r 5)` | Write through a reference |
| `(vector v1 v2 ...)` | `(vector 1 2)` | Create a vector |
| `(# vec index)` | `(# v 0)` | Indexing |
| `(push vec value)` | `(push v 3)` | Append to a vector |
| `(loop i from to body)` | `(loop i 0 3 (print i))` | Simple loop |
| `(ref var)` | `(ref x)` | Create a reference |
| `(deref ref)` | `(deref r)` | Read from a reference |
| `(add a b)` | `(add 1 2)` | Integer addition |
| `(print expr ...)` | `(print x)` | Print to stdout |
| `(write-file path data)` | `(write-file "out.txt" "hi")` | Write a file |

### runtime\_tracing events

| Event | Purpose |
|-------|---------|
| `Write` | Terminal output produced by `print` |
| `WriteFile` | File creation via `write-file` |
| `WriteOther` | Other write operations |
| `Read` | Reads from standard input |
| `ReadFile` | Reading from files |
| `ReadOther` | Miscellaneous read operations |
| `ReadDir` | Directory listing |
| `OpenDir` | Opening a directory |
| `CloseDir` | Closing a directory |
| `Socket` | Network socket activity |
| `Open` | Opening files or sockets |
| `Error` | Error reporting |
| `TraceLogEvent` | Events generated by the tracer |

## Contributing

Check out our [Contributors Guide](./CONTRIBUTING.md) for more details.

## License

CodeTracer is distributed under the GNU Affero General Public License (AGPLv3).

The wasm-sysroot is copied from the tree-sitter repo: (<https://github.com/tree-sitter/tree-sitter>) : specifically <https://github.com/tree-sitter/tree-sitter/tree/master/crates/language/wasm/include>. The C headers there are a modified version of glibc headers, which are originally under LGPL license.

---

### Overview (`browser-replay/README.md`)

# `browser-replay` — Browser-Based MCR Replay Client

This directory contains the browser-side CodeTracer MCR replay
client. The client loads a CodeTracer trace into an in-memory
WebAssembly virtual filesystem and serves the standard CodeTracer
DAP/replay protocol from a Web Worker — letting users step through
a recorded trace inside their browser without launching a native
`ct host` process.

For the full design and milestone history, see
[`codetracer-specs/Planned-Work/Browser-Replay.status.org`](../../codetracer-specs/Planned-Work/Browser-Replay.status.org)
and
[`codetracer-specs/Planned-Work/Unified-Browser-Replay-Architecture.md`](../../codetracer-specs/Planned-Work/Unified-Browser-Replay-Architecture.md).

## Two trace-loading paths

The client supports two trace-loading paths:

### 1. Gateway-authenticated load (M40, production)

The client fetches the recording manifest and trace bytes through
codetracer-ci's authenticated browser-replay gateway endpoints:

- `GET /api/v1/observability/gateway/manifests/{traceId}` — manifest fetch.
- `GET /api/v1/observability/gateway/ranges/{traceId}/{**objectKey}` — byte fetch with HTTP Range.
- `POST /api/v1/observability/gateway/credential-validate` — precondition probe (optional).

This is the path used in production. Users get to a CodeTracer
debug session via a codetracer-ci debug-session URL; codetracer-ci
issues a short-lived bearer token tied to the user's
`replay.read` role on the trace's tenant; the browser-replay client
loads the trace through the gateway with that token.

The endpoint reference lives at
[`codetracer-ci/rewrite-docs/04-apis-events/http-api.md`](../../codetracer-ci/rewrite-docs/04-apis-events/http-api.md)
section 4.11. The data-path overview is in
[`codetracer-specs/Observability-Platform/docs/direct-storage-data-path.md`](../../codetracer-specs/Observability-Platform/docs/direct-storage-data-path.md).

### 2. Static-asset load (legacy, tests)

The client can also fetch a trace directly from a static HTTP
server. This path predates M40 and is used by the local nginx
test harness (`test-server.sh`, `nginx.conf`) for development
iteration on the WASM module itself.

## URL contract

The client reads its configuration from the URL query string. The
`gateway-authenticated load` path is selected when both
`gatewayBaseUrl` and `traceId` are set; the legacy static path is
selected otherwise.

```
https://<host>/index.html
  ?gatewayBaseUrl=https://codetracer.example.com
  &traceId=019e0744-31cf-7ad9-b350-f8d2e3eda5a2
  &authToken=<bearer-token>
```

Required query parameters for the gateway path:

- `gatewayBaseUrl` — the codetracer-ci base URL. The client
  appends `/api/v1/observability/gateway/manifests/{traceId}`
  and `/api/v1/observability/gateway/ranges/{traceId}/{**}` to it.
- `traceId` — the trace UUID to load.
- `authToken` — bearer token. Sent as
  `Authorization: Bearer <authToken>` on every fetch. Production
  deployments mint short-lived tokens server-side and inject them
  into the URL when redirecting users from a debug-session link.

Optional:

- `replicaIndex` (integer) — when the manifest exposes multiple
  replicas per shard, force the client to use a specific one.
  Defaults to `0`.

## Cross-origin and auth model

The gateway endpoints require user-bearer authentication and the
`replay.read` operation on the trace's tenant. The client sends
`Authorization: Bearer <authToken>` on every fetch. CORS preflights
are NOT required when the client is served from the same origin as
the gateway — the recommended deployment is a single nginx vhost
that serves both the static client assets and reverse-proxies to
codetracer-ci.

For end-to-end local testing without nginx, see
[`codetracer-ci/tests/CrossRepo.Test.Integration/BrowserReplayClientTests.cs`](../../codetracer-ci/tests/CrossRepo.Test.Integration/BrowserReplayClientTests.cs):
the cross-repo test mounts the `app/` bundle on the same port as
the gateway via `CrossRepoTestServer.MountStaticFiles`, so the
browser sees a single origin and Playwright can drive the whole
flow without CORS configuration.

## Files

| File                          | Purpose                                                                                                            |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `app/index.html`              | Thin scaffold; documents the URL contract; loads `gateway-client.js`.                                              |
| `app/gateway-client.js`       | Main-thread driver. Reads URL params; boots the worker; sends DAP `initialize` / `launch` / `configurationDone`.   |
| `app/worker.js`               | Web Worker that loads the WASM module. Two message types: `load-trace` (legacy static fetch) and `load-trace-from-gateway` (M40). |
| `app/pkg/db_backend.js`       | Generated wasm-bindgen JS bindings (rebuilt from `src/db-backend/wasm-testing/`).                                  |
| `app/pkg/db_backend_bg.wasm`  | Generated WASM module.                                                                                             |
| `nginx.conf`, `start-server.sh`, `stop-server.sh`, `setup-certs.sh` | Local nginx test harness for the legacy static-fetch path. Not used by the gateway-authenticated path.            |
| `test-server.sh`              | One-shot wrapper around the nginx harness.                                                                         |
| `traces/`                     | Hardcoded test traces for the legacy static path.                                                                  |

## Testing

The end-to-end test is
[`codetracer-ci/tests/CrossRepo.Test.Integration/BrowserReplayClientTests.cs`](../../codetracer-ci/tests/CrossRepo.Test.Integration/BrowserReplayClientTests.cs)
(`browser_replay_client_fetches_manifest_and_ranges_through_gateway`).

It:

1. Spins up a real `DistributedHttpStorageNode` Python subprocess.
2. Spins up a real `CrossRepoTestServer` with the real
   `BrowserGatewayEndpoints` route group and the real
   `IS3Provider` / `IObservabilityAuditSink`.
3. Stages real payload bytes onto the storage node and seeds a
   `ShardedMcrSegmentManifest` whose replica points at the node's
   HTTP base URL.
4. Mounts the `browser-replay/app/` bundle on the same origin as
   the gateway.
5. Launches headless Chromium via `Microsoft.Playwright`.
6. Navigates to
   `index.html?gatewayBaseUrl=...&traceId=...&authToken=...`.
7. Hard-asserts the manifest fetch returned `200`, at least one
   range fetch returned `206`, the storage node's `payloadReads`
   counter strictly increased (proves the gateway HTTP-proxied to
   the storage node), and the audit sink recorded
   `gateway.manifest.read` and `gateway.range.read` events with
   `status="allowed"`.

Run via:

```bash
cd codetracer-ci
nix develop ./nix -c dotnet test \
  tests/CrossRepo.Test.Integration/CrossRepo.Test.Integration.csproj \
  -c Release \
  --filter "FullyQualifiedName~BrowserReplayClientTests" \
  -p:NuGetAudit=false
```

## Building the WASM bundle

The `app/pkg/` directory is a build output and is `.gitignore`d.
Re-stage it from `src/db-backend/wasm-testing/pkg/` after building
the WASM module:

```bash
cd src/db-backend/wasm-testing
just build         # or: cargo build --target wasm32-unknown-unknown
cd -
cp -r src/db-backend/wasm-testing/pkg/* browser-replay/app/pkg/
```

`build-dist.sh` automates this for distribution-ready bundles.

## Known limitations

- Materialized traces (Python / Ruby / JavaScript): the M40 gateway
  endpoints serve materialized artifacts the same way as MCR shards
  (each artifact has an object key in the manifest), but the
  WASM-side replay engine for materialized traces is still in
  development. The client returns an `unsupported-backend` error
  message when handed a manifest with `kind=materialized_trace`
  for a runtime that hasn't shipped its replay engine yet.
- Token refresh: the URL-embedded `authToken` is short-lived and
  does NOT auto-refresh. Production deployments should mint
  long-enough tokens to cover an expected replay session, or wire
  in a refresh flow via the parent page.

---

### Overview (`get/README.md`)

# `get/` — source for https://get.codetracer.com

This directory is the source tree for CodeTracer's install/onboarding host,
`get.codetracer.com`, per
[product-install-domains.md](https://github.com/metacraft-labs/metacraft-dev-guidelines/blob/main/policies/product-install-domains.md).

It exposes three surfaces:

| URL | Served file | Purpose |
| --- | --- | --- |
| `https://get.codetracer.com/` | `index.html` | Onboarding / install landing page |
| `https://get.codetracer.com/sh` | `install-on-distributions.sh` (re-served) | `curl -fsSL https://get.codetracer.com/sh \| sh` |
| `https://get.codetracer.com/pwsh` | `install.ps1` or `pwsh.stub.ps1` | `irm https://get.codetracer.com/pwsh \| iex` |

## How it is built and served

`build-get.sh` assembles `dist/`:

- `index.html`, `_headers` — copied verbatim.
- `sh` — the repo's **canonical** `install-on-distributions.sh` with a provenance
  header injected after the shebang (single-sourced, never a divergent copy).
- `pwsh` — the repo's `install.ps1` if present, otherwise `pwsh.stub.ps1`
  (a "coming soon" stub that exits non-zero and points at the desktop app /
  releases / WSL). CodeTracer has no PowerShell installer yet, so the stub ships.

`_headers` forces `Content-Type: text/plain; charset=utf-8` + a short cache TTL on
`/sh` and `/pwsh`, so `curl … | less` shows the source and a fixed URL always
yields the current bootstrapper.

The artifact **store** remains GitHub Releases / `downloads.codetracer.com`;
`get.codetracer.com` is only the advertised UX.

## Deploy

`.github/workflows/deploy-get.yml` runs `build-get.sh` and
`wrangler pages deploy get/dist --project-name=get-codetracer --branch=master`
on push to `stable` (paths `get/**`, `install-on-distributions.sh`, `install.ps1`).
The `get-codetracer` Pages project's production branch is `master`, so the
`--branch master` deploy publishes production (served on the custom domain).

Requires repo secrets `CLOUDFLARE_API_TOKEN` (Pages:Edit) and
`CLOUDFLARE_ACCOUNT_ID` (`803741d99690718276ea30950f690c46`).

Build locally: `bash get/build-get.sh && ls get/dist`.

---

### Overview (`refs/README.md`)

# References

Workspace-managed reference checkouts used for design study live here. They are
declared in the workspace manifests (`repos/codemirror-*.toml`) and cloned by
`repro`, so they are ignored by this repository the same way
`reprobuild/references/` is: the checkouts belong to the workspace, not to this
repo's history.

Product code must not import or link these checkouts directly.

Currently declared:

- `codemirror-state`, `codemirror-view`, `codemirror-commands`,
  `codemirror-collab` — CodeMirror 6 (MIT). The design reference for the
  headless editor ViewModel: change sets and transactions, the decoration and
  widget model, the named-command vocabulary, and collaborative editing by
  rebase against a central authority.

---

### Overview (`storybook/README.md`)

# CodeTracer Storybook

Storybook harness for isolated CodeTracer IsoNim panels, shell views,
building-block components, and default layouts.

## Commands

```bash
just build-storybook-components
just storybook-check-styles
just storybook
just storybook-build
```

The Nim bundle exports Storybook mount functions from
`src/frontend/storybook_components.nim` into `storybook/dist/components.js`.
Stories load that bundle and mount real ViewModels through the production
IsoNim views.

Storybook loads CodeTracer styles by parsing the production
`src/frontend/index.html`; do not copy app stylesheet lists or component visual
CSS into stories. Keep Storybook CSS limited to harness sizing/layout.

Fixtures may mock ViewModel/store state, but stories must not reimplement
component DOM in JavaScript. The DOM under test should come from the real
`src/frontend/viewmodel/views/*.nim` render or mount functions.

## Visual Review

The repeatable screenshot loop lives in `tools/visual-review/`:

```bash
nix shell nixpkgs#nodejs --command node tools/visual-review/capture-storybook.mjs
nix shell nixpkgs#nodejs --command node tools/visual-review/capture-storybook.mjs --view terminal-output --size laptop --no-build
```

The capture tool builds Storybook, serves `storybook/storybook-static`, captures
named stories at the configured viewport sizes, and writes diagnostics beside
the screenshots. It also writes HTML and computed-style dumps that can be
compared against reference dumps from older known-good builds:

```bash
nix shell nixpkgs#nodejs --command node tools/visual-review/compare-style-dumps.mjs \
  --reference ../codetracer-main/ui-tests/reference-dumps/isonim-karax-reference-20260504T155511Z/20260504T160434Z_noir-terminal-visible.computed-styles.json \
  --current tools/visual-review/dumps/terminal-output-laptop.computed-styles.json \
  --out tools/visual-review/reports/terminal-vs-karax-reference.md
```

The generated `reports/`, `screenshots/`, and `dumps/` directories are ignored;
keep the project brief, screen briefs, and capture/comparison scripts in git.

---

### Overview (`ci/runner/README.md`)

# Self-hosted macOS runner defects — the register

These runners are **persistent**. Their home directory, work directory, git
config, nix store and background daemons survive between jobs, so a defect one
job leaves behind is inherited by whatever runs next — usually a different job,
often a different repository. That is what makes this class expensive: **the job
that fails is almost never the job at fault**, and the message it fails with
tends to name neither.

This file exists because the list below lived only in one person's head. Seven
defects were diagnosed over one campaign; the count was reported as five twice
before being reconciled upward. A register that is not in the tree gets
recounted from memory, and memory rounds down.

## Rules this list is maintained by

- **A fix needs a gate that was proved able to fail.** Not re-read — *run*
  against the broken input, and seen to name what it found. Two checks in this
  campaign's history could only ever pass: a `*.lock` sweep whose first version
  matched anywhere (it was deleting `Cargo.lock`, `flake.lock`, `repro.lock` and
  `yarn.lock` from sibling checkouts — a sweep meant to unblock checkout was
  quietly unpinning the build), and a credential recount that `494d85395`
  records as "itself a no-op".
- **Never weaken a check to make it green, and never edit an expected count
  down.** Several corrections here went *up*.
- **Prefer planting the failure to arguing about the code.** Where a gate below
  has a contract suite, the negative case is the real pre-fix file read out of
  `origin/dev` rather than a mock of it.
- **A partial fix is recorded as partial.** Two entries below are mitigations
  whose root cause is still unidentified or unaddressed; saying so is the point
  of the entry.

---

## 1. The `repro` daemon never `chdir`s — CLOSED here, cause still upstream

**Symptom.** Any `repro build` on the runner dies with:

```
daemon-hosted build failed: No such file or directory
```

naming no path and no owner.

**Cause.** The `repro` user daemon is persistent and its endpoint is
deliberately stable across `nix develop` sessions, so one daemon serves every
job on a runner indefinitely. On macOS it is started via launchd, and when that
fails it falls back to a plain fork+setsid: `launchWithFork` in reprobuild's
`libs/repro_daemon_core/src/repro_daemon_core/runtime.nim` calls `setsid()` and
then `execve` **with no `chdir` between them**. `chdir` and `setCurrentDir`
appear nowhere in that library, and no launchd plist written here sets
`WorkingDirectory`. The daemon therefore inherits — and keeps — the working
directory of whichever `repro build` first spawned it. Once that directory is
gone (a re-cloned workspace, a deleted temp root), every later build dies in the
first statement of the daemon-side build executor, `getCurrentDir()`.

**State.** `a556399c0` diagnosed this and named two victims,
`reprobuild-macos-smoke` and `test-ui-tests (macos)`, but guarded only the
first. `ci/reprobuild/macos-daemon-build.sh` — the script that *deliberately*
runs `repro build --daemon=auto`, and so the likeliest thing on a runner to
leave a daemon behind — had no `repro daemon stop` anywhere, including its trap.
It now stops the daemon before building and on every exit path.

A second defect surfaced while fixing the first: that script's only
`trap ... EXIT` was armed **inside** `start_runquotad`, which returns early when
`RUNQUOTA_SOCKET` is already set. On that path the script had no exit handler at
all, so it leaked `runquotad` and its socket directory too. `trap` replaces
rather than composes; the handler is now armed once, at top level.

**Gate.** `ci/verdict/reprobuild-daemon-guard.sh`, suite
`ci/test/reprobuild-daemon-guard-test.sh` (9 assertions, in the bash lint lane).
It requires a stop before the first build, exactly one EXIT trap armed at top
level, and a stop inside the handler that trap names. Comments are stripped
before matching — these scripts discuss `repro daemon stop` at length, and a
checker satisfied by its own explanation is the defect it was written against.
It refuses to pass on an empty directory.

**STILL OPEN, upstream.** The durable fix is one line in another repo: perform
`chdir("/")` in the fork path, and let the daemon tolerate a vanished cwd.
Until then codetracer only stops being both cause and victim of its own jobs;
any other product's `repro build` on these runners can still poison them.

---

## 2. Stale git `extraheader` credential → HTTP 401 — MITIGATION, NOT A CURE

**Symptom.** `actions/checkout` fails to authenticate; git reports
`could not read Username`, which is a 401 in disguise.

**Cause.** A stale `http.<url>.extraHeader` in `$HOME/.gitconfig` carrying a dead
token. Git resolves `http.<url>.*` by **longest matching prefix**, so a narrower
stale entry outranks the fresh, correctly-scoped one that checkout installs.

**State.** An inline pre-checkout sweep,
`Evict stale git credentials from this runner's persistent home`, duplicated
across **8 jobs** in `.github/workflows/codetracer.yml` — lines 1802, 2034, 2236,
3569, 4094, 4651 (canonical copy, carries the full rationale), 5212 and 6085. It
must run *before* `actions/checkout`, so it cannot be a script this repository
provides. It matches only `http.*.extraheader`, `http.*.extraHeader` and
`url.*x-access-token*`, operates on `$HOME/.gitconfig` **by path** (not
`--global`, because XDG can redirect that), and recounts in pure bash — `grep`
and `sed` are not on a raw `run:` step's PATH on these nix-darwin runners.

Introduced by `083aa0c23`.

**STILL OPEN.** The step's own comment
(`.github/workflows/codetracer.yml:4698-4706`) says so:

> STILL UNKNOWN: WHO WRITES IT. `configure-git-auth.sh` in
> metacraft-labs/metacraft-github-actions installs exactly this key but
> deliberately passes it through `$GITHUB_ENV`, which is job-scoped, so on its
> documented path nothing should reach this file. Something on these runners
> writes it anyway (mcl-004's copy was stamped 08:15 on the day of the outage).
> Until that writer is found this step is a mitigation, not a cure — it runs
> before every checkout precisely because the poison can come back.

Finding the writer is the open work. Note also that the sweep asserts a
**non-zero** count on a poisoned host rather than merely a zero afterwards —
that distinction is what `494d85395` was about.

---

## 3. Stale `*.lock` sentinels blocking checkout — CLOSED

**Cause.** Git metadata locks (`index.lock` and friends) left by a killed job
make the next checkout fail.

**State.** `Sweep stale git lock files from the persistent work directory`,
`.github/workflows/codetracer.yml:5981`, in `visual-replay-regression-gate` —
the only persistent `[self-hosted, gpu]` job, hence the only copy.

**Read this before touching the pattern.** The first version matched
`-name '*.lock'` anywhere under the work directory and, measured on run
33734457928, deleted 42 files including `codetracer-trace-format/Cargo.lock`,
`flake.lock`, `nim-agents/repro.lock` and
`isonim/src/isonim/layout/yoga/yarn.lock`. A sweep meant to unblock checkout was
quietly unpinning the build. The current pattern is anchored:

```sh
find "$root" -type d -name .git -exec find {} -type f -name '*.lock' -mmin +60 ';'
```

The outer `find` restricts to directories literally named `.git`, so no
lockfile in a worktree is reachable; `-mmin +60` protects locks held by a
concurrent job. Both constraints are load-bearing — **do not flatten this into a
single `find`.**

Fixed by `083aa0c23`, anchored by `444cfa989`.

---

## 4. Read-only `.app` leftovers make runners uncleanable — CLOSED

**Cause.** A Nix-store-mode `.app` bundle is copied out read-only; the next
job's checkout cannot delete it.

**State.** `ci/runner/sweep-readonly-leftovers.sh`, plus a `chmod -R u+w`
remediation at `.github/workflows/codetracer.yml:6068-6083`. Fixed at the source
(the bundle is made writable when produced) *and* swept where already poisoned.
Landed in `444cfa989`.

**Gate.** `ci/test/readonly-leftovers-sweep-test.sh`, in the bash lint lane. It
asserts a **non-zero** count on a poisoned fixture, not merely a zero
afterwards.

---

## 5 & 6. `/usr/bin` missing from a raw `run:` step's PATH → exit 127 — CLOSED per site, STRUCTURALLY OPEN

These were logged as two defects and are one root cause, which is why they kept
recurring under new names.

**Cause.** `nix develop` prepends store paths and keeps the ambient PATH as the
tail. On these nix-darwin runners a raw `run:` step's ambient PATH has no
`/usr/bin`, so any Apple system tool is simply absent — exit 127, "command not
found", for a binary that is obviously installed.

**Victims, in the order they were found:** `codesign`, then `sw_vers` (via
`create-dmg`), then `hdiutil`. Each was fixed where it bit:

| Site | Fix | Commit |
|---|---|---|
| `codetracer.yml:5516` (`test-ui-tests`, ct-mcr) | `PATH="$PATH:/usr/bin"` | `3ac64e9f2` |
| `codetracer.yml:3727` (`dmg-build`) | `PATH="$PATH:/usr/bin:/usr/sbin"` | `5f6ab33ce` |
| `codetracer.yml:4252` (`dmg-lib-check`) | `PATH="$PATH:/usr/bin"` | `bf7bfd54d` |
| `codetracer.yml:5590` (`test-ui-tests`, build-once) | `PATH="$PATH:/usr/bin"` | `bed00876f` |

**Append, never prepend** — the devShell toolchain must keep winning; the point
is only to make the system tools reachable in the tail.

The last entry is the widest: `repro` passes PATH through to all 42 actions
(`0 hermetic, 22 inherited`), so the `dmg` action inherits whatever deficiency
the invoking step had.

**STILL OPEN, structurally.** Four independent point fixes for one root cause,
and the comment at `codetracer.yml:5573-5574` already records the second as "the
second confirmed instance of that one root cause". There is no runner-level or
workflow-level `env: PATH` fix, so a fifth site will fail the same way. A
workflow-level default, or a lane guard asserting `/usr/bin` is on PATH in every
raw `run:` step that invokes a system tool, would close the class rather than
the instance.

---

## 7. Recorder siblings cloned but never built — CLOSED

**Symptom, ruby.** A `require` failure from inside
`CodeTracer::Native.load_extension!`, naming a `.so`/`.bundle` the reader has
never seen.
**Symptom, js.** `codetracer-js-recorder is not built (…/packages/cli/dist/index.js)`
from `scripts/materialize-recording.sh`, repeated for every recording the
Playwright suite materialises — an hour into the run.

**Cause.** `grep -E 'rake compile|extconf|build-extension'` over `.github/`
returned **zero** hits. The ruby extension was built only by
`scripts/build-siblings.sh`, reached solely by the **Linux** `ct-test-providers`
job, while `test-non-gui` and `test-ui-tests` cloned the recorder on macOS and
ran against a tree with no compiled artefact. Separately, the fix for
"`test-ui-tests` never cloned `codetracer-js-recorder`" (`8616ff406`) added the
clone but no build — `npm run build` existed in this repository only inside that
step's own comment.

**Why it was silent rather than loud — the dominant defect of this campaign.**
`scripts/detect-siblings.sh` probed
`gems/codetracer-ruby-recorder/bin/codetracer-ruby-recorder`, a nine-line shim
**checked into git at mode 100755**. Present in every fresh clone, so the test
could not fail, detection printed `(native)` unconditionally, and the "not
built" warning beside it was unreachable code. The python recorder had the same
shape: it keyed off the source directory and a venv interpreter, neither of
which says the maturin/pyo3 extension was ever installed.

> **A detector that probes the wrong artefact does not merely fail to help — it
> actively converts a build error into a runtime mystery.** When adding a
> recorder, probe the file its *loader* opens, and check the probe against the
> loader's own resolution order.

**State.** Both probes now track the compiled artefact and resolve the platform
DLEXT (`.bundle` on macOS — `.so` is the Linux spelling and was wrong on every
Mac). `test-non-gui` and `test-ui-tests` build the ruby extension; `test-ui-tests`
builds the JS recorder. Both new steps assert the **artefact**, not the exit
code. `scripts/build-siblings.sh`'s hard-coded `.so` was made platform-aware,
since otherwise a green macOS build reports its own artefact missing.

**Python needs no workflow build step** — `nix/shells/main.nix`'s hook installs
the recorder into `.python-recorder-venv` on every `nix develop`, and writes
`.broken` (which `scripts/test-python-version-alignment.sh` fails on) when it
cannot. Asserting one would be a false failure. Checked, not assumed.

**Gates.** `ci/test/detect-siblings-recorder-artifacts-test.sh` (13 assertions)
and `ci/verdict/recorder-clone-implies-build.py` with
`ci/test/recorder-clone-implies-build-test.sh` (12 assertions), both in the bash
lint lane. Every "must warn" is paired with a "must not warn", because a probe
that only ever says one thing is the defect whichever thing it says.

The second gate follows `just` targets into the `Justfile`: `viewmodel-tests`
builds the JS recorder via `just test-vm-recorder-gated` →
`build-siblings.sh --only`, and the checker's first version called that a
violation. The rule was **not** relaxed to permit it — the checker was taught to
see it. A false positive trains the reader to skip the check, which costs
exactly what a missing check costs.

**Known remaining gap, not yet reached.** The nixos leg's `siblings:` block does
not carry the JS recorder either; that leg currently fails earlier, in the
storybook build, so the gap has never been exercised.

---

## Scoreboard

| # | Defect | State |
|---|---|---|
| 1 | repro daemon never `chdir`s | codetracer guarded; **cause open upstream** |
| 2 | stale `extraheader` → 401 | **open** — mitigation, writer unidentified |
| 3 | stale `*.lock` sentinels | closed |
| 4 | read-only `.app` leftovers | closed |
| 5 | `codesign` 127 | closed per site |
| 6 | `create-dmg`/`sw_vers`/`hdiutil` 127 | closed per site; **class open** |
| 7 | recorder cloned, never built | closed |

Four closed, three carrying open work — 1 upstream, 2 entirely, and 5/6 as a
class. Update this table in the same commit as the fix, not afterwards.

---

### Overview (`docs/book-isonim/README.md`)

# CodeTracer Book — working on docs.codetracer.com

This is the **CodeTracer documentation site** (published at
<https://docs.codetracer.com>). It is a consumer of the
[isonim-docs](../../../isonim-docs) static-site framework: the Markdown in
`content/` is rendered by the framework's SSG/SSR, themed by the shared
[CodeTracer docs design system](../../../codetracer-design-system/docs/codetracer-docs.tokens.json).

## Prerequisites

Every task runs inside **this book's own dev shell** (provides `nim`, `just`,
`node`). The book consumes the [isonim-docs](../../../isonim-docs) static-site
framework, so it reuses that framework's self-contained dev shell — which
declares **isonim as its dependency** and reuses isonim's toolchain (Nim 2.2.4,
node, `just`). Nobody has to `nix develop ../../../isonim`.

With **direnv** the shell activates automatically on `cd` (see `./.envrc`):

```bash
cd codetracer/docs/book-isonim
just dev-docs
```

Without direnv, enter the framework's dev shell once…

```bash
cd codetracer/docs/book-isonim
nix develop path:../../../isonim-docs      # the framework shell (isonim is its dep)
just dev-docs
```

…or prefix a single recipe with it:

```bash
nix develop path:../../../isonim-docs -c just dev-docs
```

All commands below assume you are in `codetracer/docs/book-isonim/` and in the
dev shell.

## Live preview (hot reload)

```bash
just dev-docs                 # http://127.0.0.1:8000  (loopback only)
just dev-docs-lan             # same, reachable on your private LAN at http://<host-ip>:8000
just open-docs                # open the running server in a browser
```

- `dev-docs` serves `content/` + the themed assets and **live-reloads every open
  tab** on any content edit.
- The **first launch compiles** the dev server and pre-compiles the client JS
  bundle (theme toggle, live search, sidebar collapse) — expect ~1 minute before
  it starts serving; after that, page loads and edits are instant.
- Default host is loopback (`127.0.0.1`). Pass `0.0.0.0` (or set `AH_DEV_HOST`)
  only on a trusted private network — it exposes the unauthenticated docs to the
  LAN/tailnet.

## Build, one-shot preview, tests

```bash
just build         # static build into build/ (what CI deploys via ci/deploy/docs.sh)
just serve-docs    # one-shot SSR preview (no live reload)
just test          # nav-order + dev-server tests
```

### If the build says `cannot open file: build_site`

Nim has no `--path` to the sibling framework. The book's `config.nims` — which
used to supply them, resolved from `currentSourcePath()` — is matched by the
repo-wide `*.nims` line in `codetracer/.gitignore`, so it is not in the
checkout. `ci/deploy/docs.sh` works around this by generating an absolute
`nim.cfg` before the build and deleting it after; do the same locally, once:

```bash
S="$(cd ../../.. && pwd)"      # the workspace root holding the sibling repos
cat > nim.cfg <<CFG
--path:"$S/isonim-docs/src"
--path:"$S/isonim/src"
--path:"$S/nim-everywhere/src"
--path:"$S/nim-faststreams"
--path:"$S/nim-stew"
--path:"$S/isonim/vendor/chronicles"
--path:"$S/isonim/vendor/serialization"
--path:"$S/isonim/vendor/json_serialization"
CFG
```

`nim.cfg` is git-ignored (RV-11 added the entry, after a run of this recipe left
an untracked file full of one machine's absolute paths sitting in the tree), and
CI writes and deletes its own copy regardless, so leaving it in place is
local-only and safe. It must stay untracked: a committed copy would collide with
the one `ci/deploy/docs.sh` generates.

## Where things live

| Path | What |
|------|------|
| `content/index.md` | The home page (WebFlow landing: hero, cards, popular articles, video, help footer) |
| `content/getting_started/`, `usage_guide/`, `deep_review/`, `reference/` | The four doc sections (order controlled by each page's `order:` front matter + `sectionOrder` in `src/docs_config.nim` — without a `sectionOrder` entry a new section sorts *alphabetically*, ahead of Getting Started) |
| `src/docs_config.nim` | Site config: title, logo, header/sidebar chrome (`headerLinks`, `sidebarLinks`, `sidebarThemeToggle`, `needHelp`), section order, redirects |
| `src/{build,dev,ssr,redirects,theme_tokens}.nim` | The consumer's build/serve entry points |
| `assets/style.css` | Book-owned CSS (theme + WebFlow-parity layout) |
| `static/` | Fonts + images |
| `tests/` | Home-landing, chrome, nav-order, redirect, publish-shape tests |

Add a page by dropping a Markdown file with front matter (`title`, `section`,
`order`, optional `slug`/`aliases`) into the right `content/<section>/` folder.

### Moving or renaming a page

Its URL is published — on the released book, on `/nightly`, and in whatever
links readers have already shared — so a move needs a redirect or the old URL
starts 404ing. Add the pair to **`movedRoutes` in `src/redirects.nim`**:

```nim
(oldRoute: "/usage_guide/deep_review", newRoute: "/deep_review"),
```

The build then writes a meta-refresh stub at the old route's own `index.html`
(the only path GitHub Pages serves for the trailing-slash URL a reader has) and
adds the pair to `_redirects`, both carrying the channel prefix. The list is
**append-only**: deleting an entry re-breaks the URL it was added for. Adding a
new section is otherwise just a directory plus a `sectionOrder` entry in
`src/docs_config.nim`.

## Changing the look (design system)

The book is themed by the **shared** design system at
`codetracer-design-system/docs/codetracer-docs.tokens.json` — the same file the
other two docs sites use. To tweak it visually, launch the design-system editor
straight from here:

```bash
just design                   # http://127.0.0.1:8080  (the shared theme editor)
just design 8080 0.0.0.0      # reachable on the LAN
```

Keep `just dev-docs` running in another shell: a token you change and **Save** in
the editor persists to `codetracer-docs.tokens.json` and **hot-reloads the book
live, no rebuild**. You can also edit that JSON file by hand. Book-specific
layout/overrides live in `assets/style.css`. (The editor itself lives in
[`isonim-docs/site/design`](../../../isonim-docs/site/design/README.md).)

## Deployment

CI (`.github/workflows/codetracer.yml` → `ci/deploy/docs.sh`) builds this book
and publishes it to GitHub Pages, decoupled from the app build/test matrix.
There are **three channels**, chosen by the event and the branch:

| Trigger | URL | Built with |
|---|---|---|
| push to `stable` | <https://docs.codetracer.com/> (plus the archived mdBook at `/old`) | `CT_DOCS_BASE_PATH` unset |
| push to `dev` | <https://docs.codetracer.com/nightly> | `CT_DOCS_BASE_PATH=/nightly` |
| pull request `N` | `https://docs.codetracer.com/pr/N/` | `CT_DOCS_BASE_PATH=/pr/N` |

A push to `main` publishes nothing. All channels commit to the same `gh-pages`
branch and each run replaces **only its own subtree**, so the other channels
survive untouched; the deploy is a fast-forward push, never a force-push.

### Pull-request previews

A pull request that changes anything under `docs/` gets its book published at
`/pr/<N>/`, and a single comment on the pull request links to it — edited in
place on every later push, never re-posted, and naming the commit it was built
from. When the pull request closes, `.github/workflows/docs-preview-cleanup.yml`
removes `/pr/<N>/` again and corrects that comment. A pull request that touches
no docs publishes nothing and says nothing.

Previews are **same-repository pull requests only**. A pull request from a fork
runs with a read-only token and no secrets, by design, because it builds code
written by whoever opened it; publishing from such a run would mean executing
untrusted build code with a token that can write to `gh-pages`. A fork pull
request therefore gets no preview (and no comment — with a read-only token the
job could not post one either).

### robots.txt

Search engines are asked to skip everything that is not the released book: the
**root** `robots.txt` (the only one crawlers read) carries `Disallow: /nightly/`
and `Disallow: /pr/`. Every released deploy regenerates that file from the build
and re-adds both lines; a preview deploy adds the `/pr/` line if it is missing,
so a preview is excluded from the moment it is first published rather than from
the next release. To publish a channel to search engines instead, drop its
`ensure_root_robots_disallow` call in `ci/deploy/docs.sh`.

### Base path

`CT_DOCS_BASE_PATH` is the URL prefix the build is hosted under: `src/build.nim`
passes it to `bookDocsConfig()`, and the framework then rewrites every
root-relative link, asset, stylesheet `url(...)`, search-index route and legacy
redirect stub to carry it. Build a prefixed variant locally with:

```bash
CT_DOCS_BASE_PATH=/nightly just build     # or /pr/123
```

### Rehearsing a deploy

To rehearse one without pushing anything, from the repo root:

```bash
DOCS_DEPLOY_DRY_RUN=1 DOCS_DEPLOY_BRANCH=dev ./ci/deploy/docs.sh   # or =stable
DOCS_DEPLOY_DRY_RUN=1 DOCS_DEPLOY_CHANNEL=preview DOCS_DEPLOY_PR=123 ./ci/deploy/docs.sh
```

It prints the staged tree, including how many files it preserved from the
channels it does not own. If the book build fails the deploy **fails**: it never
substitutes older content under the published URLs, and because nothing is
pushed the previously published site keeps serving.

The whole channel model — every channel, both preservation directions, the
robots.txt rules, the refusals, and real pushes/races/rejections against a local
bare repository — is covered by `ci/test/docs-deploy-channels-test.sh`; the
preview comment is covered by `ci/test/docs-preview-comment-test.sh`. Both run
standalone with no network access.

---

### Overview (`examples/noir_test_banking/readme.md`)

Explanation
Account Validation (account.nr)
validate_balance: Ensures that the sender's balance is sufficient to cover the transaction amount. If not, the program will abort.
verify_identity: Verifies the provided account hash against the stored account hash for both the sender and the recipient, ensuring the transaction is being initiated by the correct parties.
Transaction Execution (transaction.nr)
make_transaction:
Verifies the sender's and recipient's identities.
Validates that the sender has enough funds to complete the transaction.
Simulates the update of the recipient’s balance by adding the transaction amount.
Returns the updated balances for both the sender and recipient.
Main Logic (main.nr)
The main function coordinates the transaction.
It calls make_transaction to ensure the transaction is valid and then checks that the sender’s and recipient’s balances have been updated correctly after the transaction.
Proving the Transaction
In the Prover.toml, the values used for the transaction are as follows:

sender_balance = 1000: The sender has 1000 units.
transaction_amount = 200: The transaction amount is 200 units.
recipient_balance = 300: The recipient starts with 300 units.
sender_account_hash = "0xabc123": The hash of the sender’s account.
recipient_account_hash = "0xdef456": The hash of the recipient’s account.
With this setup, the transaction can be executed and verified using zero-knowledge proofs without ever revealing the actual balances or sensitive data of the accounts. The program simply proves that:

The sender has enough balance.
The identities of both parties are verified.
The transaction has been successfully executed, i.e., the sender’s balance is correctly updated, and the recipient’s balance is increased.

---

### Overview (`examples/ruby-project/README.md`)

# ruby

this is a simple interpreter written in ruby

it can execute ast defined as a ruby object:

```ruby
run(n(:module, [n(:binary_add, [n(:int, [0]), n(:int, [7])])]))
# rint(7)
```

it is a simple project useful to research ruby support for codetracer
and inferring types

it's also defined for python and c++

---

### Overview (`examples/ruby_exit_program/README.md`)

# Ruby Exit Methods

This project demonstrates various methods of exiting a Ruby program. Each script in the `bin/` directory illustrates a different method, providing insight into their usage and potential real-world applications.

## Scripts

- `exit_success`: Exits with a success code.
- `exit_with_error`: Exits with an error code.
- `abort`: Exits with an abort message.
- `unhandled_exception`: Raises an unhandled exception.
- `end_of_script`: Reaches the end of the script.
- `send_signal`: Sends a TERM signal.
- `thread_exit`: Exits the main thread.
- `exec_replace`: Replaces the Ruby process with another command.
- `stack_overflow`: Causes a stack overflow.
- `fork_bomb`: Initiates a fork bomb (use with caution).

## Usage

Each script can be run directly from the command line:

```bash
ruby bin/with_extensions/<script_name> or ruby bin/without_extension/<script_name>

## Script Directories

- **`bin/with_extension/`**:
  - Contains Ruby scripts with the `.rb` extension. These are primarily used during development and debugging when integration with IDEs and other development tools is required.

- **`bin/without_extension/`**:
  - Contains executable scripts without the `.rb` extension, optimized for direct execution from the command line. These scripts provide a cleaner and more tool-like interface for users and are intended for production use or manual execution.

---

### Overview (`examples/rubytracer/README.md`)

# rubytracer

very small experiment ruby tracing

```bash
bin/rubytracer <rspec>
```

Currently implemented just using the tracepoint ruby api. Some investigation
in directly doing this with gdb + Ruby VM shows it shouldn't be too hard, but
that server as a quick pre-experiment.

## infer types from trace

```bash
bin/rubytracer ../ruby-project/spec/ruby_spec.rb
ruby ../ruby-project/spec/x.rb
```

```ruby
Ruby::DSL:
  instance:

  class:
    <Symbol> -> <Array[<<Fixnum> | <type Ruby::Node> | <Symbol>>]> -> <type Ruby::Node>
    def n(kind, children)

    <type Ruby::Node> -> <<type Ruby::RObj>?>
    def run(ast)

    <Fixnum> -> <type Ruby::RObj>
    def rint(value)


Ruby::Node:
  instance:
    <Symbol> -> <Array[<<Fixnum> | <type Ruby::Node> | <Symbol>>]> -> <type Ruby::Node>
    def initialize(kind, children)

  class:


Ruby::Env:
  instance:
    <generic Hash> -> <type Ruby::Env>
    def initialize(values)

    <Symbol> -> <None>
    def [](label)

  class:


Ruby::Runner:
  instance:
    <type Ruby::Node> -> <<<type Ruby::RObj> | R::None>?>
    def run(ast)

    <type Ruby::Node> -> <type Ruby::Node> -> <<type Ruby::RObj>?>
    def run_binary_add(a, b)

    <Fixnum> -> <type Ruby::RObj>
    def run_int(value)

    <Symbol> -> <None>
    def run_var(name)

    <<type Ruby::RObj>?>
    def run_module()

  class:


Ruby::RObj:
  instance:
    <Symbol> -> <Hash[<Symbol> <Fixnum>]> -> <type Ruby::RObj>
    def initialize(type, values)

    <Fixnum>
    def value()

  class:
```

The currently used type "system" is very simple: it has

* `atomic` and `class` types that
  correspond to builtin types like String and to classes (currently no real difference)
* `none` type for nil
* `union`: `A | B` for supporting several possible
* `generic`: currently only matching empty arrays
* `concrete`: concretizing generic bases with real types
* `optional`: `A?` when it can be A or nil

We also unify concrete types with unifiable params.
It's probably not very good for a language like Ruby where something more similar to
duck typing like some form of structural typing will be better. However it's sufficient for demonstration in that simple prototype

We rewrite the spec so we can call the code in the `it` and we trace it and analyze it in
`trace.rb`.

---

### Overview (`non-nix-build/windows/README.md`)

# Windows DIY Bootstrap Maintenance

This directory contains the non-Nix Windows bootstrap workflow
and version pins.

Default install root:

- `%LOCALAPPDATA%/codetracer/windows-diy`
  (PowerShell/bootstrap default)
- POSIX equivalent in Git Bash via
  `source env.sh`
- PowerShell equivalent via
  `. .\env.ps1`
- Override with `WINDOWS_DIY_INSTALL_ROOT` or bootstrap
  `-InstallRoot`

`env.sh` sourcing behavior:

- When sourced from Git Bash, `env.sh` restores your previous
  shell options on return and does not leave `set -e` enabled
  in the caller shell.
- Missing Node tool deps are auto-provisioned by default
  (`WINDOWS_DIY_SETUP_NODE_DEPS=1`) via `yarn install` in
  `node-packages`, so `stylus.cmd`/`webpack.cmd` are available
  for Tup/Just workflows.
- Set `WINDOWS_DIY_SETUP_NODE_DEPS=0` to disable this
  auto-install and manage Node deps manually.

`env.ps1` dot-sourcing behavior (PowerShell):

- Dot-source with `. .\env.ps1` so
  environment variables remain in your current shell.
- Mirrors the same bootstrap + PATH wiring goals as `env.sh`
  for Windows-native shells.
- Supports the same key toggles (`WINDOWS_DIY_SYNC`,
  `WINDOWS_DIY_SETUP_NODE_DEPS`,
  `WINDOWS_DIY_ENSURE_TREE_SITTER_NIM_PARSER`).
- Applies shared runtime setup from
  `non-nix-build/windows/setup-codetracer-runtime-env.ps1`:
  - creates `ct_paths.json` when missing (required by
    `ct.exe` wrapper)
  - sets `CODETRACER_PREFIX`, `CODETRACER_REPO_ROOT_PATH`
  - sets `CODETRACER_E2E_CT_PATH` to
    `src/build-debug/bin/ct.exe` when available
  - prepends `src/build-debug/bin` to PATH for direct `ct`
    invocation
- Resolves `.NET` from `WINDOWS_DIY_DOTNET_ROOT` (if set),
  then
  `$WINDOWS_DIY_INSTALL_ROOT/dotnet/<DOTNET_SDK_VERSION>`,
  then `%ProgramFiles%/dotnet`, exports `DOTNET_ROOT`, and
  validates that the pinned `DOTNET_SDK_VERSION` is installed.
- Resolves Microsoft TTD + WinDbg packages through
  `Get-AppxPackage` and exports:
  - `WINDOWS_DIY_TTD_EXE`, `WINDOWS_DIY_TTD_DIR`,
    `WINDOWS_DIY_TTD_VERSION`,
    `WINDOWS_DIY_TTD_REPLAY_DLL`,
    `WINDOWS_DIY_TTD_REPLAY_CPU_DLL`
  - `WINDOWS_DIY_WINDBG_DIR`,
    `WINDOWS_DIY_WINDBG_VERSION`
  - debugger engine paths for machine APIs:
    `WINDOWS_DIY_CDB_EXE`, `WINDOWS_DIY_DBGENG_DLL`,
    `WINDOWS_DIY_DBGMODEL_DLL`,
    `WINDOWS_DIY_DBGHELP_DLL`
  - note: env scripts prefer
    `C:\Windows\System32\dbgeng.dll`/`dbgmodel.dll`/`dbghelp.dll`
    when available to avoid AppX ACL `LoadLibrary` failures
    from `WindowsApps`.
  - minimum version pins `TTD_MIN_VERSION`,
    `WINDBG_MIN_VERSION`
- By default, validates TTD/WinDbg availability and pinned
  minimum versions (`WINDOWS_DIY_ENSURE_TTD=1`). Set
  `WINDOWS_DIY_ENSURE_TTD=0` to bypass this validation.
- Prepends Nim prebuilt `bin` (when available) before
  source-built Nim `bin` so Windows runtime dependencies like
  `libcrypto-1_1-x64.dll` are discoverable by `ct.exe` and
  UI tests.

`env.sh` (Git Bash) now applies the same runtime setup via
`non-nix-build/windows/setup-codetracer-runtime-env.sh`.

## Git hooks (commit and push checks) without Nix

The checks a commit must pass are declared once, in `nix/pre-commit.nix`. The
Nix dev shell installs them as a `pre-commit` shim whose interpreter is a
`/nix/store` path, which Windows cannot execute. `env.ps1` installs the other
leg instead (`ci/dev/install-portable-git-hooks.sh`, also `just
install-portable-git-hooks`): a shim that runs the SAME hooks, read from
`nix/pre-commit.nix` by `ci/dev/portable-pre-commit.py`, through the
`pre-commit` framework with tools from `PATH`. Set
`WINDOWS_DIY_INSTALL_GIT_HOOKS=0` to skip it.

A tool that is missing does not skip its check: the commit that needs it fails,
naming the tool and the command that installs it. `just
portable-pre-commit-doctor` lists every hook and what this machine lacks. The
tools, at the versions Nix pins where a Windows build exists:

| Hooks | Install |
| --- | --- |
| the framework; whitespace, end-of-file, YAML, large files | `python -m pip install --user pre-commit==4.3.0 pre-commit-hooks==6.0.0` |
| `cspell`, `markdownlint-fix` | `npm install cspell@9.2.1 markdownlint-cli2@0.18.1` |
| `shellcheck`, `shfmt` | `scoop install shellcheck shfmt@3.12.0` |
| `taplo` | `cargo install taplo-cli --locked --version 0.10.0` |
| `rustfmt`, `cargo-check`, `clippy`, merge markers, submodule URLs | the Rust toolchain and Git Bash `env.ps1` provisions |
| `nixfmt-rfc-style` | no Windows build: commit `.nix` changes from a Nix host |

A checkout shared with a Nix shell (a WSL distribution on the same disk) has one
hooks directory, and each side reinstalls its own shim on entry. Re-run the
installer after using the other side.

The installer also removes the relative `core.hooksPath=.git/hooks` that
git-hooks.nix writes. Under that value a linked worktree (`git worktree add`)
runs no hooks at all, and nothing reports it. With the value unset, every
worktree uses the shared hooks directory.

## tree-sitter-nim parser generation

After `source env.sh` (Git Bash), the
workflow runs
`non-nix-build/ensure_tree_sitter_nim_parser.sh`, which
regenerates `libs/tree-sitter-nim/src/parser.c` when it is
missing or older than `libs/tree-sitter-nim/grammar.js`.

The script prefers the local CLI at
`libs/tree-sitter-nim/node_modules/.bin/tree-sitter` and uses
lockfile `npm ci` when `package-lock.json` is present. If
local CLI resolution fails, it falls back to an isolated
cached install of `tree-sitter-cli@<pinned-version>` under
`.tools/tree-sitter-cli-cache/` so parser generation can
proceed without building this grammar package's native addon
dependencies.
Set `TREE_SITTER_NIM_FALLBACK_CLI_PACKAGE_SPEC` to override
the fallback npm package spec if needed, and set
`WINDOWS_DIY_ENSURE_TREE_SITTER_NIM_PARSER=0` to skip this
parser check.

## ct-remote source selection (local repo vs pinned download)

`bootstrap-windows-diy.ps1` supports two ct-remote bootstrap
inputs:

1. Preferred local source build from sibling repo
   `../codetracer-ci` when available.
2. Existing pinned download fallback from
   `downloads.codetracer.com`.

Behavior is controlled by environment variables:

- `CT_REMOTE_WINDOWS_SOURCE_MODE` (default: `auto`)
  - `auto`: on Windows x64, use local source when available
    and otherwise fall back to pinned download; on Windows
    arm64, require local source and do not fall back to
    pinned x64 download.
  - `local`: require the local source path; fail if
    unavailable.
  - `download`: force pinned-download flow (Windows x64
    only).
- `CT_REMOTE_WINDOWS_SOURCE_REPO` (default:
  `../../../codetracer-ci` relative to
  `non-nix-build/windows/bootstrap-windows-diy.ps1`)
  - Override this path when the C# source repo is in a
    different location.
- `CT_REMOTE_WINDOWS_PUBLISH_SCRIPT` -- default:

  ```text
  <CT_REMOTE_WINDOWS_SOURCE_REPO>/non-nix-build/windows/publish-desktop-client.ps1
  ```

  - Override this path to use a different publish helper
    script. Relative paths are resolved from
    `CT_REMOTE_WINDOWS_SOURCE_REPO`.
  - When this script is available, bootstrap uses it for
    local-source publish and does not require host-global
    `dotnet` on PATH.
  - When this script is missing, bootstrap falls back to
    direct `dotnet publish` behavior.
- `CT_REMOTE_WINDOWS_SOURCE_RID` (default: by architecture)
  - `win-x64` on Windows x64.
  - `win-arm64` on Windows arm64.
  - Override is preserved; supported values are `win-x64`
    and `win-arm64`.

Practical local-source publish path discovered from
`../codetracer-ci`:

- Project:
  `apps/DesktopClient/DesktopClient.App/DesktopClient.App.csproj`
- Preferred publish command used by bootstrap
  (defaults to `PublishAot=false` in that script):

  ```text
  pwsh -File non-nix-build/windows/publish-desktop-client.ps1 -Rid <CT_REMOTE_WINDOWS_SOURCE_RID> -Configuration Release
  ```

- Fallback publish command (only when helper script is
  missing):
  `dotnet publish <project> -c Release -r <CT_REMOTE_WINDOWS_SOURCE_RID>`
- Output consumed by bootstrap:
  `runtime/publish/DesktopClient.App/DesktopClient.App(.exe)`
- Upstream CI workflow
  `../codetracer-ci/.github/workflows/publish-desktop-client.yml`
  currently publishes `win-x64` and has `win-arm64` commented
  as not supported yet, so `win-arm64` local publish should
  be treated as environment-dependent. Use
  `CT_REMOTE_WINDOWS_SOURCE_RID=win-x64` as an explicit
  fallback RID if needed.

## Nim source selection and cache layout

`bootstrap-windows-diy.ps1` supports source and prebuilt Nim
install paths:

- `NIM_WINDOWS_SOURCE_MODE` (default: `auto`)
  - `auto`: try source build first; on Windows x64, source
    bootstrap failure falls back to pinned prebuilt ZIP; on
    non-x64, source failure is terminal and does not attempt
    prebuilt fallback.
  - `source`: require source build; fail if source bootstrap
    fails.
  - `prebuilt`: force pinned prebuilt ZIP flow (Windows x64
    only).
- `NIM_WINDOWS_SOURCE_REPO` (default from pin:
  `NIM_SOURCE_REPO`)
- `NIM_WINDOWS_SOURCE_REF` (default from pin:
  `NIM_SOURCE_REF`)
- `NIM_WINDOWS_SOURCE_REVISION` (optional exact commit hash
  override; skips ref resolution)
- `NIM_WINDOWS_CSOURCES_REPO` (default from pin:
  `NIM_CSOURCES_REPO`)
- `NIM_WINDOWS_CSOURCES_REF` (default from pin:
  `NIM_CSOURCES_REF`)
- `NIM_WINDOWS_CSOURCES_REVISION` (optional exact commit
  hash override; skips ref resolution)
- `NIM_WINDOWS_SOURCE_CC` (default: `auto`)
  - `auto`: prefer `gcc` when available, otherwise use MSVC
    (`vcc`/`cl.exe`).
  - `gcc`: require `gcc` for source bootstrap.
  - `vcc`: require MSVC toolchain for source bootstrap.
- `NIM_WINDOWS_SOURCE_BOOTSTRAP_GCC_FROM_TUP_MSYS2`
  (default: `1`)
  - When `gcc` is not on PATH, bootstrap may provision/reuse
    pinned Tup MSYS2 toolchain to supply `gcc`.

Deterministic cache paths under install root
(`$WINDOWS_DIY_INSTALL_ROOT`, default
`%LOCALAPPDATA%/codetracer/windows-diy`):

- Source build cache root:
  `nim/<NIM_VERSION>/cache/source/<cache-key>/`
- Cached source install dir:
  `nim/<NIM_VERSION>/cache/source/<cache-key>/nim-<NIM_VERSION>`
- Prebuilt install dir:
  `nim/<NIM_VERSION>/prebuilt/nim-<NIM_VERSION>`
- Selected install pointer:
  `nim/<NIM_VERSION>/nim.install.relative-path`
- Selected install metadata:
  `nim/<NIM_VERSION>/nim.install.meta`
- Source cache metadata:
  `nim/<NIM_VERSION>/cache/source/<cache-key>/nim.source.meta`

`cache-key` is SHA256 over normalized build inputs:

- Nim version + architecture
- Nim repo/ref/resolved revision
- csources repo/ref/resolved revision
- compiler/toolchain hint snapshot (`CC`, `cl`, `gcc`
  detection/version probes)

Reuse policy:

- If `nim.source.meta` matches current normalized inputs and
  `nim --version` matches `NIM_VERSION`, bootstrap reuses the
  cached source install and skips rebuild.
- Otherwise bootstrap rebuilds source into a new
  deterministic cache key directory.
- In `auto` mode on Windows x64, source failure falls back
  to prebuilt ZIP flow.
- In `auto` mode on non-x64 architectures, source failure is
  returned directly and prebuilt fallback is skipped because
  only `nim-<version>_x64.zip` is pinned.

## Cap'n Proto source selection and cache layout

`bootstrap-windows-diy.ps1` supports source and prebuilt
Cap'n Proto install paths:

- `CAPNP_WINDOWS_SOURCE_MODE` (default: `auto`)
  - `auto`: on Windows x64, use pinned prebuilt ZIP by
    default; on non-x64, build from source.
  - `source`: require source build.
  - `prebuilt`: force pinned prebuilt ZIP flow (Windows x64
    only).
- `CAPNP_WINDOWS_SOURCE_REPO` (default from pin:
  `CAPNP_SOURCE_REPO`)
- `CAPNP_WINDOWS_SOURCE_REF` (default from pin:
  `CAPNP_SOURCE_REF`)
- `CAPNP_WINDOWS_SOURCE_REVISION` (optional exact commit
  hash override; skips ref resolution)
- `CAPNP_WINDOWS_CMAKE_GENERATOR` (optional explicit
  generator override; defaults to `Ninja` when available,
  else `Visual Studio 17 2022`)

Deterministic cache paths under install root
(`$WINDOWS_DIY_INSTALL_ROOT`, default
`%LOCALAPPDATA%/codetracer/windows-diy`):

- Source build cache root:
  `capnp/<CAPNP_VERSION>/cache/source/<cache-key>/`
- Cached source install dir:
  `capnp/<CAPNP_VERSION>/cache/source/<cache-key>/install`
- Prebuilt install dir:
  `capnp/<CAPNP_VERSION>/prebuilt/capnproto-tools-win32-<CAPNP_VERSION>`
- Selected install pointer:
  `capnp/<CAPNP_VERSION>/capnp.install.relative-path`
- Selected install metadata:
  `capnp/<CAPNP_VERSION>/capnp.install.meta`
- Source cache metadata:
  `capnp/<CAPNP_VERSION>/cache/source/<cache-key>/capnp.source.meta`

`cache-key` is SHA256 over normalized build inputs:

- Cap'n Proto version + architecture
- Cap'n Proto repo/ref/resolved revision
- CMake generator + CMake version hint
- compiler/toolchain hint snapshot (`CC`, `cl`, `gcc`
  detection/version probes)

Reuse policy:

- If `capnp.source.meta` matches current normalized inputs
  and `capnp --version` matches `CAPNP_VERSION`, bootstrap
  reuses the cached source install and skips rebuild.
- Otherwise bootstrap rebuilds source into a new
  deterministic cache key directory.
- In `auto` mode on Windows x64, bootstrap uses pinned
  prebuilt by default.
- In `auto` mode on non-x64 architectures, bootstrap selects
  source mode directly.

## Tup source selection and cache layout

`bootstrap-windows-diy.ps1` supports pinned prebuilt Tup
bootstrap by default, with source mode still available:

- `TUP_WINDOWS_SOURCE_MODE` (default: `prebuilt`)
  - `auto`: prefer pinned prebuilt ZIP first, then fallback
    to source build if prebuilt bootstrap fails.
  - `source`: require source build.
  - `prebuilt`: require prebuilt install (uses pinned
    `TUP_PREBUILT_*` defaults unless
    `TUP_WINDOWS_PREBUILT_*` overrides are set).
- `TUP_WINDOWS_SOURCE_REPO` (default from pin:
  `TUP_SOURCE_REPO`)
- `TUP_WINDOWS_SOURCE_REF` (default from pin:
  `TUP_SOURCE_REF`)
- `TUP_WINDOWS_SOURCE_REVISION` (optional exact commit hash
  override; skips ref resolution)
- `TUP_WINDOWS_SOURCE_BUILD_COMMAND` (default from pin:
  `TUP_SOURCE_BUILD_COMMAND`, currently
  `TUP_MINGW=1 TUP_MINGW32=0 ./bootstrap.sh`)
  - When this remains at the pinned default, bootstrap now
    generates and runs `codetracer-bootstrap-windows.sh`
    inside the Tup source checkout (MinGW-only compile path
    that avoids upstream `bootstrap.sh` Unix defaults and
    does not require `pkg-config fuse`).
  - Any non-default value is treated as an explicit override
    and executed as-is in the MSYS2 shell.
- `TUP_WINDOWS_MSYS2_BASE_VERSION` (default from pin:
  `TUP_MSYS2_BASE_VERSION`)
- `TUP_WINDOWS_MSYS2_PACKAGES` (default from pin:
  `TUP_MSYS2_PACKAGES`)
- `TUP_WINDOWS_SELF_HOST_UPDATE` (default: unset/off)
  - Off by default: bootstrap treats manually compiled
    `build/tup.exe` as the final artifact and skips
    `tup init`/`tup upd`.
  - Set to `1`/`true`/`yes`/`on` only for debugging
    self-host updates inside the staged source checkout.
- Pinned prebuilt defaults (toolchain pins):
  - `TUP_PREBUILT_URL` (currently
    `https://gittup.org/tup/win32/tup-latest.zip`)
  - `TUP_PREBUILT_SHA256` (currently
    `efa3a4ecb4e63a9b3f9ced9c18d15ef59fabaf1942c9fffa9a930fb5ebf853a0`)
  - `TUP_PREBUILT_VERSION` (currently `latest`)
- Optional prebuilt override controls:
  - `TUP_WINDOWS_PREBUILT_URL`
  - `TUP_WINDOWS_PREBUILT_SHA256`
  - `TUP_WINDOWS_PREBUILT_VERSION`

Deterministic cache paths under install root
(`$WINDOWS_DIY_INSTALL_ROOT`, default
`%LOCALAPPDATA%/codetracer/windows-diy`):

- Source build cache root:
  `tup/cache/source/<cache-key>/`
- Cached source install dir:
  `tup/cache/source/<cache-key>/install`
- Optional explicit prebuilt install dir:
  `tup/prebuilt/<sha256>/install`
- Selected install pointer:
  `tup/tup.install.relative-path`
- Selected install metadata: `tup/tup.install.meta`
- Source cache metadata:
  `tup/cache/source/<cache-key>/tup.source.meta`

`cache-key` is SHA256 over normalized build inputs:

- Tup source repo/ref/resolved revision
- requested source build command
- effective source build command identity (for default path
  this is the codetracer-owned generated script version)

Host prerequisites for source mode:

- `git` must be present on PATH.
- Bootstrap now provisions an MSYS2 base
  (`msys2-base-x86_64-<version>.tar.xz`) under
  `$WINDOWS_DIY_INSTALL_ROOT/tup/msys2/<version>/msys64`
  and installs the pinned package set
  (`mingw-w64-x86_64-gcc mingw-w64-x86_64-pkgconf make`)
  before running the Tup source build command.
- Tup source bootstrap exports `TUP_MINGW=1` and
  `TUP_MINGW32=0`, and prepends MinGW bin directories
  (`mingw64/bin`, `mingw32/bin`) in the MSYS2 shell PATH so
  Tup builds target MinGW compilers by default.
- In default mode, bootstrap emits
  `codetracer-bootstrap-windows.sh` and compiles Tup directly
  with the MinGW toolchain (including `src/compat/win32` and
  `src/tup/server/windepfile.c`), then installs the compiled
  `build/tup.exe` as the bootstrap artifact without running
  self-host `tup init`/`tup upd`.

Reuse policy:

- If `tup.source.meta` matches current normalized inputs and
  `tup --version` succeeds, bootstrap reuses the cached
  source install and skips rebuild.
- `prebuilt` and `auto` modes use pinned upstream ZIP
  URL/SHA by default for reproducibility. Use `source` mode
  when you need to force source bootstrap.

## Version Bump Process (`toolchain-versions.env`)

1. Pick target versions and verify they exist upstream:
   - `RUSTUP_VERSION`:
     <https://github.com/rust-lang/rustup/releases>
   - `RUST_TOOLCHAIN_VERSION`:
     <https://blog.rust-lang.org/releases/>
   - `NODE_VERSION`:
     <https://nodejs.org/dist/index.json>
   - `UV_VERSION`:
     <https://github.com/astral-sh/uv/releases>
   - `DOTNET_SDK_VERSION`:
     <https://dotnet.microsoft.com/download/dotnet> (pin
     exact SDK patch used by Windows non-Nix workflow)
   - `WINDBG_MIN_VERSION`: minimum accepted AppX package
     version for `Microsoft.WinDbg`
   - `TTD_MIN_VERSION`: minimum accepted AppX package
     version for `Microsoft.TimeTravelDebugging`
   - `NIM_VERSION`:
     <https://nim-lang.org/install_windows.html> (download
     assets are under `https://nim-lang.org/download/`)
   - `NIM_WIN_X64_SHA256`: SHA256 for
     `nim-<version>_x64.zip` from
     `https://nim-lang.org/download/nim-<version>_x64.zip.sha256`
   - `NIM_SOURCE_REPO`: Nim source repository URL used for
     source mode cache keying/build
   - `NIM_SOURCE_REF`: Nim source ref to resolve
     (tag/branch/refname)
   - `NIM_CSOURCES_REPO`: Nim csources repository URL used
     for source mode cache keying/build
   - `NIM_CSOURCES_REF`: Nim csources ref to resolve
     (tag/branch/refname)
   - `CT_REMOTE_VERSION`: artifact revision at:

     ```text
     https://downloads.codetracer.com/DesktopClient.App/DesktopClient.App-win-x64-<revision>.tar.gz
     ```

   - `CT_REMOTE_WIN_X64_SHA256`: SHA256 for that exact
     Windows archive.
   - `CAPNP_VERSION`:
     <https://capnproto.org/install.html> (Windows asset
     `https://capnproto.org/capnproto-c++-win32-<version>.zip`)
   - `CAPNP_WIN_X64_SHA256`: SHA256 for that exact
     `capnproto-c++-win32-<version>.zip` asset. Cap'n Proto
     currently does not publish a `.sha256` sidecar for this
     ZIP, so we pin and verify the repo-managed hash.
   - `CAPNP_SOURCE_REPO`: Cap'n Proto source repository URL
     used for source mode cache keying/build.
   - `CAPNP_SOURCE_REF`: Cap'n Proto source ref to resolve
     (tag/branch/refname).
   - `TUP_SOURCE_REPO`: Tup source repository URL used for
     source mode cache keying/build (default pin points to
     the variants-capable Windows fork).
   - `TUP_SOURCE_REF`: Tup source ref to resolve
     (tag/branch/refname), defaulting to
     `variants-for-windows`.
   - `TUP_SOURCE_BUILD_COMMAND`: Bash command executed inside
     Tup source checkout to build Tup for Windows bootstrap
     (default pin sets
     `TUP_MINGW=1 TUP_MINGW32=0 ./bootstrap.sh`).
   - `TUP_PREBUILT_VERSION`: version label for the pinned
     official prebuilt Tup archive.
   - `TUP_PREBUILT_URL`: prebuilt Tup archive URL (default
     pin points to
     `https://gittup.org/tup/win32/tup-latest.zip`).
   - `TUP_PREBUILT_SHA256`: SHA256 for that exact prebuilt
     Tup archive.
   - `TUP_MSYS2_BASE_VERSION`: pinned MSYS2 base release
     date (YYYYMMDD) used for Tup source prerequisites.
   - `TUP_MSYS2_BASE_X64_SHA256`: SHA256 for
     `msys2-base-x86_64-<version>.tar.xz`.
   - `TUP_MSYS2_PACKAGES`: space-separated MSYS2 package
     list installed for Tup source builds (default pin uses
     MinGW-targeted
     `mingw-w64-x86_64-gcc mingw-w64-x86_64-pkgconf make`).
2. Update
   `non-nix-build/windows/toolchain-versions.env`.
3. Keep
   `non-nix-build/windows/bootstrap-windows-diy.ps1` in
   sync with the same pinned versions.
4. Validate checksums from upstream checksum documents
   before merging:
   - Node.js archive must match `SHASUMS256.txt` from
     `https://nodejs.org/dist/v<version>/`.
   - rustup installer must match
     `rustup-init.exe.sha256` from
     `https://static.rust-lang.org/rustup/archive/<rustup-version>/<target>/`.
   - uv archive must match `<asset>.sha256` from the
     corresponding GitHub release tag.
   - Nim prebuilt archive must match
     `nim-<version>_x64.zip.sha256` from
     `https://nim-lang.org/download/`.
   - ct-remote archive must match the pinned
     `CT_REMOTE_WIN_X64_SHA256` hash in
     `toolchain-versions.env`.
   - Cap'n Proto archive must match the pinned
     `CAPNP_WIN_X64_SHA256` hash in
     `toolchain-versions.env`.

### Nim hash bump helper

Use this command from PowerShell to compute and verify the
Nim hash you should pin:

```powershell
$version = "2.2.6"
$asset = "nim-$version`_x64.zip"
$url = "https://nim-lang.org/download/$asset"
$sidecarUrl = "$url.sha256"
$tmp = Join-Path $env:TEMP $asset
Invoke-WebRequest -Uri $url -OutFile $tmp -UseBasicParsing
$calculated = (Get-FileHash -Path $tmp -Algorithm SHA256).Hash.ToLowerInvariant()
$upstream = ((Invoke-WebRequest -Uri $sidecarUrl -UseBasicParsing).Content -split '\s+')[0].ToLowerInvariant()
"calculated=$calculated"
"upstream=$upstream"
if ($calculated -ne $upstream) { throw "Nim hash mismatch" }
```

## Windows note: ct-native-replay and UI tests

- `ct-native-replay` (from sibling repo
  `../codetracer-native-backend`) can now be compiled on Windows
  in a compile-only mode.
- Runtime RR replay/record behavior is still Linux-only, so
  Windows flows that require active RR replay are expected to
  return explicit unsupported errors.
- This means `ct record` for scenarios that require the RR
  backend (for example some Noir flows) may still fail on
  Windows even when the binary is present.
- Current observed Windows non-Nix issue: Noir recording can
  hang in `nargo trace`, which leaves `trace-*` folders with
  incomplete data (for example only `symbols.json`) and
  causes Playwright UI tests to timeout waiting for core
  components.
- Interim test-unblock recommendation: run Web-mode
  program-agnostic/layout UI tests with
  `CODETRACER_TRACE_PATH` set to a known non-Noir trace
  folder (for example `src/tui/trace`) while investigating
  the Noir hang.
- Windows-native time-travel backend is now implemented
  behind `DebuggerBackend` via `ct-native-replay` using
  Microsoft TTD + dbgeng.
  - Enable native dbgeng control with
    `CT_TTD_CONTROL_MODE=dbgeng` (optional: enforce
    `CT_TTD_REQUIRE_NATIVE_DBGENG=1` to disable cdb
    fallback).
  - The Windows env scripts already export
    `WINDOWS_DIY_TTD_*`, `WINDOWS_DIY_CDB_EXE`, and
    `WINDOWS_DIY_DBGENG_DLL`; ensure those are available in
    the shell that launches `ct-native-replay`.
  - API surface reference lives in
    `codetracer-native-backend/docs/dbgeng-api-surface.md`
    alongside the implementation.

### ct-remote hash bump helper

Use this command from PowerShell to compute the hash you
should pin:

```powershell
$revision = "102d2c8"
$asset = "DesktopClient.App-win-x64-$revision.tar.gz"
$url = "https://downloads.codetracer.com/DesktopClient.App/$asset"
$tmp = Join-Path $env:TEMP $asset
Invoke-WebRequest -Uri $url -OutFile $tmp -UseBasicParsing
(Get-FileHash -Path $tmp -Algorithm SHA256).Hash.ToLowerInvariant()
```

### Cap'n Proto hash bump helper

Use this command from PowerShell to compute the hash you
should pin:

```powershell
$version = "1.3.0"
$asset = "capnproto-c++-win32-$version.zip"
$url = "https://capnproto.org/$asset"
$tmp = Join-Path $env:TEMP $asset
Invoke-WebRequest -Uri $url -OutFile $tmp -UseBasicParsing
(Get-FileHash -Path $tmp -Algorithm SHA256).Hash.ToLowerInvariant()
```

`bootstrap-windows-diy.ps1` keeps pinned ct-remote download
constrained to Windows x64
(`DesktopClient.App-win-x64-<revision>.tar.gz` +
`CT_REMOTE_WIN_X64_SHA256`). On Windows arm64,
`CT_REMOTE_WINDOWS_SOURCE_MODE=auto` now routes to
local-source mode and will not attempt x64 pinned-download
fallback. Nim source mode can be attempted on both `x64` and
`arm64`, while Nim prebuilt mode remains pinned to
`nim-<version>_x64.zip` and therefore x64-only. Cap'n Proto
now supports
`CAPNP_WINDOWS_SOURCE_MODE=auto|source|prebuilt`: prebuilt
remains pinned to x64 ZIP, and source mode provides the
non-x64 path with deterministic source-cache reuse. Tup now
supports `TUP_WINDOWS_SOURCE_MODE=prebuilt|auto|source` with
default pinned prebuilt install from
`https://gittup.org/tup/win32/tup-latest.zip` and
`TUP_PREBUILT_SHA256`, while keeping source bootstrap support
pinned to `https://github.com/zah/tup.git` branch
`variants-for-windows`, MinGW-targeted bootstrap defaults
(`TUP_SOURCE_BUILD_COMMAND=TUP_MINGW=1 TUP_MINGW32=0 ./bootstrap.sh`)
and MSYS2 prerequisite pins (`TUP_MSYS2_*`) to provision
`mingw-w64-x86_64-gcc`, `mingw-w64-x86_64-pkgconf`, and
`make` reproducibly before source builds. Source defaults come
from `NIM_SOURCE_*`, `NIM_CSOURCES_*`, `CAPNP_SOURCE_*`, and
`TUP_SOURCE_*` pins.

### ct-remote arm64 blocker runbook

If Windows arm64 `ct-remote` bootstrap is still blocked, run
these exact URL probes first:

```powershell
$revision = "102d2c8"
$base = "https://downloads.codetracer.com/DesktopClient.App"
$urls = @(
  "$base/DesktopClient.App-win-x64-$revision.tar.gz",
  "$base/DesktopClient.App-win-arm64-$revision.tar.gz",
  "$base/DesktopClient.App-win-aarch64-$revision.tar.gz",
  "$base/DesktopClient.App-win-arm64ec-$revision.tar.gz"
)
foreach ($url in $urls) {
  try {
    $r = Invoke-WebRequest -UseBasicParsing -Method Head -Uri $url
    "$url -> $([int]$r.StatusCode) $($r.StatusDescription)"
  } catch {
    $code = if ($_.Exception.Response -and $_.Exception.Response.StatusCode) { [int]$_.Exception.Response.StatusCode } else { "ERR" }
    "$url -> $code"
  }
}
```

Expected outcome as of 2026-02-09:

- `DesktopClient.App-win-x64-102d2c8.tar.gz` returns `200`.
- `DesktopClient.App-win-arm64-102d2c8.tar.gz` returns
  `404`.
- `DesktopClient.App-win-aarch64-102d2c8.tar.gz` returns
  `404`.
- `DesktopClient.App-win-arm64ec-102d2c8.tar.gz` returns
  `404`.

Required inputs to unblock arm64 support:

1. Official arm64 artifact URL at
   `downloads.codetracer.com` for the pinned revision (or an
   approved new revision to pin), including exact filename
   convention (`win-arm64` vs `win-aarch64`).
2. A trustworthy SHA256 value for that exact artifact. There
   is currently no sidecar checksum document for ct-remote
   assets, so this must come from release engineering
   process/publish channel.
3. Confirmation whether x64 and arm64 must share
   `CT_REMOTE_VERSION` or whether per-arch revisions are
   allowed.

### Cap'n Proto non-x64 mode-routing probe

To verify that `CAPNP_WINDOWS_SOURCE_MODE=auto` selects
source mode for non-x64 architectures without requiring an
arm64 host, run:

```powershell
$env:WINDOWS_DIY_ARCH_OVERRIDE = "arm64"
$env:CAPNP_WINDOWS_SOURCE_MODE = "auto"
$env:CAPNP_WINDOWS_SOURCE_REVISION = "7dbb95989721016f8b590245ec7528c6ff03d1fe"
$env:WINDOWS_DIY_SKIP_NIM = "1"
$env:WINDOWS_DIY_SKIP_CT_REMOTE = "1"
pwsh -File non-nix-build/windows/bootstrap-windows-diy.ps1
```

Expected behavior:

- Bootstrap logs Cap'n Proto source-mode activity
  (`Building Cap'n Proto ... from source` or
  `Cap'n Proto ... source cache hit`).
- `capnp/<CAPNP_VERSION>/capnp.install.meta` records
  `requested_mode=auto` and `effective_mode=source`.
- Probe intentionally skips Nim and `ct-remote` via
  `WINDOWS_DIY_SKIP_NIM=1` and
  `WINDOWS_DIY_SKIP_CT_REMOTE=1` so Cap'n Proto route
  selection can be validated in isolation.

## Required Validation Commands

Run these from repo root:

```powershell
pwsh -File non-nix-build/windows/validate-toolchain-versions.ps1
pwsh -Command "$errors = $null; [void][System.Management.Automation.Language.Parser]::ParseFile('non-nix-build/windows/bootstrap-windows-diy.ps1', [ref]$null, [ref]$errors); if ($errors.Count -gt 0) { $errors | ForEach-Object { Write-Error $_.Message }; exit 1 }"
pwsh -Command "$errors = $null; [void][System.Management.Automation.Language.Parser]::ParseFile('non-nix-build/windows/validate-toolchain-versions.ps1', [ref]$null, [ref]$errors); if ($errors.Count -gt 0) { $errors | ForEach-Object { Write-Error $_.Message }; exit 1 }"
bash -n env.sh
```

## Changelog and Status Update Expectations

- Add an entry to `CHANGELOG.md` describing the Windows
  toolchain version bump.
- Update `windows-porting-initiative-status.md` when process
  or automation changes.

---

### Overview (`src/tracer/README.md`)

tracer
-----------------

ruby tracer MOVED to a separate repo <https://github.com/metacraft-labs/codetracer-ruby-recorder>

original readme:

a very early version of a ruby codetracer tracer, that records data about
a running ruby program which is later used to simulate a limited replay of it
in codetracer.

### usage

```bash
ruby trace.rb <ruby-program>
# -> trace.json
# and some debugging output currently
# TODO more options

# or
python3 trace.py
# -> trace.json
```

### run example

```bash
# enter codetracer dir, with enabled direnv
# so you have ruby from our nix shell in PATH
cd src/ruby-tracer
ruby trace.rb calc.rb
cat trace.json
# ..
```

### in codetracer

it should be callable by codetracer with

```bash
ct record <path to ruby-program>
ct replay <ruby-program-name>
```

not yet integrated fully for python

### TODO/problems

* optimizations
* cleanup code
* support more kind of ruby values: only several simples types supported currently
* others

---

### Overview (`test-programs/py_checklist/README.md`)

# Python Checklist Test Programs

This suite mirrors the `ruby_checklist` sample by collecting small,
targeted probes that exercise diverse areas of the Python language and
standard library. The code is intentionally verbose and heavily
commented so that engineers working across multiple languages can
quickly understand what a probe demonstrates and why it matters.

## Goals

- Capture representative call stacks for critical Python features so the
  debugger and tracer integrations can be validated.
- Provide ready-made snippets that illustrate specific language
  behaviors, pitfalls, and idioms for reference during development.
- Keep each probe independent and side-effect light so they can run in
  isolation without hidden dependencies.

## Organization

- Minimum Python version: **3.11**. This allows us to cover structural
  pattern matching, `except*` for exception groups, and other
  post-3.10 features without fallbacks.
- Each feature cluster lives in its own module (for example,
  `basics.py`, `functions_exceptions.py`, …). Modules should export
  numbered `demo_*` functions that focus on a single behavior.
- A top-level `run_all()` function inside each module executes its demos
  sequentially, printing numbered lines so missed events are easy to
  spot in traces.
- Inline comments and docstrings must explain **what** the snippet does,
  **why** someone would use it, and any common gotchas.
- Helpers that spin up threads or processes must protect their entry
  points with `if __name__ == "__main__":` to stay portable across
  platforms (especially Windows).

## Feature Coverage

| Area | Modules / Demos | Notes |
| --- | --- | --- |
| Literals, operators, control flow | `basics.run_all()` | Covers literals, slicing, unpacking, match/case, loop `else`. |
| Functions, decorators, exceptions | `functions_exceptions.run_all()` | Demonstrates call signatures, mutable defaults, closures, decorator stacking, caching, chained exceptions, `ExceptionGroup`. |
| Context managers & iterators | `contexts_iterators.run_all()` | Custom `__enter__/__exit__`, `contextlib`, iterator protocol, generators with `send`/`throw`/`close`. |
| Async & concurrency | `async_concurrency.run_all()` | Async context/iter, `create_task`, contextvars, threading/TLS, `ThreadPoolExecutor`, guarded multiprocessing. |
| Data model & classes | `data_model.run_all()` | `__repr__`, formatting, rich ops, properties with `__slots__`, attribute hooks, descriptors, subclass hooks, metaclass lifecycle. |
| Collections & dataclasses | `collections_dataclasses.run_all()` | Dataclasses with slots/order, enums and flags, namedtuple/deque/defaultdict/Counter, mapping proxy, pattern matching with `__match_args__`. |
| System utilities | `system_utils.run_all()` | `subprocess.run` with env overrides, tz-aware datetime math, `Decimal`/`Fraction`, deterministic random, regex capture groups. |
| Introspection & dynamic code | `introspection.run_all()` | `inspect.signature`, dynamic attributes, globals/locals snapshots, `eval`/`exec`/`compile`, AST parse/dump (with security notes in comments). |
| Import system | `imports_demo.run_all()` | Runtime module creation via `types.ModuleType`, adjusting `sys.path`, `importlib.import_module`, `__all__`, module caching notes. |
| Logging, warnings, GC, typing | `advanced_runtime.run_all()` | Scoped logging config, forced warnings, GC cycle collection with weakrefs/finalizers, runtime protocol checks, generics. |
| Miscellaneous constructs | `miscellaneous.run_all()` | `iter(callable, sentinel)`, `del`, custom mapping protocol, context manager without suppression. |

> **Planned additions:** Remaining entries from the original checklist
> (I/O & serialization, subprocess variants, JSON/pickle, etc.) can be
> layered in by following the same pattern—add `<topic>.py`, expose
> `run_all()`, then register it in `__main__.py`.

## Running The Suite

Once all modules are implemented, you will be able to run the entire
checklist from the project root:

```bash
python -m test-programs.python_checklist
```

During early development, it is fine to run individual modules directly
with `python test-programs/python_checklist/<module>.py` to iterate on a
specific feature cluster.

## Contribution Guidelines

- Follow the numbering scheme diligently so console output gaps surface
  immediately.
- Prefer deterministic behavior (avoid randomness unless the purpose is
  to exercise the random module).
- Clean up temporary files, subprocesses, and other resources within the
  probe itself. Each demo should leave the environment in the same state
  it found it.
- If a snippet showcases risky behavior (e.g., `eval`), highlight the
  security implications in comments.

This structure keeps the checklist maintainable and allows future
contributors to plug in additional probes without surprising teammates.

---

### Overview (`test-programs/rs_checklist/README.md`)

# Rust Checklist Programs

This suite mirrors the existing Python and Ruby checklists with a
Rust-specific set of probes that exercise language features and
standard-library behaviors useful for debugging CodeTracer. Programs are
small, verbose, and grouped by topic so you can run them together or
drill into a single cluster.

## Running

```bash
# from repo root
nix develop -c cargo run -p rs_checklist

# to run a single module binary (examples)
nix develop -c cargo run -p rs_checklist --bin lexical_bindings
nix develop -c cargo run -p rs_checklist --bin traits_generics
```

The crate targets Rust 1.85+/edition 2024 to capture newer drop-order
and capture semantics. Each module prints a heading before running its
scenarios so missing events are easy to spot.

## Layout

- `src/main.rs` wires the modules and prints section headers.
- `src/macros_support.rs` holds traits used by the proc-macro derive.
- `macros/` is a tiny proc-macro crate providing `#[derive(AutoHello)]`
  to demonstrate attribute macros.

Modules (roughly aligned to the provided coverage map):

- `lexical_bindings`: bindings, shadowing vs `mut`, `const`/`static`/`static mut`,
  `thread_local!`, raw identifiers for reserved keywords, name resolution.
- `iterators_collections`: literals/ranges/struct forms, operator overloading,
  coercions/casts, closures for Fn/FnMut/FnOnce, custom iterators,
  Vec/VecDeque/LinkedList/HashMap/BTreeMap/BinaryHeap, formatting, Unicode-safe
  slicing.
- `ownership_borrowing`: Copy vs move, Clone/Drop, partial moves, RAII drop
  ordering (2024 edition semantics), lifetimes/HRTBs, reborrowing, raw refs.
- `patterns_control`: destructuring (tuples/structs/slices), `ref`/`ref mut`,
  or-patterns, guards, range patterns, let-else, `if let` chains, labelled loops,
  diverging arms using `!`.
- `traits_generics`: supertraits, default methods, trait objects vs `dyn`,
  associated types/consts, GATs, `impl Trait` in args/returns, async fn in traits,
  const generics, PhantomData.
- `errors_runtime`: Result/Option with `?` and `ok_or`, custom error impls,
  file I/O (read/write/missing), `Instant` timing, `panic!` hooks plus
  `catch_unwind`.
- `concurrency_async`: threads with Arc<Mutex>/RwLock, barriers, mpsc + sync
  channels, atomics with orderings, condvars/Once, async/await with joins and a
  manual `Future`.
- `smart_pointers`: Box raw conversions, Rc cycles + Weak, Cell/RefCell borrow
  panic, Arc<Mutex>, Cow, pinning `PhantomPinned` self-references,
  NonZero/NonNull pointer niches.
- `unsafe_macros_const`: unsafe blocks, `MaybeUninit`, unions, `transmute`,
  repr(C) + extern "C" functions, macro_rules + proc-macro derive,
  cfg attributes, const fn/const blocks, visibility modifiers, Any/type_id
  introspection, size/align checks.

Each module exposes a `run()` invoked by `main` with numbered `println!`
output to keep trace review predictable.

---

### Overview (`test-programs/stylus_fund_tracker/README.md`)

# Stylus Hello World

Project starter template for writing Arbitrum Stylus programs in Rust using the [stylus-sdk](https://github.com/OffchainLabs/stylus-sdk-rs). It includes a Rust implementation of a basic counter Ethereum smart contract:

```js
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

contract Counter {
    uint256 public number;

    function setNumber(uint256 newNumber) public {
        number = newNumber;
    }

    function increment() public {
        number++;
    }
}
```

To set up more minimal example that still uses the Stylus SDK, use `cargo stylus new --minimal <YOUR_PROJECT_NAME>` under [OffchainLabs/cargo-stylus](https://github.com/OffchainLabs/cargo-stylus).

## Quick Start

Install [Rust](https://www.rust-lang.org/tools/install), and then install the Stylus CLI tool with Cargo

```bash
cargo install --force cargo-stylus cargo-stylus-check
```

Add the `wasm32-unknown-unknown` build target to your Rust compiler:

```
rustup target add wasm32-unknown-unknown
```

You should now have it available as a Cargo subcommand:

```bash
cargo stylus --help
```

Then, clone the template:

```
git clone https://github.com/OffchainLabs/stylus-hello-world && cd stylus-hello-world
```

### Testnet Information

All testnet information, including faucets and RPC endpoints can be found [here](https://docs.arbitrum.io/stylus/reference/testnet-information).

### ABI Export

You can export the Solidity ABI for your program by using the `cargo stylus` tool as follows:

```bash
cargo stylus export-abi
```

which outputs:

```js
/**
 * This file was automatically generated by Stylus and represents a Rust program.
 * For more information, please see [The Stylus SDK](https://github.com/OffchainLabs/stylus-sdk-rs).
 */

// SPDX-License-Identifier: MIT-OR-APACHE-2.0
pragma solidity ^0.8.23;

interface ICounter {
    function number() external view returns (uint256);

    function setNumber(uint256 new_number) external;

    function mulNumber(uint256 new_number) external;

    function addNumber(uint256 new_number) external;

    function increment() external;
}
```

Exporting ABIs uses a feature that is enabled by default in your Cargo.toml:

```toml
[features]
export-abi = ["stylus-sdk/export-abi"]
```

## Deploying

You can use the `cargo stylus` command to also deploy your program to the Stylus testnet. We can use the tool to first check
our program compiles to valid WASM for Stylus and will succeed a deployment onchain without transacting. By default, this will use the Stylus testnet public RPC endpoint. See here for [Stylus testnet information](https://docs.arbitrum.io/stylus/reference/testnet-information)

```bash
cargo stylus check
```

If successful, you should see:

```bash
Finished release [optimized] target(s) in 1.88s
Reading WASM file at stylus-hello-world/target/wasm32-unknown-unknown/release/stylus-hello-world.wasm
Compressed WASM size: 8.9 KB
Program succeeded Stylus onchain activation checks with Stylus version: 1
```

Next, we can estimate the gas costs to deploy and activate our program before we send our transaction. Check out the [cargo-stylus](https://github.com/OffchainLabs/cargo-stylus) README to see the different wallet options for this step:

```bash
cargo stylus deploy \
  --private-key-path=<PRIVKEY_FILE_PATH> \
  --estimate-gas
```

You will then see the estimated gas cost for deploying before transacting:

```bash
Deploying program to address e43a32b54e48c7ec0d3d9ed2d628783c23d65020
Estimated gas for deployment: 1874876
```

The above only estimates gas for the deployment tx by default. To estimate gas for activation, first deploy your program using `--mode=deploy-only`, and then run `cargo stylus deploy` with the `--estimate-gas` flag, `--mode=activate-only`, and specify `--activate-program-address`.

Here's how to deploy:

```bash
cargo stylus deploy \
  --private-key-path=<PRIVKEY_FILE_PATH>
```

The CLI will send 2 transactions to deploy and activate your program onchain.

```bash
Compressed WASM size: 8.9 KB
Deploying program to address 0x457b1ba688e9854bdbed2f473f7510c476a3da09
Estimated gas: 1973450
Submitting tx...
Confirmed tx 0x42db…7311, gas used 1973450
Activating program at address 0x457b1ba688e9854bdbed2f473f7510c476a3da09
Estimated gas: 14044638
Submitting tx...
Confirmed tx 0x0bdb…3307, gas used 14044638
```

Once both steps are successful, you can interact with your program as you would with any Ethereum smart contract.

## Calling Your Program

This template includes an example of how to call and transact with your program in Rust using [ethers-rs](https://github.com/gakonst/ethers-rs) under the `examples/counter.rs`. However, your programs are also Ethereum ABI equivalent if using the Stylus SDK. **They can be called and transacted with using any other Ethereum tooling.**

By using the program address from your deployment step above, and your wallet, you can attempt to call the counter program and increase its value in storage:

```rs
abigen!(
    Counter,
    r#"[
        function number() external view returns (uint256)
        function setNumber(uint256 number) external
        function increment() external
    ]"#
);
let counter = Counter::new(address, client);
let num = counter.number().call().await;
println!("Counter number value = {:?}", num);

let _ = counter.increment().send().await?.await?;
println!("Successfully incremented counter via a tx");

let num = counter.number().call().await;
println!("New counter number value = {:?}", num);
```

Before running, set the following env vars or place them in a `.env` file (see: [.env.example](./.env.example)) in this project:

```
RPC_URL=https://sepolia-rollup.arbitrum.io/rpc
STYLUS_CONTRACT_ADDRESS=<the onchain address of your deployed program>
PRIV_KEY_PATH=<the file path for your priv key to transact with>
```

Next, run:

```
cargo run --example counter --target=<YOUR_ARCHITECTURE>
```

Where you can find `YOUR_ARCHITECTURE` by running `rustc -vV | grep host`. For M1 Apple computers, for example, this is `aarch64-apple-darwin` and for most Linux x86 it is `x86_64-unknown-linux-gnu`

## Build Options

By default, the cargo stylus tool will build your project for WASM using sensible optimizations, but you can control how this gets compiled by seeing the full README for [cargo stylus](https://github.com/OffchainLabs/cargo-stylus). If you wish to optimize the size of your compiled WASM, see the different options available [here](https://github.com/OffchainLabs/cargo-stylus/blob/main/OPTIMIZING_BINARIES.md).

## Peeking Under the Hood

The [stylus-sdk](https://github.com/OffchainLabs/stylus-sdk-rs) contains many features for writing Stylus programs in Rust. It also provides helpful macros to make the experience for Solidity developers easier. These macros expand your code into pure Rust code that can then be compiled to WASM. If you want to see what the `stylus-hello-world` boilerplate expands into, you can use `cargo expand` to see the pure Rust code that will be deployed onchain.

First, run `cargo install cargo-expand` if you don't have the subcommand already, then:

```
cargo expand --all-features --release --target=<YOUR_ARCHITECTURE>
```

Where you can find `YOUR_ARCHITECTURE` by running `rustc -vV | grep host`. For M1 Apple computers, for example, this is `aarch64-apple-darwin`.

## License

This project is fully open source, including an Apache-2.0 or MIT license at your choosing under your own copyright.

---

### Overview (`examples/ruby/project-euler/README.md`)

project-euler-ruby
==================

Answers to <http://projecteuler.net/problems>, in ruby.

## Expectations

Ruby 1.9.3

---

### Overview (`src/public/third_party/README.md`)

This directory stores third-party libraries used by the frontend. Files that are in folders are guaranteed to be third-party.
Files in the root of this directory are either single-file third-party libraries, or are our custom modifications and/or glue code.
So if you do something like a `grep -r` while in the process of renaming something, or when tracking where a certain style or javascript
behaviour is coming from, make sure to also check out the files in the root directory in this folder!

---

### Overview (`src/db-backend/test-programs/reprobuild_hcr_in_codetracer/README.md`)

# Reprobuild HCR In CodeTracer Fixture

This fixture is intentionally shaped for the full Reprobuild HCR in
CodeTracer gate, not the older same-process direct-patch prototype.

The accepted test path is:

- Reprobuild builds the initial native target with HCR-required compiler
  flags and build graph metadata.
- CodeTracer launches the target under MCR recording from process start.
- The edit driver replaces `src/patchable.c` with generation 1 through a
  normal filesystem write.
- The production Reprobuild HCR coordinator rebuilds the changed translation
  unit and delivers a direct patch to the in-process agent.
- CodeTracer DAP validates old and new source generations in live recording
  and replay.

The fixture files deliberately avoid any in-process shortcut to the patching
layer. The Rust gate scans this directory for those shortcuts before running.

---

### Overview (`src/public/third_party/monaco-themes/README.md`)

codetracer monaco-themes fork:
-----------------------------------

* added our custom codetracer themes in themes/customThemes : Victor Velitchkov's work
* 6.10.2025: (alexander): removed yarn.lock, package.json, index.html, webpack.config.js and `scripts/`, because we don't use them and to prevent receiving dependabot alerts for them

## monaco-themes

See extended demo at [https://editor.bitwiser.in](https://editor.bitwiser.in)

A list of theme definitions to be used with [monaco-editor](https://microsoft.github.io/monaco-editor/) in browser. See [minimal demo]('https://bitwiser.in/monaco-themes/')

### Usage

```sh
npm install monaco-themes
```

#### API

##### Using `<script>`

```html
<script type="text/javascript" src="https://unpkg.com/monaco-themes/dist/monaco-themes.js"></script>
<script type="text/javascript">
    var tmThemeString = /* read using FileReader */
    var themeData = MonacoThemes.parseTmTheme(tmThemeString);
    monaco.editor.defineTheme('mytheme', themeData);
    monaco.editor.setTheme('mytheme');
</script>
```

##### Using webpack/node

```js
const parseTmTheme = require('monaco-themes').parseTmTheme;
```

#### Directly using themes

##### With webpack

```js
const monaco = /* require monaco */

import('monaco-themes/themes/Monokai.json')
  .then(data => {
      monaco.editor.defineTheme('monokai', data);
  })
```

##### Independently

Download this [repository](https://github.com/brijeshb42/monaco-themes/archive/master.zip) and extract and save `themes` directory in your project.

```js
/* load monaco */

fetch('/themes/Monokai.json')
  .then(data => data.json())
  .then(data => {
    monaco.editor.defineTheme('monokai', data);
    monaco.editor.setTheme('monokai');
  })
```

---

### Overview (`src/public/third_party/wnumb-1.2.0/README.md`)

wNumb
=====

wNumb - JavaScript Number &amp; Money formatting

# Documentation

Documentation and examples are available on [refreshless.com/wnumb](https://refreshless.com/wnumb/).

# Changelog

### 1.2.0 (*2019-10-29*)
- Changed: License is now MIT
- Added: Prettier code formatter
- Added: Minified version

### 1.1.0 (*2017-02-04*)
- Changed: Renamed `postfix` option to the proper `suffix`. `postfix` is remapped internally for backward compatibility;

# License

Licensed MIT, so free for personal and commercial use.

---

### Overview (`docs/book-isonim/static/img/deep_review/README.md`)

# Generated screenshots

The `*.png` files in this directory are **generated assets**, not hand-made
ones. The pages of the `deep_review` section serve them from
`/assets/img/deep_review/`.

Regenerate them with:

```bash
just capture-deep-review-assets
```

That builds a real two-commit Noir repository, records it twice with the
shipping `ct record`, collects a real dataset with the shipping
`ct review collect`, opens it with the shipping `ct review`, and photographs the
running window — so the images always show the shipping UI rather than a drawing
of it. It needs a built `ct` (`just build-once`) plus `nargo`, `Xvfb`, `xdotool`
and ImageMagick; if any is missing it fails with a named remedy and a non-zero
status rather than leaving stale images in place.

| File                | Where it is used | What it shows |
| ------------------- | ---------------- | ------------- |
| `review-window.png` | `deep_review/index.md` (Introduction) | The whole review: the VCS panel, the diff tab, and the Agent Activity panel. |
| `diff-tab.png`      | `deep_review/reading.md` | A close-up of the diff tab: the invocation stepper, the loop stepper, the value chips, the expand control. |
| `vcs-panel.png`     | `deep_review/reading.md` | A close-up of the VCS panel — the review's navigation surface: the `Review: <commit>` header, the trace-context selector, the totals, the `Unified Diff` toggle and the changed-file row with its coverage badge. Added by DS-1, when the section split gave that panel a walkthrough of its own and the whole-window shot proved too small to read it in. |

All three come from **one** capture run, so the commit id in the header is the
same picture to picture. Regenerating one image without the others is how they
start disagreeing; the recipe writes all three.

## Why they are checked in

The book is published from CI on every push, and the capture needs an X display
and the Noir toolchain. Making the doc build depend on that would make the docs
unpublishable whenever the capture environment is unavailable — so the images
are committed, and the recipe above is how you refresh them.

`tests/test_nav_order_matches_summary.nim` resolves every `/assets/...` link in
the content against this tree, so a page may only reference an image that
actually exists here; deleting one fails the suite rather than 404ing in a
browser. `tests/test_deep_review_section.nim` closes the other half of the
loop: every image the section references must also be one the capture script
above actually writes, so an image cannot survive here as an orphan that
nothing regenerates.

## Why Noir

The recording is a **materialized** trace, the trace kind the db-backend
collector was added for. DeepReview is not an rr-only feature, and the pictures
in the book should not imply that it is. A native/rr capture would additionally
need an rr-capable machine (`perf_event_paranoid <= 1`).

---

### Overview (`docs/book-isonim/static/img/visual_recordings/README.md`)

# Generated screenshots

The `*.png` files in this directory are **generated assets**, not hand-made
ones. `usage_guide/visual_recordings.md` serves them from
`/assets/img/visual_recordings/`.

Regenerate them with:

```bash
just capture-book-assets
```

That records a real GL trace and drives the real visual-replay player, so the
images always show the shipping UI rather than a drawing of it. It needs a
built `ct_gfx_player` (`codetracer-visual-replay`) and a built `ct_cli`
(`codetracer-native-recorder`); if either is missing it fails with a named
remedy and a non-zero status rather than leaving stale images in place.

## Why they are checked in

The book is published from CI on every push to `main`, and the capture needs a
GPU-capable player plus two recorder siblings. Making the doc build depend on
that would make the docs unpublishable whenever the capture environment is
unavailable — so the images are committed, and this recipe is how you refresh
them.

Two things keep that from rotting into "nobody regenerates these":

- `tests/test_nav_order_matches_summary.nim` resolves every `/assets/...`
  link against this tree, so a page may only reference an image that actually
  exists here. Deleting one fails the suite rather than 404ing in a browser.
- The recipe above is the single documented way to produce them, and it is
  wired into the reprobuild graph alongside the book build.

This file replaced a `PLACEHOLDERS.txt` which said the real captures went to
`docs/book/src/generated/visual_recordings/` — the *old* mdBook's output
directory, which is not checked in and which this book never reads. The
placeholder images were shipped as if they were the real thing.

---

### Book Isonim Content Index (`docs/book-isonim/content/index.md`)

---
title: Welcome to CodeTracer Docs
description: CodeTracer is a user-friendly time-traveling debugger designed to support a wide range of programming languages. Install it, record your first trace, and explore the usage guide.
order: 1
---
# Welcome to CodeTracer Docs

:::hero title="Welcome to CodeTracer Docs"
:::button href="/getting_started/installation" variant="primary"
Get Started
:::button href="https://github.com/metacraft-labs/codetracer/issues" variant="secondary"
Support
:::

## Overview

CodeTracer is a user-friendly time-traveling debugger designed to support a wide range of programming languages. It records the execution of a program into a shareable, self-contained trace file, which you can load to move forward and backward through the execution to examine the state of memory and variables at any point.

Use the sidebar to get started with installation, learn how to record and replay traces for your language, and explore the usage guide.

:::video xZsJ55JVqmU title="CodeTracer - Noir Release Demo"

## Start here

:::cards
:::card title="Getting Started" icon="/assets/img/icon__start.svg" href="/getting_started"
Install CodeTracer and record your first trace — Noir, Stylus, WASM, Ruby and Python.
:::card title="Reference" icon="/assets/img/icon__style.svg" href="/reference/build_systems"
Build systems, contributing, troubleshooting, environment variables and building the docs.
:::card title="Usage Guide" icon="/assets/img/icon__components.svg" href="/usage_guide"
The command-line interface, the GUI replay interface, tracepoints and the CodeTracer shell.
:::

## Popular articles

:::cards variant="compact"
:::card title="Introduction" href="/getting_started/introduction"
Getting Started
:::card title="Installation" href="/getting_started/installation"
Getting Started
:::card title="Tracepoints" href="/usage_guide/tracepoints"
Usage Guide
:::card title="Graphical interface" href="/usage_guide/gui"
Usage Guide
:::card title="Command-line interface" href="/usage_guide/cli"
Usage Guide
:::card title="Build systems" href="/reference/build_systems"
Reference
:::

---

### Book Isonim Content Deep Review Index (`docs/book-isonim/content/deep_review/index.md`)

---
title: Introduction
order: 0
---

# DeepReview

A diff is a proposal written in the language of text edits. It tells you,
exactly and completely, what the author changed. It cannot tell you what the
change **did**.

So a reviewer infers. You read `let contribution = (i as Field) * x;`, you build
a model of the loop in your head, you decide the arithmetic comes out right, and
you approve. Only the first of those steps is evidence. The rest is a simulation
you ran in your head against a program you have not seen run — and it is where
review defects come from.

**DeepReview is code review with the recordings attached.** You give CodeTracer
a diff together with one or more recordings of that code actually running, and
it renders the diff with what each changed line did: which lines the run
executed, how many times, and which values they held on each pass through a
loop.



## The questions it lets you answer

Each of these is a question about a change that a diff cannot answer at all, and
that a reviewer otherwise answers by inference, by asking the author, or by
checking the branch out and running it themselves.

**Did this line run at all?** Every line inside a recorded function is shaded
executed or not executed, on the diff itself. A hunk that no recording ever
entered looks different from one that ran on every pass, so "this branch is
never taken in practice" stops being a suspicion and becomes something you can
see.

**What value did it hold — on which pass through the loop?** Executed lines
carry inline value chips, and a loop the recording entered gets an iteration
stepper above it. Stepping it walks the same line through the values it actually
held: `contribution` through `0`, `5`, `10`, `15`, and `sum` through their
running total. You are reading measurements, not re-deriving them.

**Was this function called once, or forty times?** A recorded function carries an
invocation stepper (`‹ main: call 1 / 2 ›`), and the file rows carry coverage
counts. A helper the change touched lightly and a helper it put on a hot path
are different reviews, and the dataset already knows which one you are looking
at.

**Which parts of the change did the runs never exercise?** The same shading, read
the other way. It is the honest version of "is this tested?" — not a promise
that the untouched lines are wrong, but a list of the ones no recording has
anything to say about.

**What did the agent that produced this change actually do?** When a review is
collected inside an agent session, the Agent Activity panel shows that session's
conversation, loaded by reference against the agent backend. The change, the
evidence for it, and the reasoning that produced it are in one window.

## What a review actually is

Two commands and one artefact:

```sh
ct review collect …   # build a review dataset from recordings + a diff
ct review <PATH>      # open it
```

A **review dataset** is a self-contained `review.json`: the diff, the source of
each changed file, per-line coverage, the recorded flow (steps and values), and
the call tree. It is produced once and can be opened later, on another machine,
without the original recordings — which is what makes a review something you can
hand to somebody rather than a session you have to be present for.

DeepReview adds **no panel of its own**. A review routes its data into three
panels CodeTracer already has — the VCS panel, the Editor, and the Agent
Activity panel — and leaves your layout otherwise alone. Everything you already
know about the editor still applies inside a review.

## What it does not tell you

Worth stating in the same breath, because a tool that shows execution next to a
diff invites a stronger reading than it has earned:

- **It does not tell you the change is correct.** It tells you what happened in
  the runs you recorded. Judging whether that is what *should* have happened is
  still the review.
- **It is not a test report.** A review dataset carries no test results at all —
  no names, no statuses, no durations — so no surface reports them, and none
  shows a zero in their place. `ct test`
  is a separate command, and issues **no test certificates** — see
  [Not yet available](/deep_review/not_yet_available) for exactly how far it
  goes. Nothing in a review claims otherwise.
- **Coverage is observation, never reachability.** A recording can say a line
  was not observed. It can never say a line cannot be reached, and a review does
  not pretend the two are the same.
- **It reviews the changed code, not everything it calls.** Flow is collected for
  the functions the diff touched; a callee gets a call count and coverage, but no
  overlay of its own.

The full list of the things that are deliberately not there yet lives on
[Not yet available](/deep_review/not_yet_available). It is worth a read before
your first review: everything on it is real behaviour you will meet, written
down so you do not read it as a bug or, worse, as a measurement.

## Where to go next

- [Collecting a review](/deep_review/collecting) — the two commands, a worked
  example end to end, and what happens with native (rr) recordings.
- [Reading a review](/deep_review/reading) — the three panels, the flow overlay
  and the steppers.
- [The agent workflow](/deep_review/agent_workflow) — how an agent produces a
  review, and how to make it do so reliably.
- [Not yet available](/deep_review/not_yet_available) — the deferrals, named.

---

### Book Isonim Content Getting Started Index (`docs/book-isonim/content/getting_started/index.md`)

---
title: Getting Started
order: 0
---
# Overview

Welcome to the CodeTracer's getting started section. This guide will help you understand how to record and replay programs in each of the supported programming languages.

## Core Concept: Record and Replay

Unlike traditional debuggers that attach to a running process, CodeTracer works by first **recording** your application's execution into a trace file. This trace captures everything that happens during the run.

Once a recording is made, you can **replay** it as many times as you need in the CodeTracer GUI. This allows you to inspect the application's state at any point in time, move forwards and backwards through the execution, and use powerful features like tracepoints without having to run your application again.

Think of it like recording a video of your program's execution that you can then analyze in detail.

## How to Use This Guide

This guide is structured to help you get started quickly and then dive deeper into the features that interest you.

### General-Purpose Languages

These languages are recorded directly via the `ct` CLI (`ct record`, `ct run`):

- [**Python**](./python.md): Steps for installing the recorder, creating a trace, and replaying it with your interpreter.
- [**Ruby**](./ruby.md): Examples and steps how to trace a Ruby program.
- [**JavaScript / TypeScript**](./javascript.md): Instrument and record JS/TS programs.
- [**WASM**](./wasm.md): Examples and steps how to trace a WASM program.

### Zero-Knowledge Languages

- [**Noir**](./noir.md): Examples and steps how to trace a Noir program.
- [**Circom**](./circom.md): Trace witness generation for Circom zero-knowledge circuits.
- [**Miden**](./miden.md): Trace programs on the Miden STARK-based VM.
- [**Leo (Aleo)**](./leo.md): Trace Leo smart contracts on the Aleo network.

### Smart Contract Languages

These languages use dedicated recorder binaries. Record with the language-specific recorder, then view with `ct replay --trace-folder <dir>`:

- [**Solidity (EVM)**](./solidity.md): Trace Solidity contracts via debug_traceTransaction.
- [**Stylus**](./stylus.md): Examples and steps how to trace a Stylus contract.
- [**Cairo (StarkNet)**](./cairo.md): Trace Cairo programs and StarkNet contracts.
- [**Aiken (Cardano)**](./aiken.md): Step through UPLC CEK machine execution.
- [**Cadence (Flow)**](./cadence.md): Trace Cadence contracts via interpreter instrumentation.
- [**Move (Sui / Aptos)**](./move.md): Trace Move VM execution on Sui and Aptos.
- [**Solana**](./solana.md): Trace Solana SBF programs with DWARF source mapping.
- [**Sway (FuelVM)**](./sway.md): Trace Sway contracts on FuelVM.
- [**PolkaVM (ink!)**](./polkavm.md): Trace PolkaVM programs and ink! contracts.
- [**Tolk (TON)**](./tolk.md): Trace Tolk/FunC contracts on the TON blockchain.

---

### Book Isonim Content Usage Guide Index (`docs/book-isonim/content/usage_guide/index.md`)

---
title: Usage Guide
order: 0
---
# Overview

Welcome to the CodeTracer Usage Guide. This guide will help you understand how to use CodeTracer effectively to debug your applications.

## Core Concept: Record and Replay

Unlike traditional debuggers that attach to a running process, CodeTracer works by first **recording** your application's execution into a trace file. This trace captures everything that happens during the run.

Once a recording is made, you can **replay** it as many times as you need in the CodeTracer GUI. This allows you to inspect the application's state at any point in time, move forwards and backwards through the execution, and use powerful features like tracepoints without having to run your application again.

Think of it like recording a video of your program's execution that you can then analyze in detail.

## How to Use This Guide

This guide is structured to help you get started quickly and then dive deeper into the features that interest you.

*   **To learn about the different ways to interact with CodeTracer**, see the following sections:
    *   [**Graphical User Interface (GUI)**](./gui.md): Learn how to use the visual interface to replay traces, inspect state, and set tracepoints.
    *   [**Command-Line Interface (CLI)**](./cli.md): Discover how to record traces and manage trace files from your terminal.
*   **To learn about specific features**, see these sections:
    *   [**Tracepoints**](./tracepoints.md): A deep dive into using tracepoints for advanced debugging scenarios.
*   **To review a change with its recordings attached**, see the
    [**DeepReview**](/deep_review) section: what each changed line did — which
    lines ran, with which values — shown on the diff itself, plus the agent
    handoff.
*   **For advanced use cases**, explore these topics:
    *   [**CodeTracer Shell**](./codetracer_shell.md): For integrating CodeTracer with complex or custom build systems.

---

### Book Src Getting Started Overview (`docs/book/src/getting_started/overview.md`)

# Overview

Welcome to the CodeTracer's getting started section. This guide will help you understand how to record and replay programs in each of the supported programming languages.

## Core Concept: Record and Replay

Unlike traditional debuggers that attach to a running process, CodeTracer works by first **recording** your application's execution into a trace file. This trace captures everything that happens during the run.

Once a recording is made, you can **replay** it as many times as you need in the CodeTracer GUI. This allows you to inspect the application's state at any point in time, move forwards and backwards through the execution, and use powerful features like tracepoints without having to run your application again.

Think of it like recording a video of your program's execution that you can then analyze in detail.

## How to Use This Guide

This guide is structured to help you get started quickly and then dive deeper into the features that interest you.

### General-Purpose Languages

These languages are recorded directly via the `ct` CLI (`ct record`, `ct run`):

- [**Python**](./python.md): Steps for installing the recorder, creating a trace, and replaying it with your interpreter.
- [**Ruby**](./ruby.md): Examples and steps how to trace a Ruby program.
- [**JavaScript / TypeScript**](./javascript.md): Instrument and record JS/TS programs.
- [**WASM**](./wasm.md): Examples and steps how to trace a WASM program.

### Zero-Knowledge Languages

- [**Noir**](./noir.md): Examples and steps how to trace a Noir program.
- [**Circom**](./circom.md): Trace witness generation for Circom zero-knowledge circuits.
- [**Miden**](./miden.md): Trace programs on the Miden STARK-based VM.
- [**Leo (Aleo)**](./leo.md): Trace Leo smart contracts on the Aleo network.

### Smart Contract Languages

These languages use dedicated recorder binaries. Record with the language-specific recorder, then view with `ct replay --trace-folder <dir>`:

- [**Solidity (EVM)**](./solidity.md): Trace Solidity contracts via debug_traceTransaction.
- [**Stylus**](./stylus.md): Examples and steps how to trace a Stylus contract.
- [**Cairo (StarkNet)**](./cairo.md): Trace Cairo programs and StarkNet contracts.
- [**Aiken (Cardano)**](./aiken.md): Step through UPLC CEK machine execution.
- [**Cadence (Flow)**](./cadence.md): Trace Cadence contracts via interpreter instrumentation.
- [**Move (Sui / Aptos)**](./move.md): Trace Move VM execution on Sui and Aptos.
- [**Solana**](./solana.md): Trace Solana SBF programs with DWARF source mapping.
- [**Sway (FuelVM)**](./sway.md): Trace Sway contracts on FuelVM.
- [**PolkaVM (ink!)**](./polkavm.md): Trace PolkaVM programs and ink! contracts.
- [**Tolk (TON)**](./tolk.md): Trace Tolk/FunC contracts on the TON blockchain.

---

### Book Src Usage Guide Overview (`docs/book/src/usage_guide/overview.md`)

# Overview

Welcome to the CodeTracer Usage Guide. This guide will help you understand how to use CodeTracer effectively to debug your applications.

## Core Concept: Record and Replay

Unlike traditional debuggers that attach to a running process, CodeTracer works by first **recording** your application's execution into a trace file. This trace captures everything that happens during the run.

Once a recording is made, you can **replay** it as many times as you need in the CodeTracer GUI. This allows you to inspect the application's state at any point in time, move forwards and backwards through the execution, and use powerful features like tracepoints without having to run your application again.

Think of it like recording a video of your program's execution that you can then analyze in detail.

## How to Use This Guide

This guide is structured to help you get started quickly and then dive deeper into the features that interest you.

*   **To learn about the different ways to interact with CodeTracer**, see the following sections:
    *   [**Graphical User Interface (GUI)**](./gui.md): Learn how to use the visual interface to replay traces, inspect state, and set tracepoints.
    *   [**Command-Line Interface (CLI)**](./cli.md): Discover how to record traces and manage trace files from your terminal.
*   **To learn about specific features**, see these sections:
    *   [**Tracepoints**](./tracepoints.md): A deep dive into using tracepoints for advanced debugging scenarios.
*   **For advanced use cases**, explore these topics:
    *   [**CodeTracer Shell**](./codetracer_shell.md): For integrating CodeTracer with complex or custom build systems.

---

### A (`docs/a.md`)

### CI

* We pass `-Ddisable32bit=ON` to rr on CI because I might need to set up additional g++ things (g++-9 ?)
* We build builds with more cached binaries/sources (e.g. rr, nim) for master push-es and "full rebuild" for merge requests, pushing directly to stable is forbidden from GitLab settings
* I should stop changing `/home/codetracer/codetracer/` state on server out of builds

### Local

I sometimes do
`set -gx PATH /home/user/codetracer/src/build-debug $PATH`
to get many codetracer-related binaries in my path: `codetracer`, `tester` and maybe others

# Fix preloading

# Fix history

# Fix the tests to work on nim and c++

# Monitor memory performance: and maybe gc values

# Pseudo: show how sum is working, stepping through complexity with preloading multiline/parallel

# Nim: nilcheck, go through the whole nilcheck with breakpoint/jump function with preloading multiline/parallel

# Nim: nilcheck, fix a bug in ref checking

# Tracepoints:
  Tracepoint: `active` and `not active`, `valid` and `invalid`

  Trace: `running` and `stopped`.

  We can just run one trace at a time.
  It always runs all the `active` and `valid` tracepoints.
  Basically it works like inserted logging.

  We can add a tracepoint to path and line for now.
  If we want conditional tracepoints, we use `if`.
  We can't add two tracepoints to the same location.
  If the path and location exist, it's valid.


# Actions:
  `debug`: `loading`, `ready`, `running` and `finished`.

  # `interrupt` ?

  We can only run moves when it's `ready`. Otherwise, we ignore the moves: we don't buffer them
  or something.

  Or we buffer them: and we try to show the remaining items: however can we "ignore" those? maybe no, because we can go back easily.

  With `loading` we don't buffer yet.

  If we get stuck, we should `pause` which should get us into `ready` state.

  Errors: we just show the place where we are. we get to `ready`.

  `finished` : just have a "run" which should get us to a breakpoint or main if no breakpoint?

  Should we hit main if we have a breakpoint?

# event-jump:
  We jump to an event:
    If we hit an error, we should try to present an ok error message and just leave where we are

    If we dont : we get to the new position or we pause

    Can we cancel ? Pausing should be basically cancelling and should try to get us back to the previous location
    but maybe thats cancel? And pausing should just interrupt? But then resuming is hard
    so in this case probably keep a pause/cancel for now.

# calltrace-jump:
  We jump to an event:
    error: error message, leave where we are
    else: new position or pause

# events prestarting:
  We just get to those

---

### Async Notes (`docs/async_notes.md`)

async/iterator stepping:

  * Current state: stepping literally, special keys, start of a multi-lang support focused on concurrency/async(?)
  * Think of C/C++/Rust as well
  * Think of both `stdlib`(and our patched version of it) `asyncdispatch` and `chronos`
  * What is co-step-in:
      It's continuing to next line in the current context(iterator/async function) or entering a call called by us:
        even if it happens later and invokes additional calls internally(? if awaited, otherwise not)
  * What is co-next:
      It's continue to next line in current context(iterator/async function) or if context finished, on callsite if possible(? maybe we jump/finish in other place because of a signal or other)
  * What is co-step-in for iterators:

  	Taken from stdlib of nim: `sequtils.nim`, filter
  	credit to authors and maintainers of the nim stdlib

    ```nim
    for i in 0 ..< s.len:
      if pred(s[i]):
        yield s[i]
     ```

    Normal step-in if no yield
    Step-in for yield as well? Stepping in caller which should send to actual user
  * What is co-next for iterators:
  	normal next if no yield
    continue to next entrance in internal function (probably name_iter) if yield or step-out if finished(but this should happen on last entrance in internal function? Maybe we might not hit that if something happens while yielding)
  * What about async:
    Expand a bit of code

  * Eventually TODO: fix `colonenv_ = .. newObj(..);` location directive from `internalNew` in `system.nim` to local function

  * C++: coroutines: c++ co-next seems to work, but still requires sometimes two steps and is probably buggy, co-step-in doesn't
  * C `libdill`: TODO
  * Rust async_std/tokio: TODO
  * Nim `asyncdispatch`/`chronos`: TODO


  * Detect context:
    * Co-step-in:
      * Normal: do a normal step-in and then check context again: if normal/generator: ok, else if wrapper: run to generator context
      * Wrapper: run to generator context(find equivalent generator function, make a breakpoint there, disable others, continue to it, delete the breakpoint, enable others): TODO what if we are finished, and we don't hit the generator? Maybe check some fields? After run, check context: if generator: ok, else: error(not expected)
      * Async generator/generator: (same as normal) do a normal step-in/co-next and then check context again: if normal/same generator: ok, else if wrapper: if not finished: run to same generator context, else: next to normal/other generator

    * Co-next:
      * Normal: do a normal next and then check context again: if normal/generator: ok, else if wrapper: run to generator context(maybe we finish a function and return to a wrapper)
      * Wrapper: run to generator context, after run check context: if generator: ok, else: error(not expected)
      * Async generator/generator: do a normal next, check context again: if not in the same generator: we probably returned(yielded) so if not finished: run to same generator context, else: next to normal/other generator
        else: ok

   OK, for C++ it's different: we might keep a separate logic for now

   libdill:
     * normal-or-worker context, prologue, wrapper, potential-switch context

     step-in or next: beginning, otherwise same

     normal-or-worker: step-in/next and check context again: then for normal-or-worker: ok, otherwise do the same as others
     prologue: finish, step-in again
     wrapper: continue to potential-switch context and then next until normal-or-worker
     potential-switch: next until normal-or-worker

  rust:
    async-std:
      we hit a rr(?) problem when reverse-next in async function: maybe because of smart next handling/custom gdb rust commands?
      otherwise step and use `{{closure}}` version as equivalent to generator context

    tokio:
      similar rr(?) problem when reverse-next in async function
      otherwise step and use `{{closure}}` similarly to async-std

---

### Build (`docs/build.md`)

notes about build and dependencies

### monaco-editor notes

* TODO: list
* Temporary : copy all to `editor.main.js`
IMPORTANT: change that to normal build or a simpler auto operation ?

### golden-layout notes

* For now build in a copied repo of it
* Copy its `dist/umd/golden-layout.js` file to `src/public/third_party/goldenlayout.js`
* Comment out the `// else if(typeof define === 'function' && define.amd) .. // define(["jquery"], factory);`
TODO: license

---

### Call Interleave (`docs/call_interleave.md`)

call interleave
==================

An idea for a possible feature:

We can interleave event and other info with callstack
and calltrace:

```markdown
---
  |___ a1()
      |
      | <output 2>
      |
      | <signal error>
```

This way we can have an easier way to see order of events and
to correlate them.

It can make easy some workflows:
e.g. seeing call and arg development and important events
and being able to quickly click for history of variable
and getting to a root cause there.


* Design: use the existing calltrace design: work on lines still TODO and show events with some styling under the call name/args vertically

* Backend support: should be just a recombination of existing data, it would be good to have good history support

* Search: we can have some kind of syntax / options for searching calls with certain events

---

### Ci Server (`docs/ci-server.md`)

### Setup

### Set up the container options


* Eventually we needed to set up the lxd container as privileged to make it possible to run the `aufs` driver for docker
* Also it seems we need to do this https://stackoverflow.com/questions/46645910/docker-rootfs-linux-go-permission-denied-when-mounting-proc/46648124#46648124 to be able to change system limits for rr and codetracer

### Install docker

* Install docker from their apt repository:
  * Running commands from https://docs.docker.com/engine/install/ubuntu/ -> https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository

We use those commands:

```bash
sudo apt-get remove docker docker-engine docker.io containerd runc

sudo apt-get update

sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt-get update

sudo apt-get install -y docker-ce docker-ce-cli containerd.io # original command doesn't pass -y: sudo apt-get install -y docker-ce docker-ce-cli containerd.io
```

### Install `gitlab-runner`

* Install gitlab-runner from their apt repository:
  * Running commands from https://docs.gitlab.com/runner/install/linux-repository.html#installing-gitlab-runner

```bash
# needs sudo!
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash

sudo apt-get install -y gitlab-runner # original command doesn't pass -y : sudo apt-get install gitlab-runner
```

### Build `codetracer_base` docker image

* Copy `build` folder from a codetracer repo to the server:

```bash
scp -r build codetracer-hetzner:/home/codetracer/build
```

* `codetracer-hetzner` is in my ssh config file as a host section: you might need to replace it
* `rsync` might be a faster way to copy files, but `build` is a small folder. TODO: do we need to document usage of rsync?

* Make the image

```bash
sudo docker build -t codetracer_base .
```

### Configure linux limits/settings for rr/codetracer

* Enough file watches

```bash
echo fs.inotify.max_user_watches=700000 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p;
```

* Enable `perf_event_paranoid=1`

```bash
echo kernel.perf_event_paranoid=1 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
```

### Configure `gitlab-runner`: copy `config.toml`

* Copy `config.toml` file from a codetracer repo to the server:

Locally:

```bash
scp config.toml codetracer-hetzner:/home/codetracer/config.toml
```

On server:
```bash
sudo cp /home/codetracer/config.toml /etc/gitlab-runner/config.toml
```

* `codetracer-hetzner` is in my ssh config file as a host section: you might need to replace it (see notes for building codetracer_base docker image: copying `build`)

* You can also use `gitlab-runner register` to dynamically fill such a section

* You might need to change docker volumes in config.toml

## Configuring GitLab runners: in the GitLab runners web interface and command line/config.toml

* You might need to get new or custom runner token from our/your gitlab repo's runner web interface:
  * Visit your repo's web interface
  * `Settings` > `CI/CD` > `Runners` > `Specific Runners`
  * It seems the url for our repo might be <https://gitlab.com/metacraft-labs/code-tracer/CodeTracer/-/runners#js-runners-settings> or
      <https://gitlab.com/metacraft-labs/code-tracer/CodeTracer/-/settings/ci_cd>
* Use the token and run on the server

```bash
export GITLAB_RUNNER_TOKEN=<insert token>

sudo gitlab-runner register \
  --non-interactive \
  --url https://gitlab.com \
  --registration-token $GITLAB_RUNNER_TOKEN \
  --executor docker \
  --docker-image codetracer_base:latest \
  --paused
# maybe remove the token or just use it directly in the command
```

* Copy the new added by gitlab token from /etc/gitlab-runner/config.toml and replace it in our copy from the repo's config.toml
* Copy this fixed version of the repo config.toml to /etc/gitlab-runner/config.toml
* Run `sudo gitlab-runner restart` (not sure if always required)
* Check for a new runner in the gitlab runners interface
* Make sure the setting for the runner's flag `Can run untagged jobs` is `Yes` in the gitlab runners interface
* Unpause the runner

(This GitLab token setup workflow was based on https://stackoverflow.com/questions/54658359/how-do-i-register-reregister-a-gitlab-runner-using-a-pre-made-config-toml)

* If other changes were needed in the repo config.toml file, change it and commit it

* To test: push a commit to the gitlab repo or start a job manually from gitlab web interface

### Notes

I use `fish`, so I install it with `sudo apt install fish`. Keep in mind some of those comm ands use syntax that requires `bash`.

---

### Cla (`docs/cla.md`)

Metacraft Labs Ltd Software Grant and Contributor License Agreement (“Agreement”)
======================================================================================


This agreement is based on the Apache Software Foundation Contributor License Agreement. (v r190612)

Thank you for your interest in software projects stewarded by Metacraft Labs Ltd.
In order to clarify the intellectual property license granted with Contributions from any person or entity, Metacraft Labs Ltd must have a Contributor License Agreement (CLA) on file that has been agreed to by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of Metacraft Labs Ltd and its users; it does not change your rights to use your own Contributions for any other purpose. This Agreement allows an individual to contribute to Metacraft Labs Ltd on that individual’s own behalf, or an entity (the “Corporation”) to submit Contributions to Metacraft Labs Ltd, to authorize Contributions submitted by its designated employees to Metacraft Labs Ltd, and to grant copyright and patent licenses thereto.

You accept and agree to the following terms and conditions for Your present and future Contributions submitted to Metacraft Labs Ltd. Except for the license granted herein to Metacraft Labs Ltd and recipients of software distributed by Metacraft Labs Ltd, You reserve all right, title, and interest in and to Your Contributions.

1) Definitions. “You” (or “Your”) shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with Metacraft Labs Ltd. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, “control” means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. “Contribution” shall mean any work, as well as any modifications or additions to an existing work, that is intentionally submitted by You to Metacraft Labs Ltd for inclusion in, or documentation of, any of the products owned or managed by Metacraft Labs Ltd (the “Work”). For the purposes of this definition, “submitted” means any form of electronic, verbal, or written communication sent to Metacraft Labs Ltd or its representatives, including but not limited to communication on electronic mailing lists, source code control systems (such as GitHub), and issue tracking systems that are managed by, or on behalf of, Metacraft Labs Ltd for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as “Not a Contribution.”

2) Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to Metacraft Labs Ltd and to recipients of software distributed by Metacraft Labs Ltd a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works.

3) Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to Metacraft Labs Ltd and to recipients of software distributed by Metacraft Labs Ltd a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) were submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed.

4) You represent that You are legally entitled to grant the above license. If You are an individual, and if Your employer(s) has rights to intellectual property that you create that includes Your Contributions, you represent that You have received permission to make Contributions on behalf of that employer, or that Your employer has waived such rights for your Contributions to Metacraft Labs Ltd. If You are a Corporation, any individual who makes a contribution from an account associated with You will be considered authorized to Contribute on Your behalf.

5) You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others).

6) You are not expected to provide support for Your Contributions,except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.

7) Should You wish to submit work that is not Your original creation, You may submit it to Metacraft Labs Ltd separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as “Submitted on behalf of a third-party: [named here]”.

---

### Codetracer Commands (`docs/codetracer_commands.md`)

### codetracer api


### `codetracer record`

You can record a trace with `codetracer record`.
It usually wraps a normal run of a binary: e.g. instead of `program args ...` you run `codetracer record program args ...`.
It can receive additional arguments however:
  * `-o/--output-folder <output-folder>`: the output folder where the trace should be stored (if it doesn't get exports as an archive)
  * `-e/--export <export-zip-path>`: export the trace as a zip file at the arg path

By default, it stores the trace as a directory inside `$HOME/.local/share/codetracer/trace-<trace-id>`.
(TODO: some parts of the code expect `$XDG_DATA_HOME` instead of `$HOME`: we should do it one way).

The archive options is useful when you want to send the trace to someone, or to upload it to a server or the cloud.
It can also be used by `codetracer shell`, when the `-e/--export` flag is passed to it(however, you might enable it using an environment variable `CODETRACER_EXPORT_RECORD_FOLDER=<shell-records-output-folder>` TODO this functionality).

The record should generate a directory including:
  * The rr trace after `rr pack` ! (it includes a copy of the original binary, and `rr pack` adds more data, so it can run in a portable manner on other machines)
  * Source (the source files : using source/ as prefix and appending to it as if it is root(both for absolute and for relative files))
  * `function_index.json` (the function index, however do we also copy all of those files there? or only the sources?)
  * `trace_metadata.json` (trace data that we store in db now, maybe some additional like arch/compiler/date/user?)

The record can be run with `codetracer run` on desktop, or `codetracer host` for a browser session
(or if we upload it: from the web interface)

### `codetracer run`

You can run a trace with `codetracer run`.

TODO

### `codetracer import`

```bash
codetracer import trace.zip # imports trace.zip into folder `trace` and local db
codetracer import trace.zip $TMPDIR/trace # import trace.zip into folder `$TMPDIR/trace` and local db
```

TODO

`codetracer run folder` (# search in local db for program and args and trace_metadata.json timestamp : if different/no match, assume it's a new/unknown one
and import, run for the id)
same with `codetracer import folder.zip; codetracer host folder`

---

### Concurrent Calltrace (`docs/concurrent_calltrace.md`)

* Concurrent calltrace

Interface ideas:

Based on (especially the last one)  top/htop/ [tokio-console](https://github.com/tokio-rs/console) / https://perfetto.dev/ / [goroutine gdb](https://go.dev/doc/gdb)

How:
  Normal calltrace

  async/thread/goroutine:

    start():
      let node = await parse(code)
      let typed_node = await typecheck(node, env)
      let asm = await gen_asm(typed_node)
      await assemble(asm)
      await print("ready")

    start:
      parse(code) node:
        parse_section(code1)
        parse_section(code2)
      typecheck(node, env) typed_node
      gen_asm(typed_node) asm
      assemble(asm)
      print("ready")


    start():
      var node_futures = []
      for code in code_list:
        node_futures.push(go_without_wait parse(code))
      let nodes = await join(node_futures)

    start #0:
      .. other
      parse #2 %2 (code):
        parse_section(code1)
      parse #3 %3 (code):
        parse_section(code1)
      parse #4 %4 (code):
        parse_section(code1)
      parse #5 %2 (code) node:
        parse_section(code2)
      push_in_join #6  (node)
      parse #7 %3 (code) node:
        parse_section(code2)
      push_in_join #8 (node)
      parse #9 %4 (code) node:
        parse_section(code2)
      push_in_join #10 (node)

    start:
      % concurrent group
      parse #2,#5 %2 (code) node:
        #2                            #5
        parse_section(code1) ..->%3.. parse_section(code2)
      parse #3,#7 %3 (code) node:
        #3                            #7
        parse_section(code1) ..->%4.. parse_section(code2)
      parse #4,#9 %4 (code) node:
        #4                            #9
        parse_section(code1) ..->%2.. parse_section(code2)
      push_in_join #6 (node) <- %2
      push_in_join #8 (node) <- %3
      push_in_join #10 (node) <- %4
      % end concurrent group

    start:
      % concurrent group
      parse #2,#5 %2 (code) node:
        #2
        parse_section(code1)
        ..->%3..
        #5
        parse_section(code2)
      parse #3,#7 %3 (code) node:
        #3
        parse_section(code1)
        ..->%4..
        #7
        parse_section(code2)
      parse #4,#9 %4 (code) node:
        #4
        parse_section(code1)
        ..->%2..
        #9
        parse_section(code2)
      push_in_join #6 (node) <- %2
      push_in_join #8 (node) <- %3
      push_in_join #10 (node) <- %4
      % end concurrent group

    start:
      % concurrent group
      parse #2,#5 %2 (code) node:
        #2
        parse_section(code1)
          ..->%3..
            ..%4->..
              #5
              parse_section(code2)
                ..->%3
      parse #3,#7 %3 (code) node:
          #3
          parse_section(code1)
            ..->%4..
              ..%2->..
                #7
                parse_section(code2)
                  ..->%4
      parse #4,#9 %4 (code) node:
            #4
            parse_section(code1)
              ..->%2..
                ..%3->..
                  #9
                  parse_section(code2)
      push_in_join #6 (node) <- %2
      push_in_join #8 (node) <- %3
      push_in_join #10 (node) <- %4
      % end concurrent group



    start:
      % concurrent group
      parse #2,#5 %2 (code) node:
        #2
        ->parse_section %5(code1)
                                  ..->parse %3..
                                                                                      ..parse_section %5->..
                                                                                              #5
                                                                                              parse_section %8(code2)
                                                                                                              ..->%3
      parse #3,#7 %3 (code) node:
                                  #3
                                  ->parse_section %6(code1)
                                                            ..->parse %4..
                                                                                               ..
      parse #4,#9 %4 (code) node:
                                                            #4
                                                            ->parse_section %7(code1)
                                                                                      ..->parse_section %5..
                                                                                                 ..
      push_in_join #6 (node) <- %2
      push_in_join #8 (node) <- %3
      push_in_join #10 (node) <- %4
      % end concurrent group

---

### Core (`docs/core.md`)

## Core

A short description of the core component of CodeTracer:

It is implementing the internal core of the CodeTracer's functionality.
It exposes an API to communicate with clients and maintains the internal
rr/gdb processes that we use to automate debugging operations.

The core consists mostly of two actual folders of code:

* `src/core`, which includes the middleware sitting between clients and the actual rr/gdb processes. it is written in Nim and implements a named socket-based typed rpc-like api by which it receives calls from clients and responds with serialized results.
* `src/gdb`, which includes our code which scripts the internal rr/gdb processes. it is written in Python, and it uses the gdb's Python API. It communicates with the middleware using direct gdb API custom command calls for input and internal json append-only files for result output.

### Starting the rr/gdb processes

The component that actually *starts* the rr/gdb processes currently, is a Python script
`tools/codetracer_rr_gdb_manager.py`. It is used internally in the `codetracer` binary,
which calls it in a proper way to set up the different commands and the `core`: e.g.
in the beginning of `codetracer run`, `codetracer host` or `codetracer start_rr_gdb_and_core.

We probably want to rework this python script into a better and more general pool-maintaining tool, written in a better way.


### Task processes

We have several groups of tasks that we usually run in separate core processes.

Let's take a look:

* stable: the main experience for the user, where he steps, jumps and moves in the program
* trace: a process, where we evaluate tracepoints
* history: a process, where we try to evaluate the history of variables/expressions
* flow: here we preload the flow and local expression values for certain calls
* calltrace: here we preload the graph of some of the next calls after some point

It's possible to expand some of those processes to several sub-processes, e.g. for
parallelism. It's also possible to add new task groups.

Zahary had a better design in mind, where the core can dynamically
load processes with various properties
from some kind of service(e.g. generalized version of the codetracer rr/gdb manager) and use them in a more flexible way for different tasks.

However, currently the way it works is static, where we create the needed processes before starting core (as explained in the previous section) and eventually we
interrupt and restart some of those if needed (e.g. if a new flow request comes which invalidates the older running one)

### Core middleware

The core middleware implements:
  * A typed API for communication with clients, specified with the macro `handle` in the end of `dispatcher.nim`(a corresponding one for the web client is defined with `defineAPI` in src/index_config.nim. we can somehow unify them, but they're currently separate).
  * A typed API for communication with our custom rr/gdb python commands, specified in `py_command.nim`
  * The logic for some of the debugging operations, e.g. most of the tracepoint logic in `trace_engine.nim`, some of the flow preloading logic in `flow_preloader.nim`, the event log
  loading logic in `eventLoad` in `dispatcher.nim`, etc
  * A central dispatcher which sits between the client and the task-specific internal processes
  * Task process entrypoint which runs tasks from different task groups in different task processes with their dedicated rr/gdb process(e.g. flow tasks in the flow task process with a flow rr/gdb process)
  * A named socket-based mechanism for communication between the dispatcher and the internal task processes (in `dispatcher.nim`, `dispatcher_global.nim` and `task_process.nim`)
  * Different utilities

### rr/gdb scripts

the rr/gdb custom scripts we have implement some of the core logic of CodeTracer. Critical parts of flow, calltrace and primitives for most other tasks are implemented as python commands and methods:

  * A set of Python GDB API commands corresponding to the primitives available for core
  * A hierarchy of classes corresponding to different lang implementation:
    * LangImpl
      * NativeBackend
        * COrCpp
          * C
          * Cpp
        * Rust
        * Nim
        * Go
      * VMBackend
        * Python
  * classes for several task groups:
    * `CallsAndArgsEngine` in `calltrace.py`
    * `Evaluator` in `tracepoint.py`
    * A module with event jump logic in `event_jumps.py`
    * Experimental/not finished: concurrent/async handlers in `concurrent_handlers.py`
  * Utilities
  * A small amount of tests for tracepoints/loading values in in `src/gdb/tests/`

### Logging/debugging

Currently one of the main ways to debug the core is using its logs.

A better way would be a debugger, but I haven't worked out that well. ( :) )

We generate many log files, and we provide some helper commands to view them
based on the `just` tool.

Most of the log files include the initial codetracer process pid, called `caller pid`(not a great name maybe, we can change it).

In the nim middleware (`src/core`) we log using the `chronicles` library.

We usually generate a `dispatcher_<caller pid>.log`, and also a `task_process_<caller pid>_<group name>.log` for each task group.

In the python rr/gdb scripts (`src/gdb`) we log usually using a helper called `nested_log`.
We generate most of the logs in `$TMPDIR/codetracer/log_<caller pid>_<group name>.log`.

We save the `rr/gdb` raw process text in `/dev/shm/codetracer/<group name>_<caller pid>.txt`.

We store the `rr/gdb` script results as a line with id and json in `$TMPDIR/codetracer/<caller pid>_<group name>`.

You can see some of the helper commands using `just -l`.
Related to logs are:

* `just core-logs <pid>` # nim middleware logs
* `just rr-gdb-logs <pid>` # raw process logs
* `just script-logs <pid>` # rr/gdb python script logs
* `just command-results` # id/json python command results
* `just last-dispatcher-logs`
* `just last-task-logs <name>`
* `just last-script-logs <name>`
* `just last-command-results <name>`
* `just clean-logs`

(the `last-*` commands depend currently on the user closing the application first with e.g. `ALT+F4`, which is not good, we have to improve that)

### Assumptions/limitations

TODO: write down some important assumptions, part of the system
TODO: write down current limitations

### Caching

Currently, we have a limited amount of caching logic in the core,
but we have to optimize that

### Data types

Currently, the nim core middleware and the frontend reuse the same `src/types.nim` as shared type definitions. The python code defines many of those types by itself using classes or `mypy`/`typing` definitions.

We use the `nim-json-serialization` and `nim-serialization` libraries to serialize/deserialize most of those values around.

### Clients

Currently, we maintain mostly the `electron`/`server+browser web` client based on `src/index.nim`, `src/index_config.nim` and `src/ui`.

We also have an experimental repl client in `src/repl.nim` and we have a prototype for a tui one in `src/tui.nim`, they both depend on `src/ui_data.nim`: repl was used in the past for
core integration and property tests, and can be potentially useful by itself for people
that prefer the console instead of rich desktop/browser apps.

Theoretically, in the future, additional clients can be written down, however the current abstractions and methods of communications are probably suboptimal, so this has to be planned and improved first, probably.

### Languages

Currently, we are trying to support

* C
* C++
* Rust
* Nim
* Go(not a lot of work has been done here, mostly a prototype)
* Python(more work here, but again, more experimental, mostly for internal usage)

We had prototypes for Ruby/Lua in the past, and support for other native language implementations or simple interpreters(no JIT) shouldn't be too hard if we need it:

Eventually TODO more detailed documentation of core from a lang support view.

---

### Ct Test Provider Guide (`docs/ct-test-provider-guide.md`)

# Adding a ct-test Provider

This is the release-gated checklist for adding a new language or framework
provider to `ct test`.

## Research

Add a short research note under `src/ct_test/framework_research/` before
adding code. It should state:

- framework discovery commands and stable machine-readable output, if any
- source location strategy and expected confidence level
- run and record command templates
- trace entry-point mapping strategy
- unsupported modes and the diagnostic users should see
- required toolchains, recorder binaries, and any heavy CI dependencies

## Provider Contract

Add the provider implementation under `src/ct_test/frameworks/` and register it
in `newDefaultProviderRegistry()`. Capability flags must be exact:

- Set discovery flags only for scopes the provider can populate.
- Set run flags only when `run` emits structured `TestEvent` data or a clear
  diagnostic for unsupported runtime execution.
- Set record flags only when `record` can create a non-empty trace artifact and
  emit `tekRecordingCreated`.
- Set `canMapTraceEntryPoints` only when trace metadata can be mapped back to
  catalog item ids.

Conditional or toolchain-heavy providers must make the condition explicit in
diagnostics and tests. Do not expose GUI actions for unsupported capabilities.

## Enumerating Workspace Files

A provider must **never** call `walkDirRec` (or any other unrestricted
traversal) on the workspace root. Use `walkWorkspaceFiles(root)` from
`src/ct_test/workspace_scope.nim` instead — `import ../discovery` re-exports it:

```nim
for path in walkWorkspaceFiles(projectRoot):
  if isCandidateFile(path):
    result.add path
```

Two reasons, both learned the expensive way:

1. **Scope.** `walkWorkspaceFiles` yields only the files the workspace claims as
   its own — it honours the project's `git ls-files --cached --others
   --exclude-standard` inventory (full `.gitignore` chain, stops at nested
   checkouts, keeps untracked-but-not-ignored files) and falls back to a pruning
   filesystem walk elsewhere. A vendored upstream source tree such as
   `references/llvm-project` is therefore invisible to every provider at once,
   instead of each provider needing its own reject list — and forgetting.
2. **Cost.** `discover --workspace` runs `detect` on every registered provider
   and `discoverProject` on every one that claims the workspace. The scope is
   resolved once per invocation and shared, so N providers cost one traversal
   rather than N.

A `detect` implementation that reads file *contents* to decide whether it owns
the workspace is the most expensive probe in the registry. Prefer a project
marker (`Cargo.toml`, `go.mod`, `package.json`, …) and treat the content scan as
a last resort — and when you do scan, guard `readFile` so an unreadable file
cannot abort discovery.

## Fixtures And Trace Smoke

Every provider needs a representative fixture under `src/ct_test/fixtures/` or,
for sibling recorder harnesses, explicit release-gate metadata pointing to the
heavy fixture test. The provider test must cover discovery and every declared
run/record capability. Record-capable providers must verify that a non-empty
trace artifact exists and that the event stream validates.

Update `src/ct_test/release_gate.nim` with the provider fixture, research note,
test file, and source files. Then regenerate `docs/ct-test-support-matrix.md`
by running `just test-m16-release-gate`; the test fails if the matrix drifts.

## Launcher-backed ct test

Use launcher-backed commands in docs and workflows:

```bash
ct test discover --workspace . --json
ct test discover --file path/to/test_file --json
# Vendored/ignored trees are excluded by default. To reproduce the old
# unrestricted enumeration (rarely what you want):
ct test discover --workspace . --scope unscoped --json
ct test run --test <selector>
ct test record --test <selector>
```

Direct `ct-test-runner` or `ct-test` binary invocation is legacy-only for
low-level development and compile checks. CI and user-facing docs should go
through `ct test` so launcher path resolution, component selection, and recorder
environment handling match installed CodeTracer behavior.

## Release Gate

Run:

```bash
just test-m16-release-gate
```

CI runs the same script in the `ct-test-release-gate` job of
`.github/workflows/codetracer.yml`, and the cross-language counterpart
(`just test-ct-providers`, which additionally builds the native/JS/Ruby
recorder siblings) in `ct-test-providers`. Both are listed in
`ci/verdict/required-jobs.txt`, so a run in which either is skipped is
reported as lost coverage rather than as a pass.

The gate fails on:

- provider registry entries missing release-gate metadata
- stale support matrix or catalog schema fixture versions
- declared capabilities without fixture, research, provider tests, or source
  coverage
- record-capable providers that do not verify trace artifact creation
- skipped core ViewModel tests
- visible GUI actions that only have mock coverage and no explicit unsupported
  diagnostic

---

### Ct Test Run Orchestration Design (`docs/ct-test-run-orchestration-design.md`)

# ct_test Parallel-Run + Partition Orchestration — Design

Status: design (pre-implementation). Consolidates the standalone `ct-test`
repo's high-level test-run orchestration into codetracer's `src/ct_test`
cross-language framework, layered on the provider model and the shared process
runner (`process_exec` → `runquota_process`).

## Goal & layering

Move the **high-level orchestration** — parallel execution, `--partition`
sharding, result aggregation, JSON summary — into `src/ct_test`. Keep the
**low-level process scheduling** (launch, output capture, runquota lease
negotiation) in `runquota_process`, reached through `process_exec.execCaptured`.
ct_test owns *what to run and how to aggregate*; runquota_process owns *how to
launch*.

## Current state

- **Standalone `ct-test` / `apps/ct-test-runner`**: a Nim-test-*binary* runner.
  Process-per-test worker pool (`--threads N` worker threads pulling from a
  single `Lock`-protected queue; main thread joins on a barrier), `--partition
  file:<path>` allow-list filter, and a JSON summary (total / executed /
  skipped-by-partition / passed / failed, threads, wall time). Operates by
  scanning a bin dir, probing each binary for the `--list-json` protocol, and
  running it whole or per-protocol-test.
- **`src/ct_test` (codetracer)**: the cross-language provider framework.
  `runCtTest` exposes only `test discover`. Providers declare capabilities
  (`discover-project/file`, `locate-tests`, `run-project/file/single`,
  `record-*`) and return `seq[TestEvent]`. **No run / parallel / partition
  orchestration exists.**

## Design

### CLI surface

Extend `runCtTest` with a `test run` verb:

```
ct-test test run --workspace <root> [--file <f>]
                 [--partition file:<path>] [--threads N]
                 [--json] [--summary <path>]
```

1. **Enumerate** the candidate tests via the providers' `discover` /
   `locate-tests` (qualified ids from the `TestCatalog` / `TestItem` model).
2. **Filter** by the partition allow-list (`--partition file:` = one qualified
   id per line; `--shard k/N` reserved, mapping to the same allow-list).
3. **Run** each selected test via its owning provider's `run-single`
   (falling back to `run-file` when `canRunSingle` is false), each subprocess
   launched through `process_exec.execCaptured` (runquota_process).
4. **Aggregate** the per-test `seq[TestEvent]` into a run result + JSON summary:
   total discovered, dispatched, executed, skipped, skipped-by-partition,
   passed, failed, unrunnable, wall time, threads, verdict.

### What `executed` counts, and what the exit code says

`executed` counts **tests that finished** with `tsPassed`, `tsFailed` or
`tsErrored` — not units dispatched. This is the same definition
`certificate_issuance` uses for the certificate gate, deliberately: two
counters in one binary that disagree about what "executed" means is how the
exit code came to report success for a run the certificate refused to attest.
A `tsSkipped` test counts in `skipped` and nowhere else, because a skip runs no
assertion and is not evidence. The units-dispatched count is reported as
`dispatched` (always `total - skipped_by_partition`).

The exit status distinguishes three outcomes, because two cannot tell "nothing
ran" from "everything passed":

| Verdict | Exit | Meaning |
|---|---|---|
| `passed` | 0 | at least one test executed and none failed |
| `failed` | 1 | at least one executed test failed or errored (also: a rejected invocation) |
| `nothing-executed` | 2 | no test finished passed/failed/errored |

`nothing-executed` covers an all-skipped run, an allow-list that matched
nothing, and a dispatch every provider refused — one verdict with four
messages, mirroring `certificate_issuance`'s single `wrNoTestsExecuted` reason
with its two wordings. Every non-zero verdict prints its message and remedy on
stderr, including under `--no-certificate`.

A unit whose provider declares it cannot run that scope is **not dispatched**;
it is recorded as `unrunnable` with a `dsError` and surfaced in the summary's
`errors` array. A run that executed some tests and refused some units stays
green — partial coverage is normal — but says so loudly.

### Parallelism

Port the standalone worker-pool shape onto the provider model:

- A `Lock`-protected queue of `RunUnit` (one per selected test, carrying the
  provider id + `TestScope`).
- `N` worker threads (`--threads`, default = CPU count; `REPRO_TEST_THREADS`
  override, matching the standalone) each pull a `RunUnit` and call the owning
  provider's `run-single`, appending the returned events to a `Lock`-guarded
  results seq.
- The main thread joins on a barrier and then aggregates.

Process scheduling is **not** reimplemented here: every `run-single` launches
its subprocess through `execCaptured`, so output bounds, wall-time/peak-mem
accounting, and (when a session is configured) runquota lease governance are
uniform. (A single-thread `runquota_process` async-poll variant —
`launchProcess` + `pollCompletion` for N concurrent processes from one thread —
is a future refinement; the thread pool is the faithful port and the providers'
`run` is synchronous today.)

### Partition / CI-sharding integration

`--partition file:<path>` keeps the standalone's format (one fully-qualified
test id per line) so reprobuild's `repro test --shard k/N`
(reprobuild-specs/CI-Sharding.md) can synthesise a partition file and drive the
consolidated runner unchanged — preserving the reprobuild ↔ ct-test
composition. Tests discovered but absent from the partition count as
`skipped_by_partition` (not executed, not failed). Per CI-Sharding.md the
*intelligence* (cost model, bin-packing) stays in `repro test`; ct_test only
consumes the resulting allow-list.

### What moves vs. stays

- **Moves into `src/ct_test`**: the orchestration — queue, worker pool,
  partition filter, summary writer — adapted from `apps/ct-test-runner` to
  operate on the provider model instead of raw test binaries.
- **Stays in `runquota_process`**: launch, capture, lease negotiation (via
  `execCaptured`).
- **Not moved verbatim**: the standalone runner's binary-oriented bits
  (`scanTestBinaries`, the `--list-json` protocol probe). The consolidated
  runner is enumeration/provider-oriented. The standalone `ct-test` repo can
  keep its binary runner for the pure-Nim-binary path (CI-Sharding.md keeps
  `ct-test-runner` usable standalone) or later delegate to the consolidated
  one.

## Tests (no skips, no weakened assertions)

- **Unit**: partition-file parsing + filtering; summary aggregation; worker-pool
  determinism (fixed `--threads` ⇒ stable executed/skip/pass/fail counts).
- **Integration**: `ct-test test run` over an existing multi-test language
  fixture, with and without a partition file, asserting the exact
  executed/skipped/passed/failed counts and the per-test event kinds. Reuses the
  fixtures already in `src/ct_test/fixtures`.

## Milestones

1. `RunUnit` enumeration + partition parse/filter + summary types (pure;
   unit-tested in isolation).
2. Worker-pool execution via `provider.run-single` + `execCaptured`.
3. `test run` CLI wiring + JSON summary output.
4. CI-sharding partition-file compatibility + the integration test.

---

### Ct Test Support Matrix (`docs/ct-test-support-matrix.md`)

# ct-test Provider Support Matrix

Generated from `newDefaultProviderRegistry()` and `src/ct_test/release_gate.nim`.
Regenerate/check with `just test-m16-release-gate`.

| Provider | Language | Framework | Capabilities | Record | Trace map | Fixture | Gate test | Heavy |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `ada-fallback` | ada | AUnit fallback | discover-project, discover-file, locate-tests, run-project, run-file, record-file, trace-entry-map | yes | yes | `src/ct_test/fixtures/m12_ada_project` | `src/ct_test/m12_fallback_languages_test.nim` | no |
| `assembly-fallback` | assembly | executable fallback | discover-project, discover-file, locate-tests, run-project, run-file, record-file, trace-entry-map | yes | yes | `src/ct_test/fixtures/m12_assembly_project` | `src/ct_test/m12_fallback_languages_test.nim` | no |
| `cpp-catch2` | c++ | catch2 | discover-project, discover-file, locate-tests, run-project, run-file, run-single, record-single, trace-entry-map | yes | yes | `src/ct_test/fixtures/cpp_catch2_project` | `src/ct_test/cpp_providers_test.nim` | no |
| `cpp-ctest` | c++ | ctest | discover-project, locate-tests, run-project, run-single | no | no | `src/ct_test/fixtures/cpp_ctest_fallback_project` | `src/ct_test/cpp_providers_test.nim` | no |
| `cpp-gtest` | c++ | googletest | discover-project, discover-file, locate-tests, run-project, run-file, run-single, record-single, trace-entry-map | yes | yes | `src/ct_test/fixtures/cpp_gtest_project` | `src/ct_test/cpp_providers_test.nim` | no |
| `crystal-spec` | crystal | crystal spec | discover-project, discover-file, locate-tests, run-project, run-file, run-single, record-file, trace-entry-map | yes | yes | `src/ct_test/fixtures/crystal_spec_project` | `src/ct_test/m11_native_languages_test.nim` | no |
| `d-unittest` | d | d unittest/dub | discover-project, discover-file, locate-tests, run-project, run-file, record-file, trace-entry-map | yes | yes | `src/ct_test/fixtures/d_unittest_project` | `src/ct_test/m11_native_languages_test.nim` | no |
| `fortran-fallback` | fortran | pFUnit fallback | discover-project, discover-file, locate-tests, run-project, run-file, record-file, trace-entry-map | yes | yes | `src/ct_test/fixtures/m12_fortran_project` | `src/ct_test/m12_fallback_languages_test.nim` | no |
| `go-test` | go | go test | discover-project, discover-file, locate-tests, run-project, run-file, run-single | no | no | `src/ct_test/fixtures/go_test_project` | `src/ct_test/m11_native_languages_test.nim` | no |
| `js-jest` | javascript-typescript | jest | discover-project, discover-file, locate-tests | no | no | `src/ct_test/fixtures/js_jest_project` | `src/ct_test/js_providers_test.nim` | no |
| `js-node-test` | javascript-typescript | node:test | discover-project, discover-file, locate-tests, run-project, run-file, run-single, record-single, structured-events | yes | no | `src/ct_test/fixtures/js_node_test_project` | `src/ct_test/js_providers_test.nim` | no |
| `js-playwright` | javascript-typescript | playwright | discover-project, discover-file, locate-tests, run-project, run-file, per-test-output, structured-events | no | no | `src/ct_test/fixtures/js_playwright_project` | `src/ct_test/playwright_provider_test.nim` | no |
| `js-vitest` | javascript-typescript | vitest | discover-project, discover-file, locate-tests | no | no | `src/ct_test/fixtures/js_vitest_project` | `src/ct_test/js_providers_test.nim` | no |
| `julia-fallback` | julia | Test stdlib fallback | discover-project, discover-file, locate-tests, run-project, run-file | no | no | `src/ct_test/fixtures/m12_julia_project` | `src/ct_test/m12_fallback_languages_test.nim` | no |
| `lean-fallback` | lean | fixture fallback | discover-project, discover-file, locate-tests, run-project, run-file, record-file, trace-entry-map | yes | yes | `src/ct_test/fixtures/m12_lean_project` | `src/ct_test/m12_fallback_languages_test.nim` | no |
| `nim-unittest` | nim | std/unittest | discover-project, discover-file, locate-tests | no | no | `src/ct_test/fixtures/nim_unittest_project` | `src/ct_test/nim_unittest_provider_test.nim` | no |
| `noir-nargo` | noir | nargo test | discover-project, discover-file, locate-tests, run-project, run-file, run-single, per-test-output, structured-events | no | no | `src/ct_test/fixtures/noir_nargo_project` | `src/ct_test/noir_providers_test.nim` | no |
| `odin-fallback` | odin | fixture fallback | discover-project, discover-file, locate-tests, run-project, run-file, record-file, trace-entry-map | yes | yes | `src/ct_test/fixtures/m12_odin_project` | `src/ct_test/m12_fallback_languages_test.nim` | no |
| `pascal-fallback` | pascal | FPCUnit fallback | discover-project, discover-file, locate-tests, run-project, run-file, record-file, trace-entry-map | yes | yes | `src/ct_test/fixtures/m12_pascal_project` | `src/ct_test/m12_fallback_languages_test.nim` | no |
| `python-pytest` | python | pytest | discover-project, discover-file, locate-tests | no | no | `src/ct_test/fixtures/python_pytest_project` | `src/ct_test/python_providers_test.nim` | no |
| `python-unittest` | python | unittest | discover-project, discover-file, locate-tests | no | no | `src/ct_test/fixtures/python_unittest_project` | `src/ct_test/python_providers_test.nim` | no |
| `ruby-minitest` | ruby | minitest | discover-project, discover-file, locate-tests, run-project, run-file, run-single, record-single, trace-entry-map | yes | yes | `src/ct_test/fixtures/ruby_minitest_project` | `src/ct_test/ruby_providers_test.nim` | no |
| `ruby-rspec` | ruby | rspec | discover-project, discover-file, locate-tests, run-project, run-file, run-single, record-single, trace-entry-map | yes | yes | `src/ct_test/fixtures/ruby_rspec_project` | `src/ct_test/ruby_providers_test.nim` | no |
| `rust-libtest` | rust | libtest/cargo-test | discover-project, discover-file, locate-tests, run-project, run-file, run-single | no | no | `src/ct_test/fixtures/rust_libtest_project` | `src/ct_test/rust_libtest_provider_test.nim` | no |
| `smart-cairo` | cairo | cairo-vm | discover-project, discover-file, locate-tests, conditional run-file, conditional record-file, conditional trace-entry-map | conditional | conditional | `src/ct_test/fixtures/m13_smart_contract_project` | `src/ct_test/m13_smart_contract_harnesses_test.nim` | yes |
| `smart-cardano` | aiken | cardano-uplc | discover-project, discover-file, locate-tests, conditional run-file, conditional record-file, conditional trace-entry-map | conditional | conditional | `src/ct_test/fixtures/m13_smart_contract_project` | `src/ct_test/m13_smart_contract_harnesses_test.nim` | yes |
| `smart-circom` | circom | circom-witness | discover-project, discover-file, locate-tests, conditional run-file, conditional record-file, conditional trace-entry-map | conditional | conditional | `src/ct_test/fixtures/m13_smart_contract_project` | `src/ct_test/m13_smart_contract_harnesses_test.nim` | yes |
| `smart-evm` | solidity | evm | discover-project, discover-file, locate-tests, conditional run-file, conditional record-file, conditional trace-entry-map | conditional | conditional | `src/ct_test/fixtures/m13_smart_contract_project` | `src/ct_test/m13_smart_contract_harnesses_test.nim` | yes |
| `smart-flow` | cadence | flow-cadence | discover-project, discover-file, locate-tests, conditional run-file, conditional record-file, conditional trace-entry-map | conditional | conditional | `src/ct_test/fixtures/m13_smart_contract_project` | `src/ct_test/m13_smart_contract_harnesses_test.nim` | yes |
| `smart-fuel` | sway | fuelvm | discover-project, discover-file, locate-tests, conditional run-file, conditional record-file, conditional trace-entry-map | conditional | conditional | `src/ct_test/fixtures/m13_smart_contract_project` | `src/ct_test/m13_smart_contract_harnesses_test.nim` | yes |
| `smart-leo` | leo | aleo-avm | discover-project, discover-file, locate-tests, conditional run-file, conditional record-file, conditional trace-entry-map | conditional | conditional | `src/ct_test/fixtures/m13_smart_contract_project` | `src/ct_test/m13_smart_contract_harnesses_test.nim` | yes |
| `smart-miden` | masm | miden-vm | discover-project, discover-file, locate-tests, conditional run-file, conditional record-file, conditional trace-entry-map | conditional | conditional | `src/ct_test/fixtures/m13_smart_contract_project` | `src/ct_test/m13_smart_contract_harnesses_test.nim` | yes |
| `smart-move` | move | sui-aptos-move | discover-project, discover-file, locate-tests, conditional run-file, conditional record-file, conditional trace-entry-map | conditional | conditional | `src/ct_test/fixtures/m13_smart_contract_project` | `src/ct_test/m13_smart_contract_harnesses_test.nim` | yes |
| `smart-polkavm` | polkavm | polkavm | discover-project, discover-file, locate-tests, conditional run-file, conditional record-file, conditional trace-entry-map | conditional | conditional | `src/ct_test/fixtures/m13_smart_contract_project` | `src/ct_test/m13_smart_contract_harnesses_test.nim` | yes |
| `smart-solana` | rust | solana-sbf | discover-project, discover-file, locate-tests, conditional run-file, conditional record-file, conditional trace-entry-map | conditional | conditional | `src/ct_test/fixtures/m13_smart_contract_project` | `src/ct_test/m13_smart_contract_harnesses_test.nim` | yes |
| `smart-ton` | tolk | ton-tvm | discover-project, discover-file, locate-tests, conditional run-file, conditional record-file, conditional trace-entry-map | conditional | conditional | `src/ct_test/fixtures/m13_smart_contract_project` | `src/ct_test/m13_smart_contract_harnesses_test.nim` | yes |
| `smart-wasm` | wasm | wazero | discover-project, discover-file, locate-tests, conditional run-file, conditional record-file, conditional trace-entry-map | conditional | conditional | `src/ct_test/fixtures/m13_smart_contract_project` | `src/ct_test/m13_smart_contract_harnesses_test.nim` | yes |
| `smart-wasmi` | wasm | wasmi | discover-project, discover-file, locate-tests, conditional run-file, conditional record-file, conditional trace-entry-map | conditional | conditional | `src/ct_test/fixtures/m13_smart_contract_project` | `src/ct_test/m13_smart_contract_harnesses_test.nim` | yes |
| `v-fallback` | v | fixture fallback | discover-project, discover-file, locate-tests, run-project, run-file, record-file, trace-entry-map | yes | yes | `src/ct_test/fixtures/m12_v_project` | `src/ct_test/m12_fallback_languages_test.nim` | no |

---

### Dependabot Triage 2026 09 (`docs/dependabot-triage-2026-09.md`)

# Dependabot alert triage — September 2026

Triage of the 183 open Dependabot alerts on the default branch (`stable`).
Investigation only: no dependencies were upgraded and no lockfile was changed.

**Headline: 27 of 183 alerts (15%) describe code that actually runs in a shipped
artifact's browser bundle. None of the three criticals is among them. The single
most consequential finding is not on the list at all — see "Blind spots".**

## How the counts were derived

- Alert data: `gh api /repos/metacraft-labs/codetracer/dependabot/alerts --paginate
  --state open` → 183 records; severity split 3 critical / 84 high / 70 moderate /
  26 low, matching the push banner exactly.
- npm reachability: a dependency-graph walk of `node-packages/yarn.lock` from only
  those root packages **verified** to be `require`d/imported by non-test product
  source, then intersected with the versions actually resolved in the lock.
- Browser bundle: verified by fetching the deployed assets from
  `https://ide.codetracer.com/` and fingerprinting the minified bytes, not by
  reasoning from `package.json`.

## Reachability split

| Bucket | Alerts | Severity |
|---|---:|---|
| **A. In the deployed browser bundle** (runs in visitors' tabs) | **27** | 6 high, 17 med, 4 low |
| **B. Desktop main process / server mode** (node side, not the tab) | **20** | 9 high, 9 med, 2 low |
| **C. Shipped on disk but never loaded** (dead bytes in `node_modules`) | **95** | 3 crit, 54 high, 29 med, 9 low |
| **D. Storybook, dev-only, never shipped** | **14** | 9 high, 5 med |
| **E. Rust** (see below) | **27** | 6 high, 10 med, 11 low |
| Total | **183** | 3 crit, 84 high, 70 med, 26 low |

These reconcile exactly to the reported 3 / 84 / 70 / 26.

Bucket C is large because **nothing prunes dev dependencies**. All three release
paths copy the full 806 MB dev+prod `node_modules` tree verbatim
(`appimage-scripts/setup_node_deps.sh:20`, `non-nix-build/setup_node_deps.sh:48`,
`repro.nim:1341`). A grep for `--production`, `yarn workspaces focus`, `npm prune`,
`electron-builder`, or `asar` across the release scripts returns zero hits. So these
95 packages ship to users as bytes on disk that no code path ever loads.

### A. What is genuinely in the browser bundle

The deployed page loads `/public/dist/frontend_bundle.js` (13,103,803 bytes) plus
~247 lazy chunks — a webpack build of `src/frontend/frontend_imports.js` produced
from `node-packages/yarn.lock`. This is in addition to the compiled Nim `/ui.js`
and the wasm modules named in the deployment descriptor; the descriptor does **not**
enumerate it, which is why the bundle is easy to overlook.

Only three alerted packages survive into those bytes, and only at these versions:

| Package | Bundled version | Alerts matching that version | Arrives via |
|---|---|---:|---|
| `dompurify` | 3.1.7 **and** 3.2.7 | 20 (16 med, 4 low) | `monaco-editor@0.54.0`; `@codingame/monaco-vscode-api@22.1.4` |
| `brace-expansion` | 2.0.2 | 4 (3 high, 1 med) | `minimatch@5.1.6` |
| `minimatch` | 5.1.6 | 3 (high) | `monaco-languageclient → vscode-languageclient@9.0.1` |

The other 12 `minimatch`/`brace-expansion` alerts fire on the 1.x/3.x/9.x/10.x
copies, which exist only in eslint and native-rebuild tooling and are not bundled.

**Zero critical and zero high-severity dompurify alerts.** The 20 dompurify issues
are sanitizer bypasses (XSS). Their exploitation path here is narrow but real for a
hosted multi-tenant IDE: DOMPurify sanitizes markdown in Monaco hover/completion
docs, which originate from the language server describing the source file the user
opened. Reaching it means getting a user to open a hostile project in the web IDE.

The `minimatch`/`brace-expansion` issues are all ReDoS / unbounded-expansion DoS,
triggered by attacker-controlled *glob patterns*. The patterns here come from LSP
file-watcher registrations, not from user content — plausibly unreachable, though we
did not prove it.

### B. Desktop main process (different threat model)

20 alerts in `express`(+`qs`, `body-parser`, `path-to-regexp`), `socket.io`
(+`engine.io`, `socket.io-parser`), `ws`, `js-yaml`, `lodash`. These run in the
Electron main process on the user's own machine — `src/frontend/index/server_config.nim:65,106`
binds a local express + socket.io server, and `src/lsp/bridge_reduced.nim:55` runs a
localhost WebSocket server for the language server. Mostly DoS-class issues against
a localhost listener, so an attacker needs local code execution first.

`js-yaml` deserves a note: it sits in `devDependencies` but is `require`d at runtime
by `src/frontend/lib/misc_lib.nim:41`, so Dependabot labels its 4 alerts
"development" and they would be wrongly deprioritized. It parses only the app's own
`.config.yaml` and a bundled `data.yaml`, so the quadratic-CPU issues are not
attacker-reachable.

## The three criticals, individually

**All three are in bucket C — present in the shipped tree, never loaded.**

**1. `tar` 7.5.2 — GHSA-23hp-3jrh-7fpw / CVE-2026-59873, fix 7.5.19**
Decompression/parse DoS via unlimited input. Declared as a *direct* dependency in
`node-packages/package.json`, which is why it reads as runtime scope. But
`require('tar')` appears **nowhere in the repository** outside `node_modules` — it is
an unused direct dependency. The other two copies (6.2.1, 7.4.3) sit under
`node-gyp`, `cacache`, and `electron-rebuild`, i.e. native-addon rebuild tooling that
runs at build time. Verified absent from the deployed browser bundle.
*Verified* it is not imported; *inferred* (safely) that the parse path is therefore
never entered.

**2. `shell-quote` 1.8.3 — GHSA-w7jw-789q-3m8p / CVE-2026-9277, fix 1.8.4**
`quote()` does not escape newlines in object `.op` values. Single path into the tree:
`npm-run-all`, a declared devDependency used to run package.json scripts. Its
`quote()` calls operate on script names written in our own `package.json`, not on
attacker input, and it never executes inside a shipped artifact.

**3. `basic-ftp` 5.0.5 — GHSA-5rq4-664w-9x2c / CVE-2026-27699, fix 5.2.0**
Path traversal in `downloadToDir()`. Path into the tree:
`wdio-electron-service → puppeteer-core → @puppeteer/browsers → proxy-agent →
pac-proxy-agent → get-uri → basic-ftp`. Pure browser-driver test tooling. Nothing in
CodeTracer speaks FTP, and `downloadToDir()` has no caller in the product.

## Ecosystem breakdown

| Ecosystem | Alerts | Notes |
|---|---:|---|
| npm | 156 (85%) | 142 in `node-packages/yarn.lock`, 14 in `storybook/package-lock.json` |
| Rust (Cargo) | 27 (15%) | across 4 lockfiles |
| Nix | 0 | **not a supported Dependabot ecosystem** — see Blind spots |
| GitHub Actions | 0 | `.github/dependabot.yml` configures version updates only |

The "183 with 84 high" shape is confirmed as npm transitive-dependency volume: all
183 alerts land in just **6 manifests**, and 37 distinct npm packages account for the
156 npm alerts. Of those 37, only **3** (`ws`, `js-yaml`, `vite`) are referenced by an
actual import statement in product source — the rest are purely transitive.

Encouragingly, `src/db-backend/Cargo.lock` — the main shipped Rust backend — and all
of `libs/*` have **zero** alerts.

### Rust detail (27)

- `test-programs/rs_stylus_vesting/Cargo.lock` (5) — **orphan lockfile**. The
  directory contains only a `Cargo.lock`; no `Cargo.toml`, no sources. Repo-wide
  grep for the name returns zero references. It was left behind when the program was
  moved. `git rm` closes 5 alerts at zero risk.
- `test-programs/stylus_fund_tracker/Cargo.lock` (19) — an Arbitrum Stylus sample
  contract used as a *recording target* for tests. Every consumer is `#[ignore]`d or
  skipped; no build, packaging, or CI step compiles it. All 19 vulnerable crates
  (`openssl`, `rustls-webpki`, `jsonwebtoken`, …) reach the lock only through
  `ethers 2.0.14`, a `[dev-dependencies]` entry, and cannot appear in the wasm
  `cdylib`. Note Dependabot marks all Cargo entries `runtime` — `Cargo.lock` carries
  no dev/normal distinction, so that field is meaningless for Rust.
- `src/tui/Cargo.lock` (2) — `src/tui` **ships nowhere**: absent from `nix/`,
  `flake.nix`, the justfile, and every install script; its Tupfile rule is commented
  out. It is only compiled by CI lint. Alerts are `bytes` (unreachable; `ratatui` never
  calls the affected API) and `lru` (unreachable; blocked behind a `ratatui` major).
- `src/backend-manager/Cargo.lock` (1) — this **is** shipped (`nix/packages/default.nix:544,983`).
  One `bytes` integer-overflow alert, transitive under `tokio`/`http`/`tungstenite`,
  none of which call the affected `BytesMut::reserve` path.

## Upgrade cost

| Fix | Cost | Verdict |
|---|---|---|
| `brace-expansion` 2.0.2 → 2.1.4 | Lockfile-only; `minimatch` declares `^2.0.1` | **Cheap — do it** |
| `minimatch` 5.1.6 → 5.1.8 | Lockfile-only; `vscode-languageclient` declares `^5.1.0` | **Cheap — do it** |
| `bytes` → 1.12.1 (×2 Rust locks) | `cargo update -p bytes --precise`; no manifest edit | **Cheap — do it** |
| Delete `rs_stylus_vesting/Cargo.lock` | `git rm` | **Free — do it** |
| `dompurify` 3.1.7/3.2.7 → 3.4.13 | **Breaking, multi-package** | **Schedule, don't rush** |
| `lru` (Rust) | Requires `ratatui` 0.29 → 0.30 major migration | **Don't** |

The dompurify entry is the important one, and it is a trap:
**bumping the `dompurify` entry in `yarn.lock` would not fix the shipped code.**
`monaco-editor@0.54.0` *vendors* DOMPurify 3.1.7 inline at
`esm/vs/base/browser/dompurify/dompurify.js` (verified via its license header) and
imports it by relative path. Only a `monaco-editor` upgrade replaces it — and the
second copy (3.2.7) needs `@codingame/monaco-vscode-api`, which `monaco-languageclient@10.2.0`
pins. So the real fix is a coordinated three-package upgrade of the editor core, with
regression risk concentrated in the renderer. Given the deploy pipeline is green
after a large landing, and given all 20 dompurify alerts are medium/low, this should
be a deliberately scheduled piece of work, not a reflex bump.

## Blind spots (worth more than most of the alert list)

1. **Electron is invisible to Dependabot.** There are zero `electron@` entries in
   `node-packages/yarn.lock` and no `electron` entry in `package.json`. The Nix path
   uses `pkgs.electron` (pinned through `flake.lock`), but
   `appimage-scripts/install_electron.sh:9` runs a bare, unpinned
   `npm install electron` at build time. The largest attack surface in the desktop
   product — Chromium — is covered by none of the 183 alerts and, on the AppImage
   path, floats to whatever npm serves on build day. Pinning it would both fix that
   and bring it into Dependabot's view.
2. **Nix inputs are unscanned.** `flake.nix` declares 32 inputs pinned by
   `flake.lock`, and the deploy lane now reads sibling revisions from it. Dependabot
   has no Nix ecosystem support, so none of this is covered by any alert.
3. **Alerts are computed on `stable`, but development happens on `dev`/`cloud`.**
   The npm lockfiles are byte-identical across `stable` and `dev`, so the 156 npm
   alerts apply equally; the two small Rust locks differ slightly.

## Recommendation

**Most of these warrant no action.** 136 of 183 alerts (74%) describe code that is
either never loaded (95), dev-only Storybook tooling (14), unshipped Rust test
fixtures and an orphan lockfile (24), or unreachable Rust APIs (3). Dismissing them
with `vulnerable_code_not_actually_used` — rather than upgrading — is the correct
disposition, and would take the queue from 183 to 47, of which 27 are the browser
bundle and 20 are localhost-facing desktop code.

Concretely, in priority order:

1. **Do now (free, no deploy risk):** delete the orphan `rs_stylus_vesting/Cargo.lock`;
   bump `bytes` in the two Rust locks; bump `brace-expansion` → 2.1.4 and
   `minimatch` → 5.1.8, which are pure lockfile moves inside existing semver ranges
   and clear 7 of the 27 browser-bundle alerts.
2. **Do now (paperwork):** bulk-dismiss buckets C, D, and the 24 Rust
   fixture/orphan alerts with reasons. This is where the 183 → 34 reduction comes from.
3. **Schedule deliberately:** the monaco-editor / monaco-vscode-api / monaco-languageclient
   upgrade that actually fixes the 20 dompurify alerts. Do not attempt this while the
   lane is green after a large landing; it is the one change here that could redden it.
4. **Fix the generators, not the symptoms.** Two structural changes would collapse
   most future alert volume: prune dev dependencies before packaging (also cuts a
   806 MB tree out of the shipped artifacts), and correct the `dependencies` /
   `devDependencies` split in `node-packages/package.json` — `@playwright/test`,
   `selenium-webdriver`, `browser-sync`, `node-gyp`, and `node-abi` are test/build
   tooling filed as runtime, while `js-yaml` is runtime filed as dev. Pinning
   `electron` into the lockfile belongs in the same pass.

---

### Dispatcher Docs (`docs/dispatcher_docs.md`)

-> dispatcher:
  loadProcessArgs() # processes args
  startIPC()        # start and setup loop for incoming messages(from client)
  ->
    createThread(thread, sendClientThread) # start a thread for sending messages/events to client
    readTaskLoop.loop(): # the loop
    -> # in core_common.nim
      # process incoming messages
      self.handleMessage(taskKind, taskId, eventualIntArg) # handle them
      -> # again in dispatcher.nim, calling its handleMessage
        # processing handler: some of:
        * configure(readArg[ConfigureArg](taskId)) # configure
        * fullResetOperation(taskId, resetOperationArg.resetLastLocation) # full cancel/reset
        * other cases or starting in thread(event load)
        elif for a task_process:
          run a before hook in dispatcher if such exists
          if we're jumping: dropping the task(probably ..wrong? if a new jump? otherwise ok)
          eventual cache check, but almost non used for now

          if for this task we can't interrupt: (usually a step-like move or loading info in stable):
            get the process and register the message for it
          else: (usually jump or additional info like flow/call-args):
            get an available worker
            find out if there is process for an outdated current task
            if there it is: cancel it
            register the message for the new task in the available worker
          (TODO) if there are problems with this or process cant be found:
            if it's a non-critical thing(info/non-critical action):
              log/report an error
            else: (e.g. moving):
              this shouldn't really happen normally as we should be able to
              start a new process in the worst case
              however file limits/more radical situations might lead to this:
                log/report a more critical error; eventual suggestion?


cancel:
  eventually: try to interrupt and do the replacing process
    only if it not working, but for now always replace
  if stable:
    equivalent to fullResetOperation
  else:
    stop existing worker

get an available worker:
  if stable:
    if interrupt:
      if free: stable; if not set cancelled wait for 100 ms: if free: stable;
      if not: StepBehindTracking worker if available
      otherwise: free/new
    else:
      just stable: assume it will queue either with registerMessage or socket
  elif worker, but to stable location:
    CloseTracking worker if available otherwise free/new
  else: # e.g. tracepoint
    a free/new non-tracking worker

fullResetOperation:
  eventually: try to interrupt and do the replacing process if not working
  now: always stop existing stable process
  and replace with StepBehindTracking if available, free/new otherwise
  eventually start a new StepBehindTracking process for reserve

---

### Editor Test Recording (`docs/editor-test-recording.md`)

# Editor-Based Test Recording

CodeTracer can expose `ct test` actions next to tests in the editor when a
provider can map discovered tests back to source lines. The controls are built
from the same catalog and event stream used by `ct test discover`, `ct test run`,
and `ct test record`.

## Where Controls Appear

The default placement is the editor gutter. Runnable test lines show compact
actions for:

- `Run`: run the selected test through `ct test run`.
- `Record`: record the selected test through `ct test record`.
- `Open`: open the last trace recorded for that test, when one exists.
- Status: show the latest known state, such as `passed`, `failed`, or
  `recording`.

The prototype also supports `above-line`, `both`, and `disabled` placements.
M15 selects `gutter` as the default because it keeps source layout stable,
preserves scroll anchors, and leaves multi-line test bodies readable. Above-line
controls are useful when more status text is needed, but they consume vertical
space and can make dense test files harder to scan. The `both` placement is
best reserved for debugging the feature because it duplicates actions and adds
visual noise.

## Running And Recording

Use the editor controls on a discovered test line:

1. Run a test with `Run`.
2. Record a test with `Record`.
3. Open the latest recorded trace with `Open`.

Recording opens the created trace through the configured tab policy. The current
GUI contract supports opening in the current tab or a new tab, and carries the
test id, trace path, trace id, recording id, and requested tab policy to the
trace-open layer.

Output from the test command is attached to the test state and diagnostics are
shown when discovery, running, or recording fails before a trace is created.
Failure diagnostics do not open an empty trace.

## Keyboard And Accessibility

Each editor action has a stable command name:

- `ct.test.run`
- `ct.test.record`
- `ct.test.openLastTrace`
- `ct.test.status`

Rendered controls expose an accessibility label and title for every action.
The status action is informational and disabled; run, record, and open-last-trace
are command actions when available.

## Toolchains And Recorder Configuration

Editor recording depends on the same command-line setup as `ct test`:

- The `ct` command must be available from the CodeTracer build or installed
  package.
- Language providers must be able to discover tests for the open file.
- Recorder binaries and language toolchains must be available on `PATH` or
  configured through the usual CodeTracer environment variables.
- In this workspace, the Nix development shell and `.envrc` auto-detect common
  sibling recorder repositories. Run `scripts/build-siblings.sh --check` to see
  which sibling artifacts are missing, and `just build-siblings` to build them
  through each sibling repo's `direnv exec` environment. For native Rust
  recording, ensure the native recorder from `codetracer-native-recorder` is
  built. For Python, ensure the Python recorder and the project test runner,
  such as `pytest`, are installed.

## Current Limitations

- Controls appear only for tests that providers can map to source lines.
- Some frameworks support file-level recording but not reliable single-test
  recording; those providers should report a diagnostic instead of exposing an
  unavailable action.
- Full Electron GUI coverage should run through `just test-gui`, which builds
  the frontend, builds sibling recorder artifacts, and starts Xvfb for
  Playwright/Electron on Linux. The M15 headless acceptance tests exercise the
  ViewModel and rendered control contract when the full GUI harness cannot be
  run in the current environment.

---

### Preserved Branch Triage 2026 09 (`docs/preserved-branch-triage-2026-09.md`)

# Preserved-branch triage — 2026-09-02

Six branches were pushed to `origin` purely for preservation during the
browser/cloud campaign and were *believed* to be duplicate landing attempts.
This note records what each one actually contains, established **by content**
— `git cherry` patch-ids plus blob and tree comparison — not by ancestry.

It exists so that a future audit reads a verdict instead of re-doing the
investigation. **Ancestry is not the same as landed:** much of this campaign's
work was rebased or cherry-picked onto a mainline under new SHAs, so a commit
can look unmerged while its content is fully present. Every SHA below was read
from `git rev-parse`.

**None of these branches has been deleted.** The cost of keeping a branch is
zero; the cost of a wrong "safe to delete" is unrecoverable. They are recorded
as *answered*, not as *removed*.

## Superseded — content is on `cloud`, deletion would lose nothing

| Branch | Tip | Verdict |
|---|---|---|
| `land/edit-mode-toolbar` | `365bdcaa57ff417d2f108ba19cdb2633c4c799f7` | superseded |
| `integrate/dev-toolbar` | `9ad0e6116e5aee4c831f188d600ae800ba88d3a5` | superseded |
| `land/step21-required` | `670312f85102e690f9c05667844ea85b5cf464f0` | superseded |
| `fix/asset-cache-immutability` | `60349fe7cbf3e6085dcd16cb5404b6028b3d3250` | superseded |

Evidence, per branch:

- **`land/edit-mode-toolbar`** — `git cherry origin/cloud` reports **both**
  commits (`11d4edef730c1e164e93893279506f8ecdc8663e` and `365bdcaa`) with `-`,
  i.e. already upstream by patch-id; seven of its eleven files are additionally
  byte-identical to `cloud`, and the four that differ are files `cloud` has
  evolved further. The spec half of the same work landed in `codetracer-specs`
  as `8153b9589049c0f160935e804331dc3d3b053277` (branch `latest`).

- **`integrate/dev-toolbar`** — its merge commit `9ad0e611` has an **empty
  combined diff**: the merge introduced nothing that its parents did not
  already carry. It is a landing attempt, not a source of content.

- **`land/step21-required`** — a single `wip:` commit touching only
  `.github/workflows/deploy-web-codetracer.yml`. **All 165 lines it adds are
  present on `cloud`**, checked line by line, including its two distinctive
  markers `EVERY REACHABLE HOST IS REQUIRED` and `OPTIONAL IS A PROPERTY OF
  RESOLUTION, NOT OF IDENTITY`. The file is *not* byte-identical to `cloud`'s
  version — `cloud` has since evolved it further — which is exactly why the
  test is line containment and not blob equality.

- **`fix/asset-cache-immutability`** — `60349fe7`'s patch body is **identical**
  to `8fe3fd631122174523120cc2ffe07522b6e9293c`, which is on `cloud`
  (`fix(ci): verify every host the deployment declares, and prove the set is
  not empty`). Note for accuracy: `60349fe7` is **not** an ancestor of
  `ab4511b496fabbd147e363fdc1dc1f0b92b91d0f` (the content-addressed-assets
  commit landed on 2026-09-02) — the two are independent commits over the same
  files. The content is preserved because `8fe3fd63` landed it, not because
  `ab4511b4` contains it.

## Not duplicates — unique work, now partly landed

| Branch | Tip | Verdict |
|---|---|---|
| `fix/sibling-pin-assert-cloud` | `b515e725eb173fe70e5d3757a67518517f3ac837` | **LANDED on `cloud`** |
| `fix/sibling-pin-assert` | `9f16671142f4cdae1ec4c7b1a5ac9d2f9e13e47e` | **KEEP — `dev` is still unprotected** |

These two were the sole carriers of ~826 lines that existed on no other ref:
`scripts/sibling-pins.sh` and `ci/test/sibling-pins-test.sh`. They are
near-identical twins differing only in workflow wiring; the `-cloud` variant
preserves cloud's isonim pins.

`fix/sibling-pin-assert-cloud` was landed on `cloud` on 2026-09-02. Its
contract suite reports **20 passed, 0 failed**, including eight mutation arms
(a sibling moved off its pin, an absent sibling, a dirty sibling, a non-git
sibling, an emptied sibling list, an unparseable `flake.lock`, a non-SHA
`locked.rev`, a sibling dropped from `flake.nix`).

After that landing, `scripts/sibling-pins.sh` on `cloud` is a strict superset
of the twin's version — a line-level diff shows **0 lines present only in the
twin** — and `ci/test/sibling-pins-test.sh` is byte-identical between them.

**`fix/sibling-pin-assert` is no longer the sole carrier for `dev`.** This
section previously said `origin/dev` did not carry either file. It does, and
did when that sentence was written: `git ls-tree -r origin/dev` lists both
`scripts/sibling-pins.sh` and `ci/test/sibling-pins-test.sh`, and both are
byte-identical to `cloud`'s copies. `origin/main` genuinely carries neither.

So the guard is on `dev` and the defect is closed there; what is still open is
`main`. Keep or delete the branch on that basis, not on the one this paragraph
used to give.

## Why this mattered

Every incident this guard addresses is a build sibling resolved by *tip*
instead of by *commit*:

- a build script silently resolved the Embed SDK from a sibling checkout when
  an env var was unset, so three builds of identical source came out at
  1,434,511 / 1,350,330 / 1,377,289 bytes, and journey verdicts were not
  reproducible between agents;
- pinning one source was not enough — two builds sharing an SDK revision and a
  byte-identical `src/frontend` still differed by 55,850 bytes, because two
  further siblings were on the Nim path;
- an unpinned replay engine produced a phantom origin-classification
  regression that consumed three agents and two confidently wrong diagnoses
  before the engine was suspected.

## First drift verdict, 2026-09-02: the PINS drifted, not the checkouts

The moment the guard was landed it went red on this workstation, for all
three siblings. "Stale pin" and "drifted checkout" are **opposite fixes**, so
this was established from ancestry rather than assumed:

| Sibling | Pin | Relation to the pin | Verdict |
|---|---|---|---|
| `codetracer-native-recorder` | `7e0400b3` (2026-07-06) | pin is an **ancestor** of local HEAD; pin is **1151 commits** behind the sibling's `origin/dev`; both are on `dev` | **pin stale** |
| `codetracer-trace-format` | `392c5559` | pin is **not on `origin/dev` at all** — reachable only from `origin/ci/ref-rot-gate` and `origin/fix/ci-action-refs`, two unmerged feature branches | **pin off-branch** |
| `codetracer-trace-format-nim` | `d7eca441` | pin on `origin/dev`, 7 commits behind | **pin stale** |

`flake.nix` declares all three as `.../dev`. For `codetracer-trace-format`
the lock therefore **contradicts its own declared branch** — that one is a
correctness defect, not staleness.

**Aligning the checkouts to the pins would have been the inverted fix.** It
would have moved the recorder back 1151 commits to July code and parked
`trace-format` on an unmerged feature branch — the exact "pin a workspace to
superseded code" error.

**CI was not red on this.** The deploy lane clones the siblings *at* the pins
and then verifies *at* the pins, so it is self-consistent and passes. The red
is a statement about this workstation's checkouts — i.e. that local builds do
not match CI — which is the reproducibility gap, not an outage.

The bump is prepared on branch **`fix/sibling-pins-bump-2026-09-02`** and is
deliberately **not** on a mainline: it moves the recorder by 1,459 files and
+364,380/−22,976 lines, and nothing was built, because this workspace cannot
build codetracer. The flake re-locks and evaluates and the contract suite is
20/0, but evaluation is not a build. It needs a build window and someone who
can run one. The `trace-format` correction has an independent argument and
does not have to wait for the recorder bump.

Also observed and **not** touched: the local `codetracer-trace-format` checkout
is parked on `feat/managed-upload-derivation` with 3 uncommitted files. That is
someone's live work; a shared checkout is not a thing to reset underneath them.

## Known gap: the toolchain is not pinned

`scripts/sibling-pins.sh` pins three **repositories**. It does not pin the
compilers that read them, and it now says so on every pass. There is a live,
unclosed hypothesis that the Nim compiler is a fourth unpinned input: two
builds that both claimed all three sources at their pins differed by 60,920
bytes, and the two environments were observed running different Nim versions
(`Nim 2.2.4` from `~/.nix-profile/bin/nim` against `nim-unwrapped-2.2.10`).
Pinning the toolchain is separate, unstarted work.

## Relationship to `client/hydrate/build.sh` (blocktracer)

The two mechanisms **compose; they do not duplicate.** They live in different
repositories and pin disjoint sets from different sources:

| | repo | pins | source of truth |
|---|---|---|---|
| `client/hydrate/build.sh` | blocktracer | `CODETRACER_REF`, `ISONIM_REF`, `NIM_EVERYWHERE_REF` | `ci/embed-sdk-pin.env` |
| `scripts/sibling-pins.sh` | codetracer | `codetracer-native-recorder`, `codetracer-trace-format`, `codetracer-trace-format-nim` | `flake.lock` |

blocktracer pins *codetracer* at a commit; codetracer's own guard then makes
that commit determine its three build siblings. Together the chain is fully
determined at the repository level. There is no overlap in the pinned sets, so
neither check makes the other redundant, and removing either reopens one link.

---

### Projects (`docs/projects.md`)

```bash
projects clone curl #
  # clones with git into `projects/curl` from the address we have, if not already there
  # the address might not be upstream, if we have a branch or want a certain commit
projects build #
  # builds the project where we are , based on the name of the folder(maybe the projects path as well)
  # if not in codetracer shell, probably runs projects build_in_shell in codetracer shell
  # otherwise directly runs projects build_in_shell
  # which is an internal command
projects setup curl [--build / --no-build] #
  # clones a project if needed, if no specific build flag passes
  # asks if we want to build
  # otherwise builds if `--build` passed and doesn't build if
  # `--no-build` passed
  # and runs `codetracer shell` leaving the user inside with
  # some examples printed
```

```bash
$ projects setup curl
> * cloning curl into projects/curl
> * moving there
> do you want to build it? Y/n ?
Y
> answered Y: building
> * starting codetracer shell
> * building curl: running commands
> ..
> * build finished: built bin/curl with codetracer build info
>
> now you can work with the project:
> example run commands:
> `bin/curl` # just running curl
> `bin/curl -L -X GET https://bible.com` # downloading a page, following redirects
```

---

### Research (`docs/research.md`)

I spent some time with the Nim compiler today, as I want to improve the rr logic for bigger apps like it this week.

Various observations:

* I often want to trace just a certain period of the program, e.g.
I know that it behaves ok in the first 90% of invocations, and it breaks just in a certain moment.
Here is probably where some kind of condition-based tracing/searching might help.
A progress bar on which you can jump to %-event of the program also seems like a possible solution, but it's more of a gimmick, and I am not totally sure how useful would it be in general
* Another thing that would be interesting is some kind of info on how hot some method/path is. That can give you another view into which are the important parts of the file (or dead code) but I think it's still soon for profiling features (talking about another view of the file, I also had some ideas how with dynamic call info, you can visualize/animate right of a method the methods it called and the args it passed them, but that's again more of a questionary? visualization experiment)
* Probably I can get the enum variants using the future nim syntax plugin, but no obvious way now
* Overall and most importantly, still some work on more robust handling of instances/variants to be able to use most Nim compiler values, still probably most of the complicated ones are broken , but shouldn't be too much fixing



While trying example simpler (not compilers) programs these days, I another possible operation. Following the construction of a value seems like something that could be an useful extension of the history search (and something I haven't seen before)

An example is:

```nim
proc x(z: string): G =
  var filename = z & ";"
  filename[2] = ' '
  var g = G(filename: filename)
  return g

proc a =
  var filename = "w"
  for w in 0..<4:
    echo x(filename & $w)
    filename = filename & $w
    echo x2(filename)
```

and that would be the result

```nim
							! filename: "z4;" =>
  var filename = z & ";"  	! z = "z4"
  proc x(z: string): G      ! z is arg coming from
  x(filename & $w)          ! filename = "z" w = 4
  var filename = "z"        ! filename = "z"
```

The point is that we follow not only to the allocation of a variable, but also detect how it's constructed and we also follow the history of those values recursively to get a full picture of how
was our value constructed(because that might map to something a human would do manually).
I toyed with recognizing common patterns (e.g. arg passing), I admit analyzing this seem harder and
full of edge cases, but a not-always-perfect version of it seems simple: not tough to recognize calls/variables on right of assignment or in a call and to re-apply recursively the same actions on them
As a whole that's a dubious and indefinite future idea of course, but I'll document here just in case.

---

### Search (`docs/search.md`)

search
==========

An idea for a possible feature:

Search the events and data of a trace using a search box

Have two components in the box: a kind of search and a query

Functionality:

Search, filter, map, work with results or run a command
get a single result, or a list of results, or run a command.

Run a command with arguments or apply operations functionally one after each other until a result is a produced

Data:

Search in recorded events, calls and using debugging scripting: e.g.
running queries using the python or native rr lldb/gdb engines.
also run a program command as a shortcut

* Events
* Runtime values
* Calls, calltrace
* Errors
* Memory


Syntax:

Use a simple syntax with names and parens, maybe `.` or `|` for piping / operations

`call`

`call(arg, arg2)`

`call(arg, arg2) | operation`

`call.field`

`call[subrange:subrange_end]`

Implementation :

Parse to nodes.
Based on kind, run a command or search for results
start with first subexpression


1) For each subexpression find a source based on call name or argument/arguments


* Existing data: e.g. events, paths, calls
* Search through memory/debug info using scripting/stepping
* Result of previous sub-expression
* Online result
* Some kind of file / json data
* Other

2) Run the expression in the source:

3) Get result/results asynchronously and pass them to next step

4) Run next subexpressions if we have some, show some progress

5) If at the end, show in search results (or run a command if this is possible)

Notes:

If a new search is performed, or the current one is cancelled, cancel the existing search

Cache some of the results or info found that can be relevant: for current session, eventually for new sessions

Sources:

1) Existing data: managed by backend, but it's possible to have an optimization which uses data cached in frontend: mostly filter/search there

2) Search through memory/debug info: managed by the backend and probably python/native modules in a separate process

3) Result of previous sub-expression: should stay on backend for now, note this can be cached, so we can easily change the end of expression

4) Online result: TODO maybe load from some kind of existing storage if needed

5) Some kind of file/json data: TODO we can read a file with addresses or names needed for search

Operations:

1) Depending on source and data

2) `map`: just mapping a field or sub range

3) Access to field

4) Access to sub range

5) Filtering based on a condition

6) Grouping based on a function

7) Other operations: TODO

8) Graph operations

We can show results several ways

Displayers

1) HTML results: show normal results maybe with some links as an element/list

2) Graphics/plots: show based on some calls similar to R/chart.js using chart.js in a graphics box/boxes in the search results

3) Custom display: maybe we can have a javascript plugin option where we can show custom HTML/displays (e.g. a custom graph lib display or animation)

End of implementation section

Examples:

`events(WRITE)`

```
a     (onclick jumps there)
b     (onclick jumps there)
```

TODO more

Design:

Reuse ctrlp/menu design for now and graph/chart design for charts.
Make a nice user input interface with color/progress coloring: however those are nice to have-s, not critical for now

Notes:

That's a big feature: it can be very useful, as almost a central way to analyze a program/trace, but also very complicated and time-consuming. I'd propose to write a small part of the functionality/sources code to showcase how useful it can be/to have a working example : as part of the work on normal ctrlp/search functionality (which needs some of this already).
this can be done in limited amount of hours reserved for more experimental/rest from normal tasks work, or out of my work hours possibly.

However, the focus is on documenting the idea and discussing it: it's clear that it's just one possible direction of codetracer

@events |? WRITE

-> Filter(Name("events"), FilterArg::NameMatch("WRITE"))
-> events returns an events object it implements filter_by_name_match which returns needed

value == 5

-> Cmp(Op::Eq, Name("value"), Int(5))

-> values source: implements find_by_cmp(op, expr, other_expr)
-> return stream/list of results: objects which can be chained, but if evaluated to final results:
  text content, some kind of mark/kind, location for onjump/short location or line snippet

value > 5 # same

value | line-chart

draw line chart

@calls |? parse_* |? .depth > 4

@parse_* & depth > 4 & value > 5

@calls | parse_* | .depth > 4 | value > 5

At: -> CallFilter
. Cmp(Field("depth"), 4))
. Cmp(Expr("value"), int)
-> Pipe ->

search through

Calls -> parse iterator
combined with depth filter
final one runs after

so -> running combine CallSource filter_multiple: on each checks both
and in the en

CallSource, vec of parts and ValueSource with final part

In the end for each of those also check final: ValueSource in those places filter_cmp -> produce results send back show

For simplest for now


Query parsing: maybe backend <-> frontend for highlighting/completion: yes because completion easier from backend!
so send query to backend!
parse ->


frontend -program-query-> backend
backend:
  parse -> ProgramQueryPipeline(Vec<PipelineStep>) or error(send to frontend)
  PipelineStep -> {Source, ProgramSubQuery}
  ProgramSubQuery -> Filter/Calculation/Command/displayer or other

  ->
  combine several filters for the same sources ? maybe later postprocessing

  Source : run filter for that kind of query
  it can be a kind of iterator if not the final one!
  for the final one actually collect results and send back!
  (or send error!)
frontend:
  with results: show them similar to files for now
  or similar to commands
  or for error: just show the error: notification or as a single result?

first:
  sending query
  normal is just normal
  ! is for commands
  ? for search

  parsing, sending results

  if hard, minimal interface


terminal:
  show if under some limit
  if over, for now only show up to it (or eventually by scrolling?)

---

### Simpler Search (`docs/simpler_search.md`)

view: pattern

autocomplete view / br

view

autocomplete function names TODO path:line and path for path:line

view! -> results in a special panel

navigate in results

ctrlp: `open`

open

autocomplete for paths

`open!` -> results in a special panel

Autocomplete defined by command,
some commands don't have autocomplete
e.g. for now `rg` etc.
In some autocomplete ~= results
e.g. `view` and functions.
In others it can be different, e.g.
`c-location <pattern>` for functions.
There whenever we enter/choose autocomplete, we get
results in different style,
but on input/change, we get into autocomplete again/
If one wants to see both, he can use `c-location!`
now.
We might want to have panels which maintain their own queries
(or just keep them open).
One possibility is to *keep* them.
A button/shortcut which makes them a tab that is not replaced
in special panel and let them show their query
I prefer a single input box though.
Maybe we can have a shortcut that re-types their query in the input box and updates the same panel with results

Now, functions need to be preloaded and prepared for quicker filtering.
Maybe in core, not always in python.
Even if based on debug info,
for faster search and autocomplete,
we *can* also just load them in frontend, they shouldn't be too many
similarly to paths
for fuzzy search, but then plugins need to have
optional renderer logic, which might be ok

```
command.rendererAutocomplete = proc(data: Data2, query: cstring): Future[seq[SearchResult]] {.async.} =
  return await fuzzysort.goAsync(
    query,
    data.services.search.functionsPrepared,
    FUZZY_OPTIONS)
```



Save last query when closing/opening query


For ideas document, not for now:

* History/REPL
* A bit more advanced language
* More separate parts: source/displayers to be able to have more involved searching mechanisms

---

### Small Notes (`docs/small_notes.md`)

* Format:
  * Overall types: JSON/binary
    * Maybe add instance, eventually enum? Something else important?
  * Format: keep JSON? Or probably json lines for streaming/not whole file?
    Or `capnp`/`flatbuffers`/other?
  * db-backend adaptation if needed, or giving our code as an example?


---------------

db-backend/rr gdb backend:
  graph/explain; maybe plan; eventually resolve in later days/weeks?;

Others:
  Planning/documenting: talk with Stan? Maybe event flow/step view/others?
  Tooling: editors/others
  Testing?
  Peter: CI what to do?
  Cooperation with Nedy/others; Nikola: needed/also ct-web needs/db-backend??


----

Small regression for filesystem: imported traces(ctrlp working)

Tooling: editors/monitor etc
  ok, nix setup; update; lsp
  todo helix? others?

Plan value support?
  Simple events for value register/modify
  Add value id as an node in the graph;
  and events as edges;
  value_node:
    value_id or
    literal
  full_value register specifically

  atom!
  !
  atoms and compound

  register_cell(value_id, atom_value);
  register_compound(value_id, eventually_kind: seq/object/other? or just type_id, type_id);
  register_modify(value_id, coord, cell_value_id);
  // eventually coord / modify_info can include push/pop/insert etc?
  register_variable(variable_id, value_id);
  // reassign => new register_variable or assign?
  if value starts pointing somehow to new value? maybe ref?
  register_ref(value_id, target_value_id);
  register_ref for reassign to other?

  eventually we can track reads/usages
  register_access(value_id);

  modify_info:
    index coord index: Int; value_node: ValueNode
    field coord name: String;
    push index: Int; just in case
    pop index: Int; just in case
    clear
    eventually: others?

  or register_index_assign(value_id, index, index_node, sub_value_node);
  register_field_assign(value_id, name, sub_value_node);
  register_push(value_id, sub_value);
  register_pop(value_id);
  register_clear(value_id);

  register
  // nope
  register(value_id, full_value);
  assign(value_id, value_node);
  remove(value_id); // maybe connect to variables? as well
  variable(variable_id, value_id);

  #id @name

  register can generate many edges in one
  maybe still better than producing many mini-events for now
  or we can produce them internally from register
  register_compound_value(#value_id, value)

  register_compound_value(#0, [0 1 2 3 4]) -> #5 -> #0 -> 0; #1 -> 1; etc


  register_cell(#1 @list[0], 0)..
  register_compound(#0 @list, type_id);
  init_compound(#0 @list, [#1, #2..]);

  or just
  register_compound(values); and produce the init/edges at once in the backend

  variable("list", #0 @list)
  register_atom_value(#6 @limit, 10_000)
  variable("limit", #6 @limit)

  register_atom_value(#7 @i, 0)
  variable("i", #7 @i)

  assign_atom_value(#7 @i, 1)
  assign_atom_value(#7 @i, 2)

  etc

  # function: for now no copy annotation, just address
  variable("list", #0 @list)
  i..
  new-first..
  set -> assign_atom_value(#1 @list[0], 1)
  etc..

  list now : #0 -> #1
                -> #2 etc

  ready!

  modify(value_id, sub_value_coord, sub_value_node);
  sub_value_coord:
    index value_node(int); int: probably always can use int; but tracking value id useful for read-flow/usage references
    or
    field string name
    or slice?
    or address?
    or other TODO
  operations/call results for now maybe producing anon values with new value id;
  but eventually if we track them, can have more advanced history

Editors:
  VS-Code

  Helix/emacs/vim?

  zed/custom/codetracer/other??

---

### Spec (`docs/spec.md`)

## codetracer `<path>`
  e.g. for `ls`
  if trace is nil =>

    default build/(eventually auto-detected cfg/makefile etc) => record `<defaultpath>` => record ls.nim

    otherwise =>

      editing a path, record it => record `<currentpath>` => record ls.nim

      not editing => error "cant build, please update config, or edit a file"

    eventually: change with a menu/dialog

---

### System (`docs/system.md`)

## system

UI
==

UI is important.

We have most of our code in `ui/`.
`ui_js.nim` is the main file.
We define the root view.

Most of the components are defined in their own modules:

* `calltrace` the calltrace, expanding and collapse
* `chronology` the chronology and input
* `colors` some constants
* `debug` the debug toolbar
* `editor` the main editor, uses Monaco
* `events` support coloring the output code as in terminal
* `flow` the preload ui
* `history` the history ui which reuses the value ui
* `inline_editor` uses monaco, the editor open for traces
* `layout` the overall layout
* `loading` the loading screen
* `start` would be useful as default screen
* `state` the state view with changing variables
* `trace` the trace view: now we show traces inline, so maybe remove
* `ui_imports` exports most dependencies for ui
* `value` reused by views that show values or expand them

Important stuff:

* Use direct style helpers for generated DOM and stylus for css files
* You can watch debug output in the console
* Monaco, datatables and charts have a custom DOM integration
* Layouts can be loaded from files: look at `config.nim`, we have to fix this
* We have themes: defined by variables in name_theme.styl
* Redraw needs to be manual sometimes, especially after IPC listeners or direct DOM integrations
* A lot of the helper code is in `types.nim`(types) or `lib.nim` (signatures for third party or our helpers)
* We use `view` in the name of views to easily spot them, normal functions don't need it
* When we're changing css, it's best to change the current code: remove literals and define them in defaults and theme styles, we might get the designer to color pick better colors and new themes
* We use vex-js for dialogs
* Please check id-s and classes if you remove them: we might need them for ui testing
* test_base includes most of the logic for ui testing, we use spectron

Events

You define a callback in renderer and you update state.
Most of our state is in the `data` global. It can certainly be optimized.
We maintain our panel in `sys`.

When you need to talk with the server, you send a message using `ipc.send`.
We apply the "CODETRACER::" namespace. In a browser this is a socket message, in
Electron we send it to the main process.
The main process responds, and usually you register a listener with ipcConfigure.

You can also define an async function which makes a promise which is resolved after a listener is called.



Testing

==

We have to fix the tests.

However let's look at the three main sets:

```bash
example-based/  nim.cfg  programs/  quicktests/  README.md  reproductions/  run/  Tupfile
```

In `example-based` we have normal tests.

In `quicktests` we have property tests. Their results are sometimes added to `reproductions`.

In `run` we store the compiled tests.

In `programs` we have the example programs.

We also have tests for python commands: but I must find them, probably git.

This is an old note:

```
tests/quicktests/
  reproductions/
    quicktest_basename/
      repr_id.json

    failing tests that we eventually fixed
    random subset of succeeding tests

    make sure that stuff that worked before, still works
    and that stuff that is fixed is still fixed
```

Each test folder has lang tests. They are often similar, so I am not sure if we need to parametrize them.
We define most of the main logic in test_base.

We also have `nim-ui` in `example-based` and `quicktests`.
They are based on Spectron.

They use a dsl, so you don't need to always access elements manually.
If you need to, please write a helper which can be reused.

==

Debugger

==

We are working with rr here. RR provides most of our low level functionality.

We use gdb-js, a custom fork of the original repo with minor differences: several helpers.

Most of the code is in `debugger/`

The base types are either in `types` and `core` usually.

We have a `Pool` which is currently only one.
A `Pool` has a list of `Process` which are currently rr instances.
By default, we have a `stable` process which runs most of the usual steps.
A `trace` process which runs trace debug, a `history` process which runs history search.
A `preload` process which preloads future values and `args_preload` which preloads args.
We also have `prestart` process which starts in the beginning and loads chronology: mostly writes and reads for now.

Let's take a look:

* `args_preloader` defines `loadCallTraceArgs` which loads args
* `core` contains shared logic and types for the debugger
* `debugger` is the central module
* `flow_preloader` defines the preloading code
* `history` defines the history code
* `move` runs most of the jumps
* `process_gdb` is where most of our helpers and other shortcuts live
* `py_command` is a wrapper around our python commands giving a type safe API
* `scheduler` was an experiment in mirroring stable steps in preload
* `sync_preload` is used to sync the preload process to the stable process

We also have `cgdb` which should eventually deal with gdb as a pure nim solution and give as a way to rewrite debugger with the C backend.

Basically we define a pool in the beginning and the server sends initial data to it.
After that the server starts the pre-start process which loads info while running the program
and starts the other needed processes.

The stable process jumps to main and calls back the server so the ui can show the main program.

We have a listener for gdb events and use a system of JavaScript promises to await actions.
The problem is that an action usually ends before the info of the new location arrives, so we have our own
helpers in process_gdb which we should use.

The other bizarre part is the export api: it defines with a macro the debugger interface and generates export code for it. We expose a type to other modules, so they can call our functions in a type safe way.
The debugger module can be used directly (import) or in a different process. The second thing is usually better, as
it can crash or block the main process.

It communicates using helpers and ipc send. This is also complicated internally as on desktop it uses electron, and on server `node-ipc` mapping the calls (probably).

We have some helpers to report errors and to load stuff from the server.
A lot of the moves are basically taking arguments from the server , invoking typed functions which implement the logic
and call python commands as primitives. Some actions periodically update the server , e.g. preloading.

Args preloading is a bit hard to optimize for bigger programs, so we have to work a bit on it: it visits all the call children of the current call and logs the args.

Flow preloading can be slow: most of the logic is in Python commands. We try to limit the number of steps taken there
and repeatedly call it , sending updates to the server until we are stopped by a new preloading context or finished.

We cache a lot of stuff like preload results, AST etc. Some of the caches are on function-level, some on filename-level etc.

A lot of the logic is in Python commands: usually if something needs to use a series of gdb commands or to analyze in a more powerful way the program , we create a command, it's best if it is general enough to be reused in many actions.

Parsing the modules is good for preloading: we load the labels for each line.

Overall
==


Electron:

We have several components.

The backend, the archive runner, the archive server, the shell support, codetracer, plugins, the commands, language forks, the server, the renderer and the debugger.


Codetracer calls the server, a local database and Redis.

The server is the main Electron process which dispatches tasks and messages to the renderer and the debugger.

It also maintains a running plugin for the current language and communicates on stdout with json.
It communicates on the stdout with `nimsuggest` when in a Nim project too: we have to test if this is working now.

The plugins can parse expressions and modules: expressions are parsed as QueryNode which can be interpreted by our debugger, the modules as a language-unified AST.

The QueryNode-s are interpreted with the class Loader. We visit the nodes and map each one to a gdb expression, which most of the time accesses some kind of variable or a call which can start a rr fork.

The server currently assumes it has only one renderer. We have to parametrize it.

Now, we map the messages to ipc on desktop and sockets in browser.

The renderer just talks with the server in all scenarios, for now we assume it's always a single project in a renderer.

The debugger also connects to the server for additional functionality: e.g. plugin call.
It maintains a sequence of RR debuggers with sockets and communicates with them using gdb-js either directly or with commands.

The python commands are loaded when the debugger starts: we also reuse the CPython gdb commands for Python.
They are executed only in a single RR process.

The telemetry is used for logging by me most of the time: it logs to the screen and in a file. In most of the code you can log using `event` or other shortcuts in `telemetry`. You can turn it off or on in `.config.yaml`, you can eventually share a structured log in `telemetry.log`. We probably can use nim-chronicles, but it has to be flexible enough and work on the JavaScript backend.


Plugin communicator
==

Plugins are supposed to analyze language-specific things using the language tools.
For Nim, we just import the compiler.
For C++, currently we parse clang's dump output: we have to write a plugin in C++ which actually analyzes the AST, but I had some problems with the parser API: TODO.

---

### Tab Vs Window Policy (`docs/tab-vs-window-policy.md`)

# Tab-vs-Window Policy for `ct run` / `ct replay`

## Problem

When `ct run` or `ct replay` is invoked and a CodeTracer window already
exists, the current behavior always spawns a new Electron window. This
is wasteful and confusing when the user wants to compare traces
side-by-side in tabs within a single window.

## Solution

A configurable policy controls whether a new trace opens as a tab in the
existing window or as a new Electron window.

### Policy values

| Value      | Behavior                                               |
|------------|--------------------------------------------------------|
| `"tab"`    | Open the trace as a new session tab in the existing window (default) |
| `"window"` | Open a new Electron window (legacy behavior)          |

### Configuration

The policy is set in the user's config YAML
(`~/.config/codetracer/.config.yaml`):

```yaml
newTracePolicy: "tab"   # or "window"
```

The default config (`src/config/default_config.yaml`) ships with `"tab"`.

### CLI override

Both `ct run` and `ct replay` accept flags that override the config:

```
ct replay --new-tab --id=42     # Force tab policy
ct replay --new-window --id=42  # Force window policy
ct run --new-tab my_program.py  # Force tab policy
ct run --new-window my_program  # Force window policy
```

If neither flag is given, the config value is used. If both are given,
`--new-tab` takes precedence (confutils parses left-to-right and the
flag set last wins, but the launch code checks `--new-tab` first).

## Implementation

### Flow for `"tab"` policy

1. `ct run`/`ct replay` resolves the effective policy from CLI flags
   and config.
2. The policy is passed through `run()` -> `runWithRestart()` ->
   `replay()` -> `runRecordedTrace()` -> `launchElectron()`.
3. `launchElectron()` sets `CODETRACER_NEW_TRACE_POLICY` env var
   before exec-ing the Electron binary.
4. In `src/frontend/index.nim`, if the policy is `"tab"`:
   - The first Electron instance acquires
     `app.requestSingleInstanceLock()`.
   - When a second instance starts, the lock fails. The second
     instance quits immediately.
   - The first instance receives the `"second-instance"` event with
     the second instance's argv (which contains the trace ID).
   - The first instance sends `"CODETRACER::open-trace-in-tab-ready"`
     to the renderer with the trace ID.
5. The renderer's `onOpenTraceInTabReady` handler:
   - Creates a new session tab (`createNewSession(data)`).
   - Sends `"CODETRACER::load-recent-trace"` back to the main process.
6. The main process's `onLoadRecentTrace` handler starts the replay
   backend and loads the trace into the new session.

### Caption tab layout

Session tabs are part of the caption bar flex row with the menu,
debug controls, omnibox, new-tab button, overflow chevron, and window
controls. These controls must not overlap.

The tab strip may only render as many visible tabs as can fit at the
tab minimum width. When there is not enough caption space for another
minimum-width tab, additional sessions remain available through the
overflow chevron menu. Selecting a session from that menu must switch
the active tab.

### Flow for `"window"` policy

When the policy is `"window"`, or when no policy env var is set and the
code falls back to `"window"`:
- `requestSingleInstanceLock()` is **not** called.
- Each `ct run`/`ct replay` spawns its own Electron process as before.

### IPC messages

| Message                                    | Direction         | Payload                |
|--------------------------------------------|-------------------|------------------------|
| `CODETRACER::open-trace-in-tab-ready`      | main -> renderer  | `{ traceId: int }`     |
| `CODETRACER::open-trace-in-tab`            | renderer -> main  | `{ traceId: int }`     |
| `CODETRACER::load-recent-trace`            | renderer -> main  | `{ traceId: int }`     |

### Files changed

- `src/common/config.nim` -- `newTracePolicy` field on `ConfigObject`
- `src/frontend/types.nim` -- `newTracePolicy` field on `Config`
- `src/config/default_config.yaml` -- default value
- `src/ct/codetracerconf.nim` -- `--new-tab`/`--new-window` CLI flags
- `src/ct/launch/electron.nim` -- pass policy via env var
- `src/ct/launch/launch.nim` -- resolve policy from flags, pass to commands
- `src/ct/trace/run.nim` -- accept and propagate `newTracePolicy`
- `src/ct/trace/replay.nim` -- accept and propagate `newTracePolicy`
- `src/frontend/index.nim` -- single-instance lock + second-instance handler
- `src/frontend/index/traces.nim` -- `onOpenTraceInTab` IPC handler
- `src/frontend/index/ipc_utils.nim` -- register `open-trace-in-tab` IPC
- `src/frontend/ui_js.nim` -- `onOpenTraceInTabReady` renderer handler + registration

### Known limitations

- The single-instance lock is process-wide. If two traces are launched
  simultaneously with `"tab"` policy, only one second-instance event
  fires at a time. Rapid launches may queue.
- The `"window"` policy does not use `requestSingleInstanceLock()`,
  so it does not interfere with existing multi-window workflows.
- The second instance's env var `CODETRACER_NEW_TRACE_POLICY` is
  inherited from the first `ct` process. If the user changes the
  config between launches, the env var from the first launch wins
  for the lock decision.

---

### Tests (`docs/tests.md`)

notes or todo-s about tests

old/todo:

### running property-based scenario generator test

We generate based on current time a seed and randomize it, then generate a scenario by
repeatedly generating random actions.

Each kind of action usually has a before and after(check) functions, which are invoked as hooks.
Before functions usually might set up something and after(check) functions check properties / other stuff

We should log what happens, so one can easily see how an error happened.


Regressions:
we can save the steps in a file by passing `--file`, instead of running those automatically again: currently its easier to add them to a normal repl test. `quicktest` did have some option for that, but for now this seems to work fine i guess
(we don't really use much of `quicktest` here honestly, as we mostly focus on operation-specific property functions)

Running:

```bash
src/build-debug/tests/run/a_test <traceID>
src/build-debug/tests/run/a_test examples/sum.nim
```

---

### Todo Coroutines (`docs/todo_coroutines.md`)

// rust: async_std, tokio(?)
// c++ coroutine
// c libmill, libdill(?)
// nim: chronos, asyncdispatch(?)

async_std: ~ok
tokio: ~ok similar to async_std
coroutine: ~todo a bit: works for their example
libmill: doesn't seem to work well: segfault on my system
libdill: seems to work: but it's longjmp-based, not lambda based
chronos: ~ok



match
continue
back
filter


detect and use

b dill_longmp or dill_cont or dill_wait
also skip dill_prologue
and detect enter portions: different than lambda coroutines
cr.c:444

detect poll place where we call new functions
and next entrance in async

detect if we are inside an async context

ok:
  then just go over or skip until we find the next function

for nim, improve linedir

for asynccheck:
  # co-step-in as step-in
  # co-next go over or skip

  aIter_async.

  if up in callstack
  then we setup a breakpoint and continue
  but we put a breakpoint on ret of a_async if possible?
  huh it seems we dont need
  if we hit complete and break
  fix linedir
  step-in:
    simple step-in:
      detect it's a async function of type and maybe other symbols
    break aIter
    step after resultIdent line


c++:
  we have to detect usage of await operators
  as they can be used for no-coroutine functionality
  maybe then we should still use the same logic
  for now for their example it can work by detecting await_suspend await_resume await_ready

c:
  skip dill_prologue
  detect cr.c:504 (libdill 2.14)
  check for next
  detect coroutine by calls to dill_ functions? or source code?

steps: what to do by default?
maybe next should be co-next for async context
and we should have a normal-next and normal-step-in
when we make sure it's .. normal
as a fallback
but how to do it as shortcuts
we can have some highlight to show the normal step is now async
and to show additional buttons in those cases (or different color)

flow: it can just use automatically co-next for the context
without making the next similar
but it might be good to cache at least part of this
also think more of calltrace, easier with dynamic for now

c++: detect return typ a: a::promise_type with get_return_object
     next: s

detect return type
next:
  if in async context:
    if in beginning:
      breakpoint on special function after initial async
        `suspend_never::await_resume` for c++ coroutines
      continue
      delete the breakpoint
      finish
    else:
      step-in until in same function, but different line or in await switch context
        await switch context:
          `await_ready`: await step-in context for c++ coroutines
          `resume`: await resume context for c++ coroutines

      if in await switch context:
        breakpoint on special function after resume
          `await_resume` for c++
        loop:
          enable the breakpoint
          continue
          disable the breakpoint
          finish
          check if in start function if yes:
            delete the breakpoint
            eventually next if in end/entrypoint
          else:
            continue in loop
      else:
        ok
  else:
    next


step-in:
  if in async context:
    if in beginning:
      breakpoint on special function after initial async
        `suspend_never::await_resume` for c++ coroutines
      continue
      delete the breakpoint
      finish
      (similar to next)
    else:
      step-in until in function next in callstack, but different line or in await switch context
        await switch context:
          `await_ready`: await step-in context for c++ coroutines
          `resume`: await resume context for c++ coroutines

      if in await switch context:
        breakpoint on special function for suspend
          `await_suspend` for c++
        continue
        delete the breakpoint
        eventually next if in end/entrypoint
      else:
        ok
  else:
    next

---

### Tui Testing (`docs/tui-testing.md`)

# Testing the CodeTracer TUI

**Audience:** anyone adding a pane, a widget, a keybinding or a golden to
`src/frontend/tui/`.

**Governing specs:** `codetracer-specs/Front-Ends/CodeTracer-TUI.md` §7 and
`codetracer-specs/Front-Ends/CodeTracer-TUI.milestones.org`
§"Testing architecture". This document is the operational half: which tier to
reach for, what each one can and cannot see, and the one rule that keeps the
Tier-1 goldens honest.

---

## The two tiers, and why there are two

| | Tier 1 | Tier 2 |
|---|---|---|
| what runs | `TerminalTestHarness`, in process | the compiled binary, in a real pty |
| what parses the screen | nothing — the harness *is* the screen | `libvterm`, via `nim-libvterm` |
| driver | `nim-pty` + `TermAssert` | — |
| lane | `just test-tui` | `just test-tui-real-terminal` |
| cost | ~20 ms per case | a compile plus a spawn per case |
| lives in | `src/frontend/tui/tests/` | `src/frontend/tui/tests/real_terminal/` |

Tier 1 mounts a component tree, runs layout, composites strips into a
`ScreenBuffer`, and lets a test assert on `cellAt`, `dumpTree`, `layoutRegion`,
`dirtyRegions`, `stripCacheStats` and `bytesEmitted`. It records six golden
formats: `plaintext.txt`, `ansi.ansi`, `cellmap.json`, `svg.svg`,
`annotated.svg`, `treedump.txt`.

**All six are derived from the same in-process model that produced the ANSI.**
If the compositor's idea of the screen and a terminal's idea of the screen come
apart, Tier 1 cannot see it. It will record and re-verify a wrong screen for as
long as nobody looks.

That is not a hypothetical. The first run of the cross-tier suite
(`tests/real_terminal/test_cross_tier_snapshot_equivalence.nim`) found three
defects, all in sibling libraries, all sitting inside green runs:

1. `isonim-tui`'s compositor never wrote a ghost cell into the composited
   buffer, so `encodeAnsi` emitted a real space after every wide glyph.
   Measured on `┌世界─┐`: the buffer put `┐` at column 6 and a terminal put it
   at column 8 — one column of drift per wide glyph, accumulating.
2. `nim-libvterm`'s `cellAt` raised `RangeDefect` on the trailing half of any
   wide glyph, which took down every consumer that walks a whole screen.
3. `TermAssert`'s `renderPlain` and `renderCellmap` disagreed with isonim-tui's
   encoders about that same trailing half, and did not encode `underline` at
   all.

---

## Which tier to assert in

### Assert at Tier 1

* **A pane's content and semantics.** Which rows exist, what text is in them,
  which cell carries the execution pointer, whether the gutter shows a
  breakpoint, what the status line reads.
* **Layout arithmetic.** Pane regions, disjointness, totality over the cell
  grid, reflow after `h.resize`.
* **Reactivity.** That a ViewModel change reaches the screen after `flush()`.
* **Emission budgets.** `bytesEmitted` after a single step, `stripCacheStats`
  hit rate.

Tier 1 is the per-PR lane. Everything that can be asserted there should be,
because it is two orders of magnitude cheaper and its failures point at one
component.

### Only Tier 2 will do

Anything that involves the terminal **as a peer**. Tier 1 has no peer — it is
both sides of the conversation — so these are not "better" at Tier 2, they are
*unobservable* anywhere else:

| subject | why Tier 1 cannot see it |
|---|---|
| the screen a terminal actually renders | Tier 1's screen is the model that produced the bytes |
| real cell attributes after parsing | a style struct is not a colour a terminal shows |
| SIGWINCH | `h.resize()` is a method call; `setWindowSize` is a kernel signal |
| capability negotiation | what the app negotiated is read from the terminal's side |
| synchronized output (DEC 2026) | the bracketing is in the byte stream, not the model |
| mouse (SGR 1006) | real byte sequences arriving on a real fd |
| keys as bytes | `sendKey("f10")` is an xterm sequence, not a synthesized event |
| cursor position, shape, visibility | the model has no cursor |
| OSC 8 hyperlinks | not represented in the `ScreenBuffer` |
| termios, alt-screen, signal-safe restore | there is no tty to leak |
| process lifecycle and exit codes | there is no process |

### The rule

> **A pane's content and semantics are asserted at Tier 1.
> A pane's appearance on a real terminal is asserted ONCE per pane at Tier 2,
> as cross-tier `snap` equality.
> Anything involving the terminal as a peer is asserted only at Tier 2.**

---

## THE RULE FOR A NEW PANE: exactly one equivalence test

**A new pane needs exactly one cross-tier equivalence test. Not zero, and not
one per assertion.**

*Not zero*, because a pane with only Tier-1 goldens has goldens that nothing
has ever checked against a terminal. The equivalence test is what grounds every
other golden that pane records — after it passes once, a Tier-1 golden for that
pane is evidence, and before it does, it is a screenshot of a model.

*Not one per assertion*, because each costs a compile and a spawn, and because
the second one proves nothing the first did not. Cross-tier equality is a
property of the RENDERING PATH — compositor, SGR encoder, width table, terminal
parser — and that path does not change between two panes' assertions. Once a
pane's characteristic screen round-trips, its other states are Tier-1 work.

Concretely, adding a pane means:

1. `src/frontend/tui/tests/apps/app_<pane>.nim` — a module exporting
   `buildTree*(r: TerminalRenderer): TerminalNode` and, under
   `when isMainModule`, one call to `snapshotAppMain(buildTree,
   commandLineParams())`.
2. One case in `tests/real_terminal/test_cross_tier_snapshot_equivalence.nim`
   calling `bothGeometries("app_<pane>", paneApp.buildTree)`.
3. Everything else about the pane at Tier 1.

The child app and the Tier-1 half run **the same `buildTree` proc**. That is
what makes the result a statement about the renderer rather than about two
hand-written fixtures that happen to agree.

---

## How the cross-tier comparison works

`src/frontend/tui/testing/dual_snap.nim` is the whole mechanism.

```
runDualSnap(stem, buildTree, cols, rows)
  ├─ Tier 1: newTerminalTestHarness(cols, rows).mount(buildTree)
  │          → six files in test-logs/tui-dual-snap/cases/<case>/tier1/
  ├─ Tier 2: compile tests/apps/<stem>.nim, spawn it under TermAssert
  │          at cols×rows, wait for the frame barrier
  │          → six files in .../tier2/
  └─ compareSnapshotDirs(tier1, tier2)
```

Nothing is written into the repository: everything lands under `test-logs/`,
which `.gitignore` covers.

### What is compared

`plaintext.txt` and `cellmap.json`, projected onto a canonical cell model —
rune, width, foreground, background, attribute set, underline style — because
the two tiers write two different JSON dialects for the same screen.

A divergence report **names the first differing `(row, col)` with both runes
(and their codepoints) and both style sets**. "Screens differ" is not a
diagnosis and this comparison never emits one.

### What is NOT compared, and the doctrine about it

Four files are not compared and two cell fields are not compared. Every one is
a named entry in `CrossTierExclusions` in `dual_snap.nim`, carrying a
justification **and the evidence that established it**, and
`CrossTierExclusionCount` is asserted by the suite so one cannot be added
without the number moving in a diff a reviewer reads.

> **Never loosen the comparison.** There is no tolerance, no "close enough",
> no whitespace-insensitive mode. A difference is either a defect or a named,
> individually justified exclusion. An unexplained exclusion is a review
> failure.
>
> **If you find yourself wanting to relax the check to make it pass, stop.**
> The three defects listed at the top of this document were all found that way,
> and all three would have been "an irreducible difference between the tiers"
> to anyone who reached for a tolerance instead of a debugger.

A **canonicalisation** is a different thing and is kept in a different list
(`CrossTierCanonicalisations`). It maps two spellings of one fact onto one
representation — Tier 1's `ckAnsi n` and Tier 2's `ckIndexed n` are the same
colour — and it still FAILS when the fact differs. Confusing the two is how a
comparison quietly stops checking.

The exclusion register is `echo`ed by the suite on every run, pass or fail, so
a reader of a green run can see what it stopped checking — and `checkpoint`ed
again beside any divergence, which is where a reader of a failure wants it. It
has to be an `echo`: `std/unittest` accumulates checkpoints and flushes them
only from `fail()`, so a checkpoint alone would print on a red run and nowhere
else. Note that `run-nim-test-lane.sh` captures a green file's stdout, so read
the register by running the suite binary directly:

```sh
nim c --path:src/frontend/viewmodel --path:../TermAssert/src \
      --path:../TermAssertClient/src --path:../nim-libvterm/src \
      -d:isonimTuiGrammarArchive=$PWD/build/grammars/libcodetracer_tui_grammars.a \
      -r src/frontend/tui/tests/real_terminal/test_cross_tier_snapshot_equivalence.nim
```

### What cross-tier equality cannot catch, and never will

**Cross-tier equality is a DIFFERENTIAL check.** It compares two renderings of
the *same* program: both tiers run the same `buildTree` over the same code under
`app/`. So it is blind by construction to any defect the two renderings share.
A wrong screen painted identically twice compares equal.

This was measured rather than reasoned about. A mutation arm made
`app/views/gutter.lineNumberStyle(gpUnverified)` return the VERIFIED style, so
an `savUnverified` file rendered exactly like the recording's own copy — the one
distinction the source-access seam exists to preserve. `runDualSnap` reported
**0 divergences at both geometries, correctly**: both tiers painted
`indexed:8`. What reddened was the assertion about what the colour MEANS —
`cellAt(row, col).fg.idx == 3'u8` — and nothing else in the tree could have.

> **A Tier-2 case that only compares the tiers asserts that the renderer is
> faithful. It never asserts that the screen is right.** Every colour, glyph or
> position a Tier-2 case relies on for its MEANING must also be asserted
> ABSOLUTELY — `fg.idx == 3'u8`, not "the same as Tier 1".

The corollary matters most where a pane's cross-tier case is the only Tier-2
case it has, which the rule above makes the common shape: adding a pane to
`runDualSnap` buys nothing about that pane's semantics. Semantics are Tier-1
work — except the ones observable only as a real terminal's cell state, colour
and attributes, and those need an absolute assertion here.

---

## Determinism: never sleep, never poll for a needle

`waitForText("line 42", 5s)` is banned in this tree, and it is worth knowing
why. It passes on a **partially painted** frame that happens to contain the
needle, and when it fails it fails as a timeout — a symptom, not a diagnosis,
whose natural remedy ("raise the timeout") is wrong for every cause it can
have. See `codetracer-specs/Testing/Verification-Harness-Traps.md` §3.

Two mechanisms replace it.

### The frame barrier (the cursor)

`encodeAnsi` emits every cell of every row in order, so the last glyph a frame
writes is the bottom-right one and the cursor comes to rest at
`(rows-1, cols-1)` exactly when the frame is complete. It cannot be there
earlier. `dual_snap.waitForCompleteFrame` waits for that, and its failure names
**the cursor position it actually observed**, whether the child is alive, and
what is on the screen — so "still painting", "died on startup" and "painted
something else" are three different reports.

An OSC-title barrier was tried and rejected: libvterm delivers OSC payloads as
string fragments and `nim-libvterm`'s mirror overwrites rather than
accumulates, so a title straddling one of TermAssert's 4096-byte pty reads
would never match.

### The IPC channel (`--test-ipc`)

TermAssert hosts a per-session Unix socket and passes `$TERM_ASSERT_URI` to the
child; `TermAssertClient` lets the child call `screenshot(label)`, which the
harness records into `snapshots()`. That inverts who decides a frame is final:
the app declares it, and the test asserts on the frame the app declared.

**`--test-ipc` is a test-only flag.** It is parsed by
`src/frontend/tui/testing/test_app_runtime.nim` and nowhere else. Three
assertions keep it out of a release build, all in
`tests/test_tui_build_prerequisites.nim`:

* `app/cli.parseTuiCommand` **refuses** it, in every argument position — asked
  of the parser, not of the source text, because a scan for the spelling would
  be reddened by a future comment and satisfied by a rename;
* `TuiHelpText` does not mention it;
* **no module under `testing/` appears in `main.nim`'s resolved import
  closure**, which is what makes the first two more than a convention.

The toolchain says the same thing a fourth way: `test_app_runtime.nim` imports
`term_assert_client`, whose `--path` only the `tui-real-terminal` lane and the
child-app compile line carry, so a release build that reached it would not
compile.

#### The two-channel race, and the handshake that closes it

The pty and the IPC socket are **two unordered channels**. TermAssert's `pump`
reads the pty 4096 bytes at a time and services IPC *between* chunks, so a
child that requested a screenshot immediately after painting would be serviced
with the tail of its own paint still unread — and the harness would record a
partial frame, silently, more often on the larger geometry.

So the child waits to be asked:

```
parent: waitForCompleteFrame(...)     # proves the whole frame is consumed
parent: send("S")
child:  requestScreenshot(label)      # only now
parent: waitForSnapshotLabel(label)
```

No sleep anywhere. What the child still owns is the decision to *answer* —
which is exactly what `--never-settle` withholds, and what the negative arm
measures.

### Three distinguishable failures

`test_ipc_settled_frame.nim` asserts all three, because folding any two of them
together turns a plumbing defect into an application defect in the report:

| state | how it presents |
|---|---|
| the child never painted | `waitForCompleteFrame` fails naming the cursor position it saw |
| the child painted but declined to emit the label | **"label never arrived"**, naming the label, the labels that did arrive (`[]`), `child alive=true`, and the screen |
| the socket was miswired | the child exits `3` before painting with `TERM_ASSERT_URI connect failed: <path>` |

---

## Test-quality rules for this tree

These are not stylistic. Each one is a defect class that has cost a milestone
somewhere in this repository — see
`codetracer-specs/Testing/Silent-Self-Pass-Audit-2026-08-23.md` and
`codetracer-specs/Testing/Verification-Harness-Traps.md`.

1. **No skipped tests for a missing prerequisite.** A missing grammar archive,
   an unbuilt binary, an absent recorder: each FAILS, by name, and names the
   recipe that fixes it. A test that detects a missing prerequisite, returns
   early and is counted PASSED is the defect the silent-self-pass audit
   catalogues. The one sanctioned exception is the fixture corpus's counted
   `MISSING-PREREQ SKIP:` — and an all-skipped run fails the lane.
2. **No `when false`, no bare early return, no `try/except` that turns a
   failure into a pass.**
3. **Count your assertions at runtime.** Every suite here carries
   `const ExpectedAssertions = <n>` on one line (the lane runner reads exactly
   that spelling), a counted `ck` template, and a final case that echoes
   `CHECKS: <n>` and asserts the total. A case that returned early or a loop
   that skipped a member reddens the file on the spot instead of being noticed
   by someone differencing two runs. **Write the number last, from a run.**
4. **Every negative assertion needs a positive twin through the same code
   path.** `assert not contains` is satisfied by an empty haystack. If a scan's
   size is knowable, assert the SIZE, not that it is non-empty: "at least one"
   is satisfied by one member of three.
5. **A mutation arm is a deliverable, not a demonstration.** A comparison that
   cannot be made to fail is indistinguishable from one that is not reading the
   files. The cross-tier suite carries four: a changed rune, a changed style, an
   *excluded* attribute that must NOT fail (with a non-excluded one through the
   same helper that must), and a directory compared with itself, which is
   refused rather than passed.
6. **An expected value must not be produced by the code under test.** Where
   the subject IS a published table, read the publication: CTUI-9's
   `app/tests/test_keymap_no_conflicts.nim` parses §4.2's markdown out of
   `codetracer-specs/Front-Ends/CodeTracer-TUI.md` at run time (located from
   `currentSourcePath()`, not from the working directory) and compares it with
   `app/input/keymap.defaultKeymap()`. A hand-copied table in the test would
   have been written from the same reading that produced the implementation,
   and the two would agree about a misreading. A missing sibling checkout
   FAILS by name — rule 1 applies to an absent oracle exactly as it does to an
   absent grammar archive.
7. **No mocks of ViewModels or trace engines.** Tests load real `.ct`
   containers produced by real recorders driven by a real `replay-server`. The
   permitted fakes are `Pilot` synthetic input and `TestClock` virtual time, at
   the hardware boundary, each justified in the header of the file that uses
   it; and `MockBackendService` in exactly one file, CTUI-0's stack-compiles
   test.
8. **A generated file this repository COMMITS needs a reader in a lane.**
   `bench-results/benchmark_results.json` is committed, is what
   `github-action-benchmark` consumes, and for the whole of CTUI-14 no test
   opened it — so four defects lived in it (a `samples=` that no test asserts,
   a `verdict=met` on two rows whose published condition was not satisfied, a
   `verdict=` value with a space in it, a comparison direction) while every
   suite stayed green. `tests/test_benchmark_artifact.nim` reads it, and takes
   its expectations from `benchmarks/tui_benchmarks.nim`'s own `record(...)`
   call sites rather than from a list in the test, so entry count, names,
   condition-gap kinds and comparison directions are compared BETWEEN the
   source in the tree and the file committed beside it. Rule 4 applies to the
   parse itself: a scan that found no call sites would make every one of those
   comparisons pass, so it carries an explicit non-vacuity floor.

---

## Running it

```sh
just tui-prereqs                # submodules + build/grammars/*.a  (idempotent)
just build-tui                  # build/bin/codetracer-tui
just test-tui                   # Tier 1
just test-tui-real-terminal     # Tier 2 (depends on build-tui)
```

Both lanes are discovered, not enumerated: `ci/lib/test-lane-files.sh` globs
`src/frontend/tui/tests/test_*.nim`, `src/frontend/tui/app/tests/test_*.nim`
**and** `src/tests/launcher/test_*.nim` one level deep for `tui`, and finds
`src/frontend/tui/tests/real_terminal/test_*.nim` recursively for
`tui-real-terminal`. Adding a suite needs no edit there.

`src/tests/launcher/` is the third `tui` glob and the odd one out. CTUI-12 put
it there because the milestone names the path, and the placement is right for
what the suite is: it drives the `ct` **launcher** — a binary built in the
`codetracer-launcher` sibling — over component bundles
`scripts/build-tui-component.sh` assembles, so it belongs to neither `app/`
(which may not spawn a process) nor `real_terminal/` (it needs no pty). It runs
with the Tier-1 flags, which cost it nothing: it links no grammar archive and
imports no `isonim_tui`.

`app/tests/` is a Tier-1 directory with one extra property, and it is the
reason CTUI-3's layout suites live there rather than in `tests/`:
**`tests/test_tui_facade_boundary.nim` walks every `.nim` file under `app/`,
including those.** A suite placed there cannot import `host/`, `std/posix` or
`std/osproc` without reddening that guard. So "the reflow suite must not reach
for SIGWINCH" is enforced structurally instead of by agreement — the signal is
asserted at Tier 2, where a signal exists.

The split matters and is deliberate: only the Tier-2 lane carries
`--path:../TermAssert/src --path:../TermAssertClient/src
--path:../nim-libvterm/src`. A Tier-1 suite that could compile against
TermAssert would be one edit away from spawning a pty in the fast lane, and the
separation is what keeps "asserted in process" and "asserted on a terminal"
answerable separately.

`src/frontend/tui/tests/apps/` holds child apps, not tests. They are named
`app_*.nim` so neither lane's discovery collects them; the Tier-2 suites
compile them on demand through `dual_snap.compileChildApp`, which is
timestamp-guarded against the app source, `test_app_runtime.nim` **and every
`.nim` under `app/`** (`dual_snap.newestSourceTime`).

That last clause was added by CTUI-5 after it bit: a mutation arm edited
`app/views/gutter.nim`, rebuilt, ran the Tier-2 suite and restored the file —
and the next lane run reused the MUTATED binary, because neither the app source
nor the runtime had changed. The suite reported a defect that no longer
existed. The dangerous direction is the other one: a cross-tier comparison
between a fresh Tier-1 model and a stale Tier-2 binary is a comparison of two
different programs.

**CTUI-7 met the same trap through a different door, and it is worth knowing
before you run a mutation arm.** The stamp compares MTIMES. Restoring a mutated
file from a backup taken with `cp -p` — or with `git stash`, or with any tool
that preserves timestamps — gives the restored file an mtime OLDER than the
binary built from the mutation, so `newestSourceTime` sees nothing new and the
next Tier-2 run reuses the mutated child app. It presents as three suites
failing on rows whose model and terminal text differ by exactly the thing the
arm changed, long after the arm was restored and `git diff` is clean. The remedy
is two commands:

```sh
find src/frontend/tui/app -name '*.nim' -exec touch {} +
rm -rf test-logs/tui-dual-snap/bin
```

The guard is right; a timestamp-preserving restore is what defeats it. Sibling libraries (`isonim-tui`, `TermAssert`) are
deliberately **not** in the stamp — a child app draws `app/`, and rebuilding on
every sibling edit would cost the lane a link per case for a dependency that
moves far less often.

### Where the artifacts are

```
test-logs/tui-dual-snap/
  bin/                       compiled child apps
  nimcache/                  shared, so the second app costs a link
  cases/<stem>-<W>x<H>/
    tier1/  plaintext.txt ansi.ansi cellmap.json svg.svg annotated.svg treedump.txt
    tier2/  the same six, written by TermAssert
```

Both directories survive the run. On a failure, diff them — and read
`svg.svg` in a browser if the plaintext is not enough. `/test-logs/` is
gitignored, so nothing here can dirty the tree.

---

## Layering: where a test module may live

```
src/frontend/tui/
  app/       SDK-CONSUMER. codetracer_embed, headless_app, isonim, isonim_tui,
             std/* that touch neither a process nor a terminal. Covered by
             ci/test/sdk-facade-boundary.sh through a .sdk-consumer marker.
    layout/  profile selection (pure) and the LayoutNode -> Yoga projection.
    views/   header, status bar, shell — every one a pure function to text.
    tests/   Tier-1 suites that MUST obey the app/ layer rule (see above).
  host/      NATIVE HOST. The only place allowed to reach backend/stdio_backend
             and viewmodel/headless_session (osproc, pty, termios, signals),
             and the owner of SIGWINCH (host/resize.nim). Exempt from the
             facade guard, and the exemption is CHECKED.
  testing/   TEST-ONLY. Needs app/'s capability AND host/'s, and the shipped
             binary links none of it. Not reachable from main.nim — asserted.
  tests/     Tier-1 suites, plus apps/ and real_terminal/.
  main.nim   wires host/ to app/ and nothing else.
```

### The F10 step key

`testing/test_app_runtime.nim` also recognises one INPUT sequence, `\x1b[21~`
— xterm's F10, which is exactly what `TermAssert`'s `sendKey("f10")` writes.
On it the runtime increments a step counter, rebuilds the tree and repaints,
leaving the cursor back on the frame barrier. CTUI-5's Tier-2 test is specified
as "step with `sendKey(\"f10\")`", and a snapshot app therefore has to be able
to advance rather than only to paint.

It is **not a flag** and adds nothing to any command line: `buildTree` gains an
optional `step` parameter (`SteppedTreeBuilder`), and an app that ignores it
paints the same tree however often F10 arrives. Under `--test-ipc` the child
labels step 0 with the parent's own label and step *N* with `<label>-stepN`
(`stepLabel`), so a parent driving F10 asks for the frame it wants BY NAME
rather than by timing.

### Driving a snapshot app with real input (CTUI-6)

`runSnapshotApp` takes two optional callbacks, both `nil` for every CTUI-2,
CTUI-3 and CTUI-5 app — which is why their byte streams are unchanged:

* **`input: proc(token: string): bool`** receives one complete input token — a
  plain byte, or a whole escape sequence — and answers whether to repaint. The
  runtime does the framing (accumulate from `ESC [` to a final byte in
  `0x40..0x7E`), so an app decodes a value instead of running a state machine.
  That is what lets `app/input/call_stack_keys.decodeMouse` be asserted against
  the exact bytes `TermAssert.sendMouseClick` writes, in the Tier-1 lane, with
  no pty.
* **`links: proc(cols, rows: int): seq[PaneHyperlink]`** says where OSC 8
  hyperlinks go on the frame about to be painted. isonim-tui has no hyperlink
  concept at all, so `app/views/hyperlinks.frameBytesWithHyperlinks` emits them
  **inline with the frame**, walking the same buffer in the same order as
  `encodeAnsi`. With no links its output is byte-identical to `frameBytes`, and
  `tests/real_terminal/test_real_call_stack.nim` asserts exactly that.

**An input-driven repaint increments the step counter**, so the new frame has a
name. This is not cosmetic: `waitForCompleteFrame` cannot be the barrier after
an input, because the cursor is *already* parked on the bottom-right cell from
the previous frame and the call returns immediately. A parent that drove a click
and then read the screen would read the frame from before its own click. The
child labels the repaint `<label>-stepN` and the parent waits for that label.

> **Rule: after sending input to a child, wait for a frame you can NAME.** The
> cursor barrier proves *a* frame is complete, never that *your* frame is.

### Driving a snapshot app with real KEYS (CTUI-9)

CTUI-9 made three changes to `testing/test_app_runtime.nim`, all of them
additive, and each one closes a hole that made a §4.2 binding untestable at
Tier 2. Know them before you write a key-driven case.

* **F10 now reaches the app.** CTUI-5 made `\x1b[21~` the runtime's own step
  key, which meant the one function key §4.2 binds — "Step Over (Forward)":
  `n` / `F10` — was the one key no app could be asked about. The token is now
  offered to `input` *before* the step, and the step still happens, so every
  CTUI-2/3/5/6/8 app behaves exactly as it did.

* **`ISIG` is cleared in `enterRawMode`.** §4.2 binds `Ctrl+c` to "Quit
  Debugger — Exit CodeTracer TUI session **cleanly**". With `ISIG` set the line
  discipline turns `0x03` into `SIGINT` before the application reads a byte:
  the child dies on the signal and `sendControl('c')` measures the tty rather
  than the keymap. Clearing it is what `cfmakeraw(3)` does. No existing app is
  affected — none is sent `0x03`.

* **`FramePrologue`**, bytes written immediately *before* a frame. It exists
  for the cursor: §4.1's modes are told apart on a real terminal by DECSCUSR
  shape and DECTCEM visibility (`app/input/modal_state.cursorControlBytes`),
  and "the model has no cursor" is a row of the table above. `nil` for every
  app that does not want it, and with it the byte stream is unchanged.

**Two more bytes never reach the app, and F10 was only the third.** `q` is
`TestAppQuitByte` and `0x04` ends the child too, so the keybinding table's `q`
("Quit Debugger") and `Ctrl+d` ("Half Page Down") cannot be driven at Tier 2 —
sending either ends the process instead of pressing a key. `Ctrl+c` is the
quit binding that *is* exercised here, and its half-page twin `Ctrl+u` (`0x15`)
is unaffected. Assert those two at Tier 1, and do not read a green Tier-2 case
that sends `q` as evidence that anything was bound.

**A lone `Esc` cannot be delivered as one byte.** The runtime accumulates from
`\x1b` while the buffer is still a prefix of the F10 sequence, so a single
`\x1b` sits there until something else arrives. Send `\x1b\x1b`: the second one
breaks the prefix, is not a CSI, and falls through the "honour the byte that
broke the prefix" arm — delivering exactly one `Esc` token.
`tests/real_terminal/test_real_keybindings.nim` asserts the MODE that produces,
so a change to the framing reddens rather than silently delivering nothing.

**`TermAssert.sendKey` drops `shift+`.** Its modifier loop consumes the prefix
without recording it, so `sendKey("shift+f10")` writes `ESC [ 2 1 ~` — byte for
byte what `sendKey("f10")` writes. To exercise a shifted function key, write
xterm's modified sequence yourself: `ESC [ 2 1 ; 2 ~` (the parameter is
`1 + Shift`). CTUI-9 drives both and asserts the consequence of each, rather
than working around the harness silently.

### Parking the cursor somewhere else (CTUI-10)

CTUI-10 added one more additive hook to `testing/test_app_runtime.nim`, and it
is the only one that **moves the cursor**.

* **`FrameEpilogue`**, bytes written immediately *after* a frame. It exists for
  §3.3.6's prompt: a `:` prompt is a prompt only if the terminal's own cursor is
  sitting in it, and "cursor position" is a row of the table above. `nil` for
  every CTUI-2 through CTUI-9 app, and with it the byte stream is unchanged for
  all of them — the cross-tier suite is what keeps that true.

> **An app that installs a `FrameEpilogue` breaks `waitForCompleteFrame`.**
> The barrier is "the cursor rests at `(rows-1, cols-1)`", and an epilogue that
> moves it means that can never be satisfied. Wait on
> `dual_snap.waitForCursorAt(row, col)` instead: the same barrier argument at
> the position the epilogue parks on, and equally exact, because the CUP is
> written after the last cell of the last row. `apps/app_command_mode.nim`
> returns `""` from its epilogue whenever no prompt is open, so its NORMAL
> frames still use the ordinary barrier and only its prompt frames need the
> other one.

#### Barriers across a RELAUNCH (PLAT-6's persistence)

A test whose subject is *what one process wrote and a different process read*
needs a barrier for the **second** process, and the two obvious ones are both
wrong:

- **"the screen has content" is not a barrier.** After a relaunch the pty is
  fresh, and a blank frame and a restored frame both take time to arrive — only
  one of them is the subject, and a `screenContents().len > 0` poll cannot tell
  them apart.
- **`waitForCompleteFrame` is not one either** for an app with a
  `FrameEpilogue`, for the reason in the box above.

`apps/app_layout_persist.nim` parks the cursor at `(0, step + 1)` after every
frame, so the second process's step 0 is `(0, 1)` — a position a freshly spawned
terminal's cursor was not at a moment earlier, because it starts at `(0, 0)`.
**The off-by-one IS the barrier**, and `step` is `test_app_runtime`'s own
counter, which advances only when `handleInput` returns true. That is the
important half: a child that stopped repainting makes the barrier
**unreachable** — a named timeout carrying the observed cursor and screen —
rather than trivially true. Preserve that failure direction in any barrier you
write.

### Driving the SHIPPED BINARY on a real trace (CTUI-11)

Until CTUI-11 every Tier-2 case spawned a **snapshot app**: one component tree,
`test_app_runtime`'s runtime, no debugger. CTUI-11 gave `main.nim` a driver, so
`build/bin/codetracer-tui <trace-folder>` is now itself a subject —
`tests/real_terminal/test_real_capability_negotiation.nim` spawns it. Three
things about it differ from a snapshot app and all three will bite.

**It paints TWICE on startup, and `waitForCompleteFrame` returns on the first.**
`main.nim` negotiates the terminal, claims the tty and paints *frame 0* — the
shell, with `opening <folder> …` on the status line — and only then spawns
`replay-server`. Frame 1 is the debugger. Both end with the cursor on the
bottom-right cell, so the cursor barrier cannot tell them apart. Wait for the
status row to stop saying `opening ` and *then* for the cursor barrier;
`settleOnDebugger` in that suite is the shape.

That order is deliberate and is not a testing convenience: a front-end that
showed nothing until the engine answered would be indistinguishable, for that
whole second, from one that had hung. It is also what makes CTUI-11's cold-start
gate measurable — process start to frame 0, with probing enabled, measured at
**25 ms** on a 24-core host at load 10.8.

**It needs a real recording.** `resolveFixture("calc")` from
`tests/fixtures/fixture_provider.nim`, and a resolution failure **fails by
name** with the recipe rather than skipping.

**It restores the terminal on the way out**, so anything read after the child
exits reads a terminal that has been given back: `mouseProtocol()` is `mpNone`
again after `?1006l ?1000l`. Read the negotiation **while the child is alive**.

### What libvterm can and cannot see about DEC 2026

`synchronizedOutput()` is a **live flag, not a latch**:
`nim-libvterm/extended_state.handleCsi` sets it on `CSI ? 2026 h` and clears it
on `CSI ? 2026 l`. After a complete frame it reads `false` whether the driver
bracketed correctly or never opened a bracket at all.

**`TermAssert.assertSynchronizedRender` cannot fail.** Its "not observed" arm is
`discard` (`TermAssert/src/term_assert.nim:640-647`). Calling it satisfies the
published gate and asserts nothing, so it is never the only thing a case does.

The pairing is therefore established in two halves, both falsifiable:

* feed `host/terminal_driver.bracketFrame`'s own output into a fresh
  `nim_libvterm.newScreen` — the same parser the pty path uses. The flag goes
  **true** after the open and **false** after the close, and a stream missing the
  close leaves it **true**. That is the positive control, and the mutation arm
  is one line.
* assert `synchronizedOutput() == false` on the live binary after a settled
  frame. An unpaired open latches it and reddens this.

### The token framer is `host/`'s now, and the snapshot runtime consumes it

`testing/test_app_runtime.nim` no longer carries its own byte-to-token state
machine, timed read or `frameBytes`. All three moved to
`host/terminal_driver.nim` and it imports them. The direction is forced:
`tests/test_tui_build_prerequisites.nim` asserts that no module under `testing/`
is in `main.nim`'s import closure, so the shipped driver cannot import the
runtime.

Behaviour is byte-for-byte what it was on every sequence any suite here sends,
including the two documented above (a held lone `\x1b`, and `\x1b\x1b`
delivering exactly one `Esc`). **One thing was added: SS3.** `ESC O P` … `ESC O
S` are xterm's F1-F4 and exactly what `sendKey("f1")` writes; the old framing
dropped the `ESC` and delivered `O` then `P` as two ordinary bytes, so §4.2's
`F1` (Command Palette) was a binding no terminal could reach. Nothing in this
tree sent those bytes, so no existing byte stream moved.

**What was NOT shared is the termios.** `host/terminal_driver.start` uses
`nim-termctl`'s `enableRawMode`, which installs the signal-safe restore CTUI-11
asks for — and with it an `atexit` hook that writes an alt-screen leave, a
mouse-off and a cursor-show as the process dies. A snapshot app must not do
that: those bytes would land on the pty a suite is still parsing, and
`cursorVisible` is a fact CTUI-9's modal cases assert.

### An empty shell has no styled spans on it

Measured while writing `app/tests/test_degraded_style_tables.nim`:
`newShellModel(...)` plus `shellStyledRows` produces a screen with **zero**
cells carrying a colour. `app/views/shell.paintPane` draws CTUI-3's plain
`TITLE ────` row with the default style and only delegates to a pane's own
painter when that pane's model has content.

So a fixture built from `newShellModel` alone makes "no colour after degrading"
true for free, on a screen that never had any — the positive-floor trap arriving
through a fixture instead of through an assertion. Fill at least one pane model
(`initTimelineBarModel(boundsKnown = true)` is the cheapest) before asserting
anything about styles.

### `unicode.strip` does not strip an all-whitespace string

A Tier-2 suite that reads rows off the terminal almost always trims the
right-hand padding, and almost always imports `std/unicode` for `Rune`. Those
two facts collide:

```nim
import std/[strutils, unicode]
let blank = repeat(' ', 10)
blank.strip(leading = false)            # ten spaces — unicode.strip wins the overload
strutils.strip(blank, leading = false)  # ""
```

Measured on nim 2.2.8. `unicode.strip` returns an **all**-whitespace string
unchanged; `strutils.strip` returns `""`. Every assertion about a row *with
content* passes either way, so this only ever shows up as "a blank row is 100
characters long" — which is how it was found, in
`tests/real_terminal/test_real_command_mode.nim`. **Qualify `strutils.strip` in
any helper that reads a terminal row.**

### The raw byte stream (CTUI-14)

`TermAssert` parses everything it reads with libvterm, and for almost every
assertion in this tree that is the right subject. Two claims are not about the
parsed screen at all, and both are about **sequences**:

* **the alternate screen is PAIRED** — `CSI ? 1049 h` and `CSI ? 1049 l`,
  counted;
* **DEC 2026 is paired** — the same shape, and CTUI-11 established it by feeding
  the driver's own `bracketFrame` output into a fresh libvterm screen.

Neither is readable off a terminal after the child exits, for the reason
CTUI-11 recorded about `synchronizedOutput`: these are **live flags, not
latches**. After the leave, alt-screen state is `false` whether the child left
correctly or never entered.

So `newTuiTest(...).transcript()` keeps the raw bytes beside the parse.

```nim
var sess = newTuiTest(bin, args).width(120).height(40).transcript().spawn()
…
let counts = altScreenCounts(sess)       # lifecycle_support.nim
check counts.enters == 1
check counts.leaves == 1
check sess.transcriptDroppedBytes() == 0 # a truncated transcript is not sound
```

**Off by default**, so no existing case pays for a second copy of its output,
and capped at `TermAssert.TranscriptCapBytes` (8 MB) with
`transcriptDroppedBytes` reporting when the cap bit — because a "does not
contain" assertion over a truncated buffer is not an assertion.

### `lifecycle_support.nim` is a library, not a suite

`tests/real_terminal/lifecycle_support.nim` holds what CTUI-14's four
`test_real_*` suites share: `tuiSession` (the shipped binary in a pty with a
known environment), `settleOnDebugger`, `waitForOpeningFrame`,
`altScreenCounts`, `wedgeFolder`, and the surviving-child detectors. The name
is what keeps it out of the lane — `ci/lib/test-lane-files.sh` finds
`test_*.nim` — and **nothing in it calls `check`**, which is stronger than "use
a template": a shared helper is exactly where the invisible-`testStatusIMPL`
trap does the most damage, so those functions answer or raise and the suites
assert.

**`TermAssert`'s blocklist used to beat its overrides.** `effectiveEnv` skipped
any override whose key was also blocked (`if k in blocked: continue`), so
`.envRemove("X").envSet("X", v)` left `X` **absent**. This cost CTUI-14 a
confusing run: the clock case set `CODETRACER_TUI_HANDSHAKE_MS` to 1500 and to
6000 and measured 30013 ms and 30016 ms both times, because the variable never
reached the child and the binary used its own default.

**Fixed in `TermAssert` (2026-09-06)** — an explicit `envSet` now wins over
`envRemove` and over the tmux defaults, pinned by
`TermAssert/tests/test_harness_env_overrides.nim`, and written up as trap 11 of
`codetracer-specs/Testing/Verification-Harness-Traps.md`. **Keep blocking
**or** setting, never both, anyway**: this workspace pins no `TermAssert`
revision, so this lane can be built against a checkout that predates the fix,
and the discipline costs nothing.

**Two more `TermAssert` traps this campaign found, neither fixed, both
measured** — traps 9 and 10 of the same document, and both make a case
*vacuous* rather than red:

* **`sendKey` silently strips `shift+`.** Measured by spawning `cat -v` in a
  pty: `sendKey("shift+f10")` puts `^[[21~` on the wire, byte for byte what
  `sendKey("f10")` puts there. `ctrl+` has the same hole for a *named* key (it
  is only applied in the single-character arm), so `ctrl+f10` is also `F10`.
  Write a modified function key as bytes — `ESC [ 21 ; 2 ~` for shifted `F10`.
* **`assertSynchronizedRender` cannot fail.** Its "not observed" arm is a bare
  `discard` and the function contains no `raise` at all. Measured against
  `echo no-sync-here` (14 bytes of output, zero DEC 2026 sequences,
  `synchronizedOutput()` false): the assertion returned normally. Assert the
  pairing on the byte stream instead, as
  `test_real_capability_negotiation.nim` does.

### The shape-valid non-recording (CTUI-14)

`lifecycle_support.wedgeFolder()` builds `test-logs/ctui14-wedge/` with a
4096-byte deterministic blob as its `trace.bin`. It is the folder that
**passes** `host/native_host.traceFolderProblem` and then **stalls the DAP
handshake**: `replay-server` answers `initialize`, `configurationDone` and
`launch` in full — `Content-Length: 102`, all 102 bytes, `success: true` — and
then never sends the `stopped` event the handshake waits for, and never exits.
The stall is therefore at a MESSAGE BOUNDARY; a stall mid-body is what
`DapStdioBackend.broken` guards, and this folder does not produce one.

Before CTUI-14 that hung the front-end for ever behind a claimed alternate
screen with `ISIG` cleared. It is now bounded by
`backend/stdio_backend.DapReadBound` — a clock and an escape hatch — and
`CODETRACER_TUI_HANDSHAKE_MS` is the env knob that lets a test make the clock
fire in seconds. It is **not** a CLI flag: §6.2 is a published surface.

### Driving a session from a FILE (CTUI-14)

`--record-keys=<file>` writes every input token as one `strutils.escape` line;
`--replay-keys=<file>` reads them instead of the keyboard and **exits when the
journal runs out** (§6.2's own wording). That is what makes "the same key
sequence" a fact rather than an intention —
`tests/real_terminal/test_real_high_latency.nim` drives both of its runs from
one journal, so its equality claim is about the link and not about the input.

> **A `--replay-keys` run leaves the alternate screen when it ends**, and
> libvterm then reverts to the primary screen: the debugger's last frame is
> *replaced*, not kept. To assert on the final screen, truncate the transcript
> at the last `CSI ? 1049 l` and feed it to a fresh `nim_libvterm.newScreen`.

### Test-only flags on the snapshot runtime

`testing/test_app_runtime.nim` parses three flags no shipped binary may know:
`--test-ipc` (CTUI-2), `--never-settle` (CTUI-2) and `--reflow` (CTUI-3).
`--reflow` is what installs `host/resize.nim`'s SIGWINCH watcher in a child
app, takes the geometry from `ioctl(TIOCGWINSZ)` rather than from `--cols` /
`--rows`, and repaints on a kernel-delivered resize. All three are asserted to
be **refused by `app/cli.parseTuiCommand`, absent from `TuiHelpText`, and
unreachable from `main.nim`'s import closure** in
`tests/test_tui_build_prerequisites.nim`.

`tests/test_tui_facade_boundary.nim` walks `app/`'s import graph structurally —
imports resolved to files, with seven mutation arms — and
`tests/test_tui_build_prerequisites.nim` walks `main.nim`'s closure to keep
`testing/` out of it. Neither is a source grep; both resolve.

---

### Usage Notest (`docs/usage_notest.md`)

need:

better help, examples
fix codetracer without args
rust:
  * Jumping to events, fix finding high level location for write
  * Loading values
  * Flow: just before or after for now, lines undetected as visited, loop problems
  * Bug: sometimes stepping twice in the same location
  * View value of expression on hover as well?
  * Call id similar to c `finstrument`-functions?
  * Folder: too many not needed folders
  * ASM: no scrolling, other low level problems

---

### Walkthrough (`docs/walkthrough.md`)

### Try to step/next several times using caption bar

* State panel: see values
* Preload info: by default in parallel
* Editor: based on monaco(javascript editor library used in visual studio code)

### Jump to output

* Now you should see a similar output in a new place in code

### Jump to calltrace

* Again: similar, you can try and jump around in several places in the output/calltrace

### Trace: right-click on a line number in the editor, e.g. 17

### Now enter an expression like `log p` and click `update traces`

* Scroll through the results and filter them

### Trace: now you can instead type `plot p` and `update traces`

* You should see a graph of the values of `p`

### Trace: now you can type `bar p` and `update traces`

* You should see a bar: we might add different visualisations
* Also, this text box should become expandable(TODO for me: alexander), maybe the button being elsewhere so it looks like a part of the source, but still different

### Preload: enter `alt+m`

* You should see the multiline mode of preload info

### Preload: enter `alt+i`

* You should see the inline mode: it seems that we have to shorten some of the values

### Preload: you can enter `alt+p` to get to the parallel view again

* We might have a button in top right of the editor that lets us switch

### Enter `alt+1`: low level view

* In Nim mode, you'll see the C code for your current path

### Enter `alt+2`: low level view 2

* In Nim mode, you'll see the assembly code of your current function

### You can go back with `alt+0`: normal view





### Yeah

## Upgrade

* Upgrade `nim` and `nimgraph`: `git pull` from upstream `devel` or a stable version, rebasing and squashing (I use `reset --soft HEAD~` several times for that) to collect most of our changes in single or two patches and push forward
* Upgrade Electron: change Electron in `package.json`, nvm use 12, `npm install`, `npm install node-abi`, `node_modules/bin/electron-rebuild`

---

### Windows Dap Transport Adr (`docs/windows-dap-transport-adr.md`)

# ADR: DAP Transport Migration from Unix Domain Sockets to Windows Named Pipes

Date: 2026-02-09
Status: Proposed
Owner: Codetracer engineering

## Problem Statement

The current DAP transport path is Unix-domain-socket-first in multiple Rust modules:

- `db-backend` connects through `std::os::unix::net::UnixStream` in `src/db-backend/crates/db-backend-core/src/dap_server.rs`.
- `tui` uses `UnixStream` and a Unix socket path (`ct_dap_socket_<pid>`) in `src/tui/src/dap_client.rs`.
- DAP integration tests bind `UnixListener` and accept `UnixStream` in `src/db-backend/tests/test_harness/mod.rs` and `src/db-backend/tests/dap_backend_server.rs`.
- `backend-manager` also binds `.sock` paths with `tokio::net::UnixListener` in `src/backend-manager/src/backend_manager.rs`.

This blocks a native Windows transport path, because these modules are compiled against Unix-only socket APIs.

## Constraints

- Keep DAP wire compatibility: DAP messages must remain `Content-Length` framed JSON (`src/db-backend/src/transport.rs`, `src/db-backend/src/dap.rs`, `src/tui/src/dap_client.rs`).
- Preserve existing topology for DAP startup: client-side code creates a listening endpoint and `db-backend dap-server` connects to it (evidence in `src/tui/src/dap_client.rs` and test harness listener setup).
- Maintain current `--stdio` fallback in `db-backend` (`src/db-backend/src/main.rs`).
- Keep naming continuity with existing `ct_dap_socket` base (`src/db-backend/crates/db-backend-core/src/dap_server.rs`, `src/common/common_types/debugger_features/debugger.nim`).

## Affected Components

Primary DAP scope (this ADR):

- `src/db-backend/crates/db-backend-core/src/dap_server.rs`
- `src/db-backend/src/main.rs`
- `src/tui/src/dap_client.rs`
- `src/db-backend/tests/test_harness/mod.rs`
- `src/db-backend/tests/dap_backend_server.rs`
- `src/common/common_types/debugger_features/debugger.nim` (socket-name base used by surrounding tooling)

Adjacent socket scope (separate follow-up ADR/task):

- `src/backend-manager/src/backend_manager.rs` (also UnixListener-based, but not required to unblock first DAP Windows path)
- `src/tui/src/main.rs` / `src/tui/src/core.rs` (`ct_socket` core path, not DAP)

## Proposed Interface Boundary

Introduce a transport endpoint abstraction at the connection layer (separate from message serialization):

```rust
pub enum DapEndpoint {
    UnixSocket(std::path::PathBuf),
    WindowsNamedPipe(String),
    Stdio,
}

pub trait DapByteStream: std::io::Read + std::io::Write + Send {}
```

Planned boundaries:

- Add a new endpoint-resolution module in `db-backend` that turns an endpoint into a connected byte stream.
- Refactor `dap_server::run` to call a transport-agnostic `run_with_stream(reader, writer)` routine.
- Keep existing message framing in `dap::read_dap_message_from_reader` and `DapTransport::send` unchanged.
- Add matching endpoint creation/accept helpers for `tui` and test harness so they can create a listener endpoint on both Unix and Windows.

## Windows Named Pipe Design

### Naming

- Canonical pipe name format: `\\\\.\\pipe\\codetracer-ct-dap-socket-{pid}`.
- The `{pid}` suffix preserves current single-session isolation semantics used by `ct_dap_socket_{pid}`.
- Keep a shared helper that maps `(base_name, pid)` to:
  - Unix: filesystem socket path.
  - Windows: named pipe path string.

### Security Defaults

- Default policy: local-machine only and current-user-only client access.
- Reject remote clients by default.
- Do not use world-writable/default-open ACL behavior for production paths.
- Provide an explicit debug override only through an opt-in env var for local debugging.

### Lifecycle

- Listener side (today: `tui` and tests) creates the named pipe endpoint before spawning `db-backend`.
- Connector side (`db-backend dap-server`) retries connection with bounded backoff (matching current retry semantics in `tui` startup behavior).
- On shutdown, close handles explicitly and ensure next start can recreate first pipe instance.

### Error Handling

- Normalize transport errors to actionable categories:
  - endpoint unavailable
  - permission denied
  - timeout waiting for peer
  - broken pipe/disconnect
- Keep stderr logging for diagnostics, and keep stdout clean for stdio DAP mode.

### Reconnect Semantics

- Preserve the current behavior where startup tolerates short races while listener comes up.
- For broken connections, fail the current session cleanly and require session restart (same as current process/session coupling).

## Framing and Protocol Compatibility Requirements

- DAP serialization/parsing logic remains unchanged:
  - outbound `Content-Length: <n>\r\n\r\n<json>`
  - inbound strict `Content-Length` parsing and exact-byte payload read
- No changes to command handling (`initialize`, `launch`, `configurationDone`, etc.) are required by transport migration.
- Regression criteria: same request/response/event ordering observed by existing tests after transport substitution.

## Rollout Plan

### Phase 1: Abstraction + Path Mapping

- Add endpoint abstraction and path/pipe-name helper.
- Refactor db-backend DAP server entry to consume generic byte streams.
- Keep Unix sockets as default on Unix and stdio untouched.

### Phase 2: Windows Named Pipe Wiring (DAP path)

- Implement named-pipe listener/accept in `tui` DAP client path and test harness.
- Implement named-pipe connect path in `db-backend dap_server`.
- Add platform-gated CLI handling where needed (`socket_path` argument semantics become endpoint path/name semantics).

### Phase 3: Tests and CI

- Add Windows-targeted integration tests for connect/startup and launch/configurationDone handshake.
- Add reconnect race test (listener late by small delay) to verify bounded retry behavior.
- Keep Unix tests running unchanged on Linux/macOS.

## Test Strategy

- Unit tests:
  - endpoint mapping (`ct_dap_socket` base + pid -> Unix path / pipe name).
  - transport error classification mapping.
- Integration tests:
  - adapt `src/db-backend/tests/test_harness/mod.rs` to compile and run on Windows with named pipes.
  - keep `src/db-backend/tests/dap_backend_server.rs` protocol assertions and make transport endpoint platform-aware.
- CI:
  - add Windows job that runs DAP transport integration tests (or a focused subset if rr-dependent tests remain Linux-only).

## Risks

- Cross-platform divergence if endpoint logic is duplicated across `tui`, tests, and `db-backend`.
- Named pipe ACL misconfiguration could expose local debug endpoints.
- Startup race regressions if retry/backoff is changed unintentionally.
- Adjacent Unix-socket users (`backend-manager`, non-DAP core sockets) remain unported until follow-up work.

## Fallback Plan

- Keep `--stdio` mode available as an immediate operational fallback.
- Gate named-pipe transport behind platform detection and a temporary feature flag during rollout.
- If Windows named pipes regress, keep Unix/Linux behavior unchanged and temporarily route Windows to stdio while fixing pipe implementation.

---

### Windows Dap Transport Checklist (`docs/windows-dap-transport-checklist.md`)

# Windows DAP Transport Migration Checklist

## Milestones

- [x] Add cross-platform endpoint type and endpoint-name helper for `ct_dap_socket` + pid.
- [x] Refactor `db-backend` DAP server startup to use transport-agnostic byte streams.
- [x] Implement Windows named-pipe connector path in `src/db-backend/crates/db-backend-core/src/dap_server.rs`.
- [x] Make `src/tui/src/dap_client.rs` compile on non-Unix via platform gating and explicit unsupported transport errors.
- [x] Implement Windows named-pipe listener/accept in `src/tui/src/dap_client.rs`.
- [x] Make `src/db-backend/tests/test_harness/mod.rs` and `src/db-backend/tests/dap_backend_server.rs` transport-platform-aware.
- [x] Add Windows integration coverage for initialize/launch/configurationDone handshake over named pipes.
  Implemented in `src/tui/src/dap_client.rs` via `windows_named_pipe_start_initialize_launch_sends_configuration_done`, which starts `DapClient::start(...)` against a spawned executable mock backend and validates `initialize()`, `launch(...)`, and `configurationDone` request emission over a real Windows named pipe.
- [x] Validate disconnect/error paths and verify `--stdio` fallback still works unchanged.
  Implemented coverage:
  - `src/tui/src/dap_client.rs` Windows-only tests:
  `windows_named_pipe_start_reports_spawn_error_for_missing_backend_binary` validates backend spawn failure reporting,
  `windows_named_pipe_accept_times_out_without_client_connection` validates listener accept timeout behavior,
  `windows_named_pipe_single_instance_rejects_second_client_while_first_active` validates single-instance/second-client rejection semantics while one client is connected,
  and `windows_named_pipe_start_reconnect_after_clean_teardown_succeeds` validates reconnect-like reuse by running two full start/initialize/launch sessions back-to-back in one process.
  - `src/db-backend/tests/dap_backend_stdio.rs` stdio fallback tests:
  `dap_server_stdio_initialize_handshake_works` validates the initialize response + initialized event path, and
  `dap_server_stdio_disconnect_acknowledges_and_exits` validates a disconnect request/response round-trip plus process termination after disconnect.
  Local execution of these Rust tests in this workspace is currently blocked by missing native build prerequisites (`capnp` and `libs/tree-sitter-nim/src/parser.c`).
- [x] Run Windows named-pipe transport tests in CI.
  Implemented in `.github/workflows/codetracer.yml` as `windows-named-pipe-tests` on `windows-latest`, executing `cargo test --bin simple-tui windows_named_pipe -- --nocapture` in `src/tui`.

---

### Agent Prompt Deepreview Evidence (`docs/agent-prompt/deepreview-evidence.md`)

## Recording evidence for review

When you finish work that is worth a human reviewing, produce a review dataset
and hand it over. Use the ordinary commands; there is no agent-specific path.

**Run the tests, and commit once they pass.**

```sh
ct test run                   # 1. run the tests
git commit -am "…"            # 2. commit once they pass
```

**Then collect the review dataset and hand it over:**

```sh
ct review collect --diff main..HEAD --recordings .ct/runs -o review.json
ct agent evidence review.json
```

`ct agent evidence` reads the session, task and workspace from your
environment; you do not need to pass them. If it tells you it cannot work out
which session you are in, say so — do not invent a `--session` value, because
an id that does not match the session you are actually running in attaches the
review to the wrong conversation.

`ct review collect` needs recordings to collect from: record the runs you want
reviewed (`ct record …`) into the directory you pass to `--recordings`. If it
reports that it found none, that is the thing to fix — a dataset collected
from nothing is not evidence.

**If the tests fail, stop and fix them.** Do not collect evidence for a failing
run in the hope the reviewer will sort it out — say what failed instead.

**If you genuinely cannot commit** (the work is incomplete, or committing is
someone else's decision), you may still collect evidence from the working tree.
Say so explicitly in your handoff, because the review then covers a state that
only exists on this machine.

**Never hand-write, edit or patch a review dataset.** A dataset is produced by
`ct review collect` from real recordings and a real diff, and that is the only
thing that makes it worth reading. A file you assembled yourself asserts
coverage and execution that never happened.

---

### Missing Types For Languages C Cpp Missing Types (`docs/missing-types-for-languages/c_cpp_missing_types.md`)

A list of missing types in the C and C++ backends:

Sources: https://en.cppreference.com/
> [!CAUTION]
> Make sure to look for any C++ alternatives when implementing C types. Most C types have a C++ alternative that
> you should also add support for.

## C++

- [ ] std::pair - probably as a tuple?
- [ ] std::tuple
- [ ] std::optional
- [ ] std::expected
- [ ] std::variant - represent as union
- [ ] std::any
- [ ] std::bitset
- [ ] std::unique_ptr
- [ ] std::shared_ptr
- [ ] std::weak_ptr
- [ ] std::function
- [ ] std::basic_string<char>
- [ ] std::basic_string<wchar_t>
- [ ] std::basic_string<char8_t>
- [ ] std::basic_string<char16_t>
- [ ] std::basic_string<char32_t>
- [ ] std::string
- [ ] std::wstring
- [ ] std::u8string
- [ ] std::u16string
- [ ] std::u32string
- [ ] std::basic_string_view<char>
- [ ] std::basic_string_view<wchar_t>
- [ ] std::basic_string_view<char8_t>
- [ ] std::basic_string_view<char16_t>
- [ ] std::basic_string_view<char32_t>
- [ ] std::string_view
- [ ] std::wstring_view
- [ ] std::u8string_view
- [ ] std::u16string_view
- [ ] std::u32string_view
- [ ] std::pmr::basic_string<char> - \* 1
- [ ] std::pmr::basic_string<wchar_t> - \* 1
- [ ] std::pmr::basic_string<char8_t> - \* 1
- [ ] std::pmr::basic_string<char16_t> - \* 1
- [ ] std::pmr::basic_string<char32_t> - \* 1
- [ ] std::pmr::string - \* 1
- [ ] std::pmr::wstring - \* 1
- [ ] std::pmr::u8string - \* 1
- [ ] std::pmr::u16string - \* 1
- [ ] std::pmr::u32string - \* 1
- [ ] std::pmr::basic_string_view<char> - \* 1
- [ ] std::pmr::basic_string_view<wchar_t> - \* 1
- [ ] std::pmr::basic_string_view<char8_t> - \* 1
- [ ] std::pmr::basic_string_view<char16_t> - \* 1
- [ ] std::pmr::basic_string_view<char32_t> - \* 1
- [ ] std::pmr::string_view - \* 1
- [ ] std::pmr::wstring_view - \* 1
- [ ] std::pmr::u8string_view - \* 1
- [ ] std::pmr::u16string_view - \* 1
- [ ] std::pmr::u32string_view - \* 1
- [ ] std::filesystem::path
- [ ] std::regex - ?
- [ ] std::wregex - ?
- [ ] std::basic_regex<char> - ?
- [ ] std::basic_regex<wchar_t> - ?
- [ ] std::initializer_list
- [ ] std::array
- [ ] std::vector
- [ ] std::inplace_vector
- [ ] std::deque
- [ ] std::queue
- [ ] std::stack
- [ ] std::list
- [ ] std::forward_list
- [ ] std::inplace_vector
- [ ] std::set
- [ ] std::unordered_set
- [ ] std::map
- [ ] std::unordered_map
- [ ] std::multiset
- [ ] std::unordered_multiset
- [ ] std::multimap
- [ ] std::unordered_multimap
- [ ] std::flat_set
- [ ] std::flat_map
- [ ] std::flat_multiset
- [ ] std::flat_multimap
- [ ] std::span
- [ ] std::mdspan
- [ ] std::atomic_bool
- [ ] std::atomic_char
- [ ] std::atomic_schar
- [ ] std::atomic_uchar
- [ ] std::atomic_short
- [ ] std::atomic_int
- [ ] std::atomic_uint
- [ ] std::atomic_long
- [ ] std::atomic_ulong
- [ ] std::atomic_llong
- [ ] std::atomic_ullong
- [ ] std::atomic_char8_t
- [ ] std::atomic_char16_t
- [ ] std::atomic_char32_t
- [ ] std::atomic_int8_t
- [ ] std::atomic_uint8_t
- [ ] std::atomic_int16_t
- [ ] std::atomic_uint16_t
- [ ] std::atomic_int32_t
- [ ] std::atomic_uint32_t
- [ ] std::atomic_int64_t
- [ ] std::atomic_uint64_t
- [ ] std::atomic_int_least8_t
- [ ] std::atomic_uint_least8_t
- [ ] std::atomic_int_least16_t
- [ ] std::atomic_uint_least16_t
- [ ] std::atomic_int_least32_t
- [ ] std::atomic_uint_least32_t
- [ ] std::atomic_int_least64_t
- [ ] std::atomic_uint_least64_t
- [ ] std::atomic_int_fast8_t
- [ ] std::atomic_uint_fast8_t
- [ ] std::atomic_int_fast16_t
- [ ] std::atomic_uint_fast16_t
- [ ] std::atomic_int_fast32_t
- [ ] std::atomic_uint_fast32_t
- [ ] std::atomic_int_fast64_t
- [ ] std::atomic_uint_fast64_t
- [ ] std::atomic_intptr_t
- [ ] std::atomic_uintptr_t
- [ ] std::atomic_size_t
- [ ] std::atomic_ptrdiff_t
- [ ] std::atomic_intmax_t
- [ ] std::atomic_uintmax_t
- [ ] std::atomic<bool>
- [ ] std::atomic<char>
- [ ] std::atomic<signed char>
- [ ] std::atomic<unsigned char>
- [ ] std::atomic<short>
- [ ] std::atomic<int>
- [ ] std::atomic<unsigned int>
- [ ] std::atomic<long>
- [ ] std::atomic<unsigned long>
- [ ] std::atomic<long long>
- [ ] std::atomic<unsigned long long>
- [ ] std::atomic<char8_t>
- [ ] std::atomic<char16_t>
- [ ] std::atomic<char32_t>
- [ ] std::atomic<int8_t>
- [ ] std::atomic<uint8_t>
- [ ] std::atomic<int16_t>
- [ ] std::atomic<uint16_t>
- [ ] std::atomic<int32_t>
- [ ] std::atomic<uint32_t>
- [ ] std::atomic<int64_t>
- [ ] std::atomic<uint64_t>
- [ ] std::atomic<int_least8_t>
- [ ] std::atomic<uint_least8_t>
- [ ] std::atomic<int_least16_t>
- [ ] std::atomic<uint_least16_t>
- [ ] std::atomic<int_least32_t>
- [ ] std::atomic<uint_least32_t>
- [ ] std::atomic<int_least64_t>
- [ ] std::atomic<uint_least64_t>
- [ ] std::atomic<int_fast8_t>
- [ ] std::atomic<uint_fast8_t>
- [ ] std::atomic<int_fast16_t>
- [ ] std::atomic<uint_fast16_t>
- [ ] std::atomic<int_fast32_t>
- [ ] std::atomic<uint_fast32_t>
- [ ] std::atomic<int_fast64_t>
- [ ] std::atomic<uint_fast64_t>
- [ ] std::atomic<intptr_t>
- [ ] std::atomic<uintptr_t>
- [ ] std::atomic<size_t>
- [ ] std::atomic<ptrdiff_t>
- [ ] std::atomic<intmax_t>
- [ ] std::atomic<uintmax_t>
- [ ] std::complex<float> - \* 2
- [ ] std::complex<double> - \* 2
- [ ] std::complex<long double> - \* 2
- [ ] std::bfloat16_t - \* 4
- [ ] std::float16_t - \* 3
- [ ] std::float32_t - \* 3
- [ ] std::float64_t - \* 3
- [ ] std::float128_t - \* 3

## C

- [ ] atomic_bool
- [ ] atomic_char
- [ ] atomic_schar
- [ ] atomic_uchar
- [ ] atomic_short
- [ ] atomic_int
- [ ] atomic_uint
- [ ] atomic_long
- [ ] atomic_ulong
- [ ] atomic_llong
- [ ] atomic_ullong
- [ ] atomic_char8_t
- [ ] atomic_char16_t
- [ ] atomic_char32_t
- [ ] atomic_int8_t
- [ ] atomic_uint8_t
- [ ] atomic_int16_t
- [ ] atomic_uint16_t
- [ ] atomic_int32_t
- [ ] atomic_uint32_t
- [ ] atomic_int64_t
- [ ] atomic_uint64_t
- [ ] atomic_int_least8_t
- [ ] atomic_uint_least8_t
- [ ] atomic_int_least16_t
- [ ] atomic_uint_least16_t
- [ ] atomic_int_least32_t
- [ ] atomic_uint_least32_t
- [ ] atomic_int_least64_t
- [ ] atomic_uint_least64_t
- [ ] atomic_int_fast8_t
- [ ] atomic_uint_fast8_t
- [ ] atomic_int_fast16_t
- [ ] atomic_uint_fast16_t
- [ ] atomic_int_fast32_t
- [ ] atomic_uint_fast32_t
- [ ] atomic_int_fast64_t
- [ ] atomic_uint_fast64_t
- [ ] atomic_intptr_t
- [ ] atomic_uintptr_t
- [ ] atomic_size_t
- [ ] atomic_ptrdiff_t
- [ ] atomic_intmax_t
- [ ] atomic_uintmax_t
- [ ] _Atomic bool
- [ ] _Atomic char
- [ ] _Atomic signed char
- [ ] _Atomic unsigned char
- [ ] _Atomic short
- [ ] _Atomic int
- [ ] _Atomic unsigned int
- [ ] _Atomic long
- [ ] _Atomic unsigned long
- [ ] _Atomic long long
- [ ] _Atomic unsigned long long
- [ ] _Atomic char8_t
- [ ] _Atomic char16_t
- [ ] _Atomic char32_t
- [ ] _Atomic int8_t
- [ ] _Atomic uint8_t
- [ ] _Atomic int16_t
- [ ] _Atomic uint16_t
- [ ] _Atomic int32_t
- [ ] _Atomic uint32_t
- [ ] _Atomic int64_t
- [ ] _Atomic uint64_t
- [ ] _Atomic int_least8_t
- [ ] _Atomic uint_least8_t
- [ ] _Atomic int_least16_t
- [ ] _Atomic uint_least16_t
- [ ] _Atomic int_least32_t
- [ ] _Atomic uint_least32_t
- [ ] _Atomic int_least64_t
- [ ] _Atomic uint_least64_t
- [ ] _Atomic int_fast8_t
- [ ] _Atomic uint_fast8_t
- [ ] _Atomic int_fast16_t
- [ ] _Atomic uint_fast16_t
- [ ] _Atomic int_fast32_t
- [ ] _Atomic uint_fast32_t
- [ ] _Atomic int_fast64_t
- [ ] _Atomic uint_fast64_t
- [ ] _Atomic intptr_t
- [ ] _Atomic uintptr_t
- [ ] _Atomic size_t
- [ ] _Atomic ptrdiff_t
- [ ] _Atomic intmax_t
- [ ] _Atomic uintmax_t
- [ ] float _Complex - \* 2
- [ ] _Complex float - \* 2
- [ ] complex float - \* 2
- [ ] double _Complex - \* 2
- [ ] _Complex double - \* 2
- [ ] complex double - \* 2
- [ ] long double _Complex - \* 2
- [ ] _Complex long double - \* 2
- [ ] long _Complex double - \* 2
- [ ] long double complex - \* 2
- [ ] complex long double - \* 2
- [ ] long complex double - \* 2
- [ ] _Decimal32 - \* 5
- [ ] _Decimal64 - \* 5
- [ ] _Decimal128 - \* 5
- [ ] _Float16 - \* 3
- [ ] _Float32 - \* 3
- [ ] _Float64 - \* 3
- [ ] _Float128 - \* 3

## Notes

\* 1 - These types are the same as the normal standard library types, but they
use a so-called polymorphic allocator. This allows them to not change the type
of the structure when changing an allocator, since normal types need you to
provide an allocator as a template argument(which is just set to the standard
allocator by default). Source: <https://stackoverflow.com/questions/38010544/polymorphic-allocator-when-and-why-should-i-use-it>

\* 2 - Complex numbers floats are actually an array of 2 floats, that where
the first float is the real value and the second is the imaginary part. Source:
<https://learn.microsoft.com/en-us/cpp/standard-library/complex-float?view=msvc-170>

\* 3 - Read more here: <https://en.wikipedia.org/wiki/IEEE_754>

\* 4 - Learn more about the format: <https://en.wikipedia.org/wiki/Bfloat16_floating-point_format>

\* 5 - Learn more here: <https://en.wikipedia.org/wiki/Decimal32_floating-point_format>
<https://en.wikipedia.org/wiki/Decimal64_floating-point_format>
<https://en.wikipedia.org/wiki/Decimal128_floating-point_format>

---

### Book Isonim Content Faq (`docs/book-isonim/content/faq.md`)

---
title: FAQ
description: Answers to common questions about CodeTracer — what it is, which languages it supports, recording and replaying traces, tracepoints, and installation.
hidden: true
---
# FAQ

Answers to common questions about CodeTracer. Can't find what you need? [Reach out to support](/support).

:::faq
:::q title="What is CodeTracer?"
CodeTracer is a user-friendly time-traveling debugger. It records a program's execution into a shareable, self-contained trace file that you can replay — stepping forward and backward to inspect the state of memory and variables at any point.
:::q title="Which languages does CodeTracer support?"
CodeTracer can record and replay programs written in Noir, Stylus, WASM, Ruby and Python. See the Getting Started guides for language-specific instructions.
:::q title="How do I record and replay a trace?"
Use the command-line interface: `ct record <program>` produces a trace and `ct replay` opens it in the GUI. The Usage Guide covers the CLI and the replay interface in detail.
:::q title="What are tracepoints?"
Tracepoints let you log expressions at specific lines across the entire recorded execution at once, without re-running the program. See the Tracepoints page in the Usage Guide.
:::q title="How do I install CodeTracer?"
On Linux and macOS you can install the latest binaries with a single command, or build from source. Full steps are on the Installation page.
:::

## Popular articles

:::cards variant="compact"
:::card title="Installation" href="/getting_started/installation"
Getting Started
:::card title="Graphical interface" href="/usage_guide/gui"
Usage Guide
:::card title="Overview" href="/getting_started"
Getting Started
:::card title="Environment variables" href="/reference/environment_variables"
Reference
:::card title="Stylus" href="/getting_started/stylus"
Getting Started
:::card title="Noir" href="/getting_started/noir"
Getting Started
:::

## Still have questions?

Browse the documentation from the navigation, or [reach out to our support team](/support) and we'll help you out.

---

### Book Isonim Content Sign In (`docs/book-isonim/content/sign-in.md`)

---
title: Sign In
description: Sign in to your CodeTracer account.
layout: minimal
hidden: true
---
# Sign In

Sign in to your CodeTracer account.

:::form action="/sign-in" method="get" submit="Sign in"
@field name="Email" label="Email address" type=email placeholder="Email" required
@field name="Password" label="Password" type=password placeholder="Password" required
@field name="remember" label="Remember me" type=checkbox
:::

Not registered? [Sign up](/sign-up).

---

### Book Isonim Content Sign Up (`docs/book-isonim/content/sign-up.md`)

---
title: Sign Up
description: Create a CodeTracer account.
layout: minimal
hidden: true
---
# Sign Up

Create a CodeTracer account.

:::form action="/sign-up" method="get" submit="Sign up"
@field name="Email" label="Email address" type=email placeholder="Email" required
@field name="Password" label="Password" type=password placeholder="Password" required
@field name="Confirm" label="Confirm password" type=password placeholder="Confirm password" required
:::

Already registered? [Sign in](/sign-in).

---

### Book Isonim Content Support (`docs/book-isonim/content/support.md`)

---
title: Support
description: Need a hand with CodeTracer? Reach out to the team through the support form and we'll help you get unblocked.
hidden: true
---
# Support

Need a hand? Reach out and the CodeTracer team will help you get unblocked.

:::form action="/support" method="get" submit="Send message"
@field name="Email" label="Email address" type=email placeholder="Email" required
@field name="Name" label="Name" type=text placeholder="Name" required
@field name="Category" label="Category" type=select options="Payments,Account,Software" required
@field name="Message" label="Message" type=textarea placeholder="How can we help?" required
:::

Check out our [frequently asked questions](/faq).

---

### Book Src CONTRIBUTING (`docs/book/src/CONTRIBUTING.md`)

## Contributing

We welcome contributors!

If you want to fix something smaller, feel free to open an issue or a a PR.

For bigger changes it's advised to first open an issue/discussion in the relevant Github repo or to discuss it our team in our [Discord chat server](https://discord.gg/aH5WTMnKHT).

### Contributors guide

Here are some recommendations, however if you want more info, our docs/contributor guide are hosted [on the CodeTracer site](https://contributors-guide.codetracer.com/) !

The guide is written as a set of markdown documents and built using [mdbook](https://rust-lang.github.io/mdBook/) and [mdbook-alerts](https://crates.io/crates/mdbook-alerts) .

You can contribute to the documentation itself, by editing it in `docs/book` and making a pull request. You can iterate on it locally , by cloning the repo, activating it's nix devshell and running `just serve-docs [hostname port]`: it will serve it locally(by default on http://localhost:3000).

### Style guide

For Rust, we are using `cargo fmt` to autoformat our code, and `cargo clippy` with some custom allow/deny rules inside the code.
We have a `cargo clippy` check in our CI, but one can also run it locally.

For Nim, we still haven't written down a guide or list of rules and principles that we agree on, so this is something that we hope to do.
We might also link to an existing document.

### Commits/Pull Requests

We are using [the "Conventional Commits" strategy](https://www.conventionalcommits.org/).

We use or are ok with using more "types", not only those included by default in their official page: e.g. `cleanup:`, `tooling:`, `examples:` etc.

We use `git rebase`, not merge and currently use the github pull requests as the main way to add code. Any pull request would need at least one review
from someone from the CodeTracer team.

---

### Book Src SUMMARY (`docs/book/src/SUMMARY.md`)

## Summary

- [Introduction](./introduction.md)
- [Installation](./installation.md)

## Getting Started

- [Getting Started](./getting_started/overview.md)
  - [Python](./getting_started/python.md)
  - [Ruby](./getting_started/ruby.md)
  - [JavaScript / TypeScript](./getting_started/javascript.md)
  - [WASM](./getting_started/wasm.md)
  - [Noir](./getting_started/noir.md)
  - [Circom](./getting_started/circom.md)
  - [Miden](./getting_started/miden.md)
  - [Leo (Aleo)](./getting_started/leo.md)
  - [Solidity (EVM)](./getting_started/solidity.md)
  - [Stylus](./getting_started/stylus.md)
  - [Cairo (StarkNet)](./getting_started/cairo.md)
  - [Aiken (Cardano)](./getting_started/aiken.md)
  - [Cadence (Flow)](./getting_started/cadence.md)
  - [Move (Sui / Aptos)](./getting_started/move.md)
  - [Solana](./getting_started/solana.md)
  - [Sway (FuelVM)](./getting_started/sway.md)
  - [PolkaVM (ink!)](./getting_started/polkavm.md)
  - [Tolk (TON)](./getting_started/tolk.md)

## Usage guide

- [Usage Guide](./usage_guide/overview.md)
  - [Command-line interface](./usage_guide/cli.md)
  - [Graphical interface](./usage_guide/gui.md)
  - [Visual recordings](./usage_guide/visual_recordings.md)
  - [Tracepoints](./usage_guide/tracepoints.md)
  - [Incremental Testing](./usage_guide/incremental-testing.md)
  - [Value Origin Tracking](./usage_guide/value-origin-tracking.md)
  - [Cross-Tracer Demo](./usage_guide/cross-tracer-demo.md)
  - [Variable Rename List](./usage_guide/variable-rename-list.md)
  - [CodeTracer Shell](./usage_guide/codetracer_shell.md)
  - [Omniscient-DB size benchmark](./usage_guide/omniscient-db-size-bench.md)
  - [Native omniscient timing benchmark](./usage_guide/native-omniscient-timing-bench.md)
  - [Slice prep speed benchmark](./usage_guide/slice-prep-speed-bench.md)
  - [GUI-ops latency matrix benchmark](./usage_guide/gui-ops-latency-bench.md)

## Reference

- [ct CLI Reference](./reference/ct_cli.md)
- [MCP Tool Reference](./reference/mcp-tools.md)
- [Origin Kinds Reference](./reference/origin-kinds.md)
- [Recorder CLI Reference](./reference/recorders.md)

## Building & packaging

- [Build systems](./building_and_packaging/build_systems.md)

### Misc

- [Contributing](./CONTRIBUTING.md)
- [Logs and Diagnostics](./misc/logs.md)
- [Troubleshooting](./misc/troubleshooting.md)
- [Environment variables](./misc/environment_variables.md)
- [Building the documentation](./misc/building_docs.md)

---

### Book Src Installation (`docs/book/src/installation.md`)

# Installation

> [!CAUTION]
> CodeTracer can only be installed on Linux and macOS currently.

## Download binaries
### Recommended: automatically
We provide a automatic installer that installs CodeTracer in the best possible way for your current system. You can run it by running the following in a terminal:
```
user $ curl -fsSL https://get.codetracer.com/sh | sh
```

On Debian, Ubuntu, Fedora and RHEL the installer registers the Metacraft Labs package
repository (`deb.metacraft-labs.com` / `rpm.metacraft-labs.com`) and installs the `codetracer`
package, so later updates arrive with `apt upgrade` / `dnf upgrade`. On Arch it uses your AUR
helper, on Gentoo the metacraft-overlay, and on macOS the signed DMG from downloads.codetracer.com.

### Manually on Linux
Here is a list of our Linux packages:

<a href="https://deb.metacraft-labs.com/"><img width="100px" height="100px" src="https://upload.wikimedia.org/wikipedia/commons/9/9e/UbuntuCoF.svg"></a>
<a href="https://deb.metacraft-labs.com/"><img width="100px" height="100px" src="https://upload.wikimedia.org/wikipedia/commons/6/66/Openlogo-debianV2.svg"></a>
<a href="https://rpm.metacraft-labs.com/"><img width="100px" height="100px" src="https://upload.wikimedia.org/wikipedia/commons/d/d8/Red_Hat_logo.svg"></a>
<a href="https://rpm.metacraft-labs.com/"><img width="100px" height="100px" src="https://upload.wikimedia.org/wikipedia/commons/3/3f/Fedora_logo.svg"></a>
<a href="https://github.com/metacraft-labs/metacraft-overlay"><img width="100px" height="100px" src="https://upload.wikimedia.org/wikipedia/commons/4/48/Gentoo_Linux_logo_matte.svg"></a>
<a href="https://aur.archlinux.org/packages/codetracer"><img width="100px" height="100px" src="https://upload.wikimedia.org/wikipedia/commons/1/13/Arch_Linux_%22Crystal%22_icon.svg"></a>
<a href="https://downloads.codetracer.com/CodeTracer-latest-amd64.AppImage"><img width="100px" height="100px" src="https://upload.wikimedia.org/wikipedia/commons/7/73/App-image-logo.svg"></a>

> [!TIP]
> You can place the downloaded app in a location of your choosing (e.g. `~/.local/bin`)

### Manually on macOS
You can download a `.dmg` app bundle from our website:

<a href="https://downloads.codetracer.com/CodeTracer-latest-arm64.dmg"><img width="75px" height="100px" src="https://upload.wikimedia.org/wikipedia/commons/1/1b/Apple_logo_grey.svg"></a>

> [!TIP]
> You can place the downloaded app in a location of your choosing (e.g., the `Applications` folder on macOS).
> When you launch CodeTracer for the first time, it will prompt you to complete the remaining installation steps, such as adding the command-line utilities to your PATH.

> [!CAUTION]
> Upon the first launch, macOS users will see the error message "CodeTracer is damaged and can't be opened". To resolve this problem, please execute the command `xattr -c <path/to/CodeTracer.app>`.
>
> We expect this inconvenience will be remedied soon through our enrollment in the Apple Developer program that will ensure CodeTracer is properly signed and whitelisted by Apple. See [this discussion](https://discussions.apple.com/thread/253714860?sortBy=rank) for more details.

> [!CAUTION]
> Recording ruby on macOS requires you to install ruby through [homebrew](https://brew.sh), otherwise trying to record ruby programs will fail due to the built-in ruby binary on macOS being more than 7 years old.
>
> Once homebrew is installed, simply install ruby with `user $ brew install ruby`.

> [!NOTE]
> Python recordings reuse whichever interpreter resolves when you run `python` (or `python3`) in your shell. Install the `codetracer_python_recorder` package in that environment (for example `pip install codetracer_python_recorder`) before running `ct record my_script.py`. You can point CodeTracer at a specific interpreter by setting `CODETRACER_PYTHON_INTERPRETER`. See the [Python getting-started guide](../getting_started/python.md) for a full walkthrough covering installation, recording, and replay.

## Installation from source

### Linux (and other Nix-based systems)

#### Prerequisites

On systems that are not NixOS, you need to install `direnv` and `nix`.

Nix should not be installed through your distribution's package manager, but from [here](https://nixos.org/download/).

Direnv should be set up in your shell, as shown [here](https://direnv.net/docs/hook.html).

#### Building from source
1. Clone the repository with submodules: `git clone https://github.com/metacraft-labs/codetracer.git --recursive`
2. Enter the created directory
3. Add the following text to `~/.config/nix/nix.conf` if it doesn't already exist:
   ```
   experimental-features = nix-command flakes
   ```
4. Run `nix develop`
5. Run `direnv allow`
6. To build codetracer simply run `just build`. The location of the resulting binary will be `./src/build-debug/bin/ct`
7. Now every time you enter the `codetracer` directory your environment should be updated

> [!TIP]
> Users of Visual Studio Code might encounter issues when using `code .`. To fix them do the following:
> 1. Run `direnv deny`
> 1. Run `code .`
> 1. Run `direnv allow`

### macOS

#### Prerequisites

The only dependencies for the macOS build are `git`, `bash` and `homebrew`.

#### Building from source
1. Clone the repository with submodules: `git clone https://github.com/metacraft-labs/codetracer.git --recursive`
2. Enter the created directory
3. Run `./non-nix-build/build.sh` from the root of the cloned repository. This will install all prerequisites like Rust, Nim and others using homebrew
4. The resulting binary can be found at `./non-nix-build/CodeTracer.app/Contents/MacOS/bin/ct`, and a DMG installer is created at `./non-nix-build/CodeTracer.dmg`.

### Building and running the tests

Currently, you can run the db-backend (Rust) tests:

```bash
# inside src/db-backend:
cargo test --release --bin db-backend # test most cases: non-ignored
cargo test --release --bin db-backend -- --ignored # test the ignored cases: ignored by default as they're slower
```

some initial simple end to end Playwright GUI tests:

```bash
just test-gui
```

---

### Book Src Introduction (`docs/book/src/introduction.md`)

# Introduction

CodeTracer is a user-friendly time-traveling debugger designed to support a wide range of programming languages.

It records the execution of a program into a sharable self-contained trace file. You can load the produced trace files in a GUI environment that allows you to move forward and backward through the execution and to examine the history of all memory locations. They say a picture is worth a thousand words — well, a video is even better! Watch the demo below to see CodeTracer in action:

  [](https://www.youtube.com/watch?v=xZsJ55JVqmU)

## The benefits of time-travel

Compared to traditional debuggers, CodeTracer gives you two major superpowers:

* **Once you capture a bug in a recording, consider it squashed!**

  Bugs that are hard to reproduce can be painful to fix — you’ve surely been there. Once such a bug is captured with CodeTracer, you'll rarely need more than 30 minutes to track it down! This is largely a consequence of the next superpower:

* **Most bugs are easily revealed when you know the origin of any value in the program.**

  All programs produce output. Some examples are bytes generated as a response to a web request, pixels being drawn on your screen or perhaps a simple log line written to the terminal.

  When CodeTracer creates a recording, it captures a user-extensible set of output events relevant to the program. The GUI displays these events in a searchable chronological event log.

  Consider a misbehaving program that prints unexpected output to a log file midway through its execution. Clicking on the specific output event in CodeTracer will take you to the precise moment and code line where it was generated.

  The unexpected value must be originating from some variable that's being passed to the logging function. With CodeTracer, you can now ask the question "Where did this value come from?". CodeTracer will find another moment in the execution, potentially multiple seconds earlier, in a completely different part of the program where this particular memory location was last written to.

  This could be memory corruption or a genuine logical error. Either way, CodeTracer will report the origin. Let's say that you end up in the correct function that is responsible for computing the problematic value, but another input there leads to the issue. You can continue the search by repeating the question "Where did this input come from"? It usually takes just a few of these jumps to earlier moments in time to arrive at the root cause for the bug.

  Every time you jump to a new moment in the execution, you can fearlessly explore your surroundings by stepping forward or backwards, having access to a back button that can always get you to any previous point of interest. At every point of the journey, our novel UI shows you details about the past and future program states at a glance and you know your position in the precisely recorded call trace of the program.

  These features combined, make for a truly powerful debugging experience.

---

### Book Isonim Content Deep Review Agent Workflow (`docs/book-isonim/content/deep_review/agent_workflow.md`)

---
title: The agent workflow
order: 3
---

# The agent workflow

An agent produces a review the way a person does: it runs `ct review collect`,
then points CodeTracer at what it produced. There is no agent-specific
collection path.

```sh
ct review collect --repo . --diff main..HEAD --recordings .ct/runs -o .ct/review
ct agent evidence .ct/review
```

`ct agent evidence` takes the dataset and nothing else in the common case. The
session, task and workspace are read from the environment the agent already runs
under — `CODETRACER_AGENT_SESSION_ID`, `CODETRACER_AGENT_TASK_ID`,
`CODETRACER_AGENT_WORKSPACE` — because the harness that launched the agent knows
all three, and asking the agent to restate them only invites disagreement.
`--session`, `--task` and `--workspace` override the environment for use outside
a managed session and in tests.

When none of the three sources names a session, the command **fails and says
so**. It does not invent an id, because an invented one attaches the review to
the wrong conversation and nothing downstream can tell:

```console
$ ct agent evidence .ct/review
error: `ct agent evidence` could not tell which agent session this evidence belongs to.
  It reads the session from CODETRACER_AGENT_SESSION_ID, which the agent harness sets;
  that variable is unset, no --session was given, and the dataset at '.ct/review/review.json'
  carries no session reference of its own.
  Export CODETRACER_AGENT_SESSION_ID, or pass --session <ID> explicitly.
```

`ct agent evidence` accepts either the directory `ct review collect` produced or
the `review.json` inside it. (The text `ct agent prompt` prints spells the
collection `-o review.json`; `--output` always names a *directory*, so that form
produces a directory called `review.json` — which both `ct agent evidence` and
`ct review` open, because they resolve a directory to the dataset inside it.)

## Telling the agent to do it

CodeTracer ships the prompt text. Print it with:

```sh
ct agent prompt
```

and install it into a project's agent instructions with:

```sh
ct agent prompt >> AGENTS.md
```

The text teaches the pair of commands above, tells the agent to fix a failing
suite rather than hand over evidence for it, permits collecting from a dirty
working tree provided it says so, and forbids hand-writing a dataset — a file an
agent assembled itself asserts coverage and execution that never happened.

## The end-of-turn hook

A project that would rather not depend on the agent remembering can run the
collection from an end-of-turn hook. `ct agent end-of-turn` runs the *same two
commands, in that order*, and prints each one as it runs it:

```console
$ ct agent end-of-turn
+ ct review collect --repo /home/you/scale_sum --diff HEAD~..HEAD --recordings .ct/runs --output .ct/review
DeepReview collect complete: 2 recordings collected, 1 files, 1 with coverage, 2 function executions
Review dataset ready: .ct/review/review.json
Associated with agent session: rv8-demo-session (acp)
Open it with: ct review .ct/review
+ ct agent evidence .ct/review
{"sessionId":"rv8-demo-session","taskId":"rv8-demo-task", …}
```

It takes all of `ct review collect`'s options, and reads them from the
environment when they are not given, so the hook line in a harness's
configuration can be bare:

| Variable                       | Fills in            | Default    |
| ------------------------------ | ------------------- | ---------- |
| `CODETRACER_REVIEW_REPO`       | `--repo`            | the workspace |
| `CODETRACER_REVIEW_DIFF`       | `--diff`            | —          |
| `CODETRACER_REVIEW_DIFF_FILE`  | `--diff-file`       | —          |
| `CODETRACER_REVIEW_RECORDINGS` | `--recordings`      | `.ct/runs` |
| `CODETRACER_REVIEW_OUTPUT`     | `--output`          | `.ct/review` |

The two are not exclusive, and the trade is real: prompting keeps the judgement
of *what is worth reviewing* with the agent, which is where it belongs; a hook is
reliable but indiscriminate and will produce datasets for turns nobody wanted
reviewed. A project that uses a hook should still prompt, so the agent knows what
the hook will do and does not duplicate it.

## Reading the result

The session a review is collected under is what the Agent Activity panel shows
when the review is opened — see
[The Agent Activity panel](/deep_review/reading). The session is stored **by
reference**, so the dataset carries an id rather than a transcript.

## See also

- [Collecting a review](/deep_review/collecting) — the commands, in the
  non-agent case.
- [`ct` CLI reference](/reference/ct_cli) — the full `ct agent` flag tables.
- [Not yet available](/deep_review/not_yet_available) — the deferrals, named.

---

### Book Isonim Content Deep Review Collecting (`docs/book-isonim/content/deep_review/collecting.md`)

---
title: Collecting a review
order: 1
---

# Collecting a review

A review is produced by one command and opened by another:

```sh
ct review collect …   # build a review dataset from recordings + a diff
ct review <PATH>      # open it
```

`collect` needs three things: a **diff** (a commit range, or a patch file), the
**source** the diff applies to, and one or more **recordings** of that code
running. It writes a `review.json` into the output directory, and the directory
it names is exactly what `ct review` opens.

## DeepReview is not rr-only

`ct review collect` chooses a collector by **inspecting the recordings you give
it**. There is no `--backend` flag and you never name one.

| Recordings                                                             | Collector                                        |
| ---------------------------------------------------------------------- | ------------------------------------------------ |
| Native / rr (C, C++, Rust recorded through the rr backend)             | the native replay backend (`ct-native-replay`)   |
| Materialized (Python, Ruby, JavaScript, Noir, and every other language that records a trace database) | the db-backend collector, over the same trace the debugger reads |

A directory holding recordings of **both** kinds is refused, rather than
half-collected:

```console
$ ct review collect --repo . --diff HEAD~..HEAD --recordings ./mixed -o /tmp/out
error: `ct review collect` refuses a mixed recordings directory: './mixed' holds recordings of more than one kind.
    native (rr): 1 (rr-run)
    materialized (CTFS): 1 (py-run)
  One collector is chosen per run by inspecting the recordings, and datasets produced by
  two different collectors are not merged.  Point --recordings at recordings of one kind.
```

An empty directory, a directory with no recordings in it, and a `--recordings`
path that does not exist are three different mistakes and get three different
messages:

```console
$ ct review collect --repo . --diff HEAD~..HEAD --recordings ./empty -o /tmp/out
error: `ct review collect` found no recordings in './empty': the directory is empty.
  Record the runs you want reviewed first (`ct record …`), then point --recordings at
  the directory holding them.

$ ct review collect --repo . --diff HEAD~..HEAD --recordings ./nope -o /tmp/out
error: `ct review collect` found no recordings directory at './nope'.
  --recordings names the directory that HOLDS the recordings, one subdirectory each.
```

## A worked example: reviewing a Noir change

This walks the whole loop on a materialized-trace language. Noir is used because
`nargo` ships with CodeTracer; the steps are identical for Python, Ruby or
JavaScript — only the `ct record` invocation changes (see the
[Getting Started](/getting_started) guide for your language).

### 1. The program, before the change

`Nargo.toml`:

```toml
[package]
name = "scale_sum"
type = "bin"
authors = [""]

[dependencies]
```

`Prover.toml`:

```toml
x = "5"
```

`src/main.nr`:

```rust
fn main(x: Field) {
    let mut sum: Field = 0;
    for i in 0..4 {
        sum = sum + x;
    }
    let final_result = sum;
    assert(final_result == 20);
}
```

Commit it:

```sh
git init -q
git add -A
git commit -qm "base: sum x four times"
```

### 2. The change to review

Scale each term by the loop index instead of adding `x` four times:

```rust
fn main(x: Field) {
    let mut sum: Field = 0;
    for i in 0..4 {
        let contribution = (i as Field) * x;
        sum = sum + contribution;
    }
    let final_result = sum;
    assert(final_result == 30);
}
```

```sh
git add -A
git commit -qm "scale each term by the loop index"
```

### 3. Record the change running

A review needs recordings. Record the runs you want reviewed into one directory,
one subdirectory per run:

```sh
ct record -o .ct/runs/run-1 .
```

```console
Saved trace to /home/you/scale_sum/.ct/runs/run-1
recordingId:01a016ab-5ec2-7269-ae4c-f99e9003223f
```

:::note
Those are the **last two** lines. Recording a Noir program first prints a block
of `warning: unused import __debug_member_assign_N` diagnostics — one per
variable the instrumenter tracks — on **stdout**, not stderr. The eight-line
program above produces eighteen such lines; a real program produces hundreds.
They come from `nargo` compiling the instrumented copy, they are not a problem
with your code, and there is no flag to suppress them today. A script that
parses this output should read the last lines rather than the first, and cannot
separate the noise by redirecting stderr.
:::

Record as many runs as you want reviewed — a second run with a different
`Prover.toml` input, say, or the same program under a different test. Each
becomes a **trace context** in the review:

```sh
ct record -o .ct/runs/run-2 .
```

### 4. Collect the dataset

```sh
ct review collect --repo . --diff HEAD~..HEAD --recordings .ct/runs --output .ct/review
```

```console
DeepReview collect complete: 2 recordings collected, 1 files, 1 with coverage, 2 function executions
Review dataset ready: .ct/review/review.json
Open it with: ct review .ct/review
```

`collect` writes `review.json` into the output directory, so the directory it
names is exactly what `ct review` opens.

### 5. Open the review

```sh
ct review .ct/review
```

Either the directory or the `review.json` inside it works. A path that does not
exist is diagnosed by `ct` before anything launches.

What you see next is covered by [Reading a review](/deep_review/reading).

## Reviewing native (rr) recordings

Nothing about the workflow changes. Record the runs the way you normally would
for your language (for native programs that means the rr backend — see
[`ct record`](/reference/ct_cli)), point `--recordings` at the directory holding
them, and `ct review collect` routes to the native collector. The `collect` and
`review` commands are the same ones used above; only the recordings differ.

The native collector is reached as a subprocess. `ct` finds it on `PATH` as
`ct-native-replay`, or wherever `CODETRACER_NATIVE_REPLAY_PATH` points. If it is
missing, only native collection fails — collecting a Python or Noir recording on
a machine that never installed the rr backend works normally, and says so if it
cannot.

Reading a diff out of a git repository with `--repo` needs the native
collector's optional git support, which the default build does not enable. If
`--repo` is refused, generate the patch yourself and pass it instead:

```sh
git diff main..HEAD > change.patch
ct review collect --diff-file change.patch --recordings .ct/runs --output .ct/review
```

A patch file names no commits, so a dataset collected that way carries empty
commit ids rather than invented ones.

## Inspecting a dataset without a GUI

**The dataset the worked example above just produced cannot be inspected
today.** `ct review inspect` reads only the **native** collector's format — the
directory holding a `manifest.dr` — and the materialized collector writes a
single `review.json` and no `.dr` chunks. Running it on `.ct/review` fails:

```console
$ ct review inspect .ct/review
error: `ct review inspect` needs the native replay backend, and no `ct-native-replay` binary was found.
  Add it to PATH, or point CODETRACER_NATIVE_REPLAY_PATH at it.
```

With the native backend present it gets one step further and then fails on the
missing `manifest.dr`. Either way the exit status is 1, so a CI job that shells
out to it on a materialized dataset will fail rather than print statistics.

For a materialized dataset, read `review.json` directly — it is plain JSON, and
`files[].coverage`, `files[].flow` and `files[].diff` are the same numbers
`inspect` would summarise:

```sh
jq '{files: (.files | length), commit: .commitSha, recordings: .recordingCount}' .ct/review/review.json
```

For a **native** dataset the command is:

```sh
ct review inspect <PATH> [--format text|json]
```

which prints file and symbol counts, coverage and flow steps.

## See also

- [Reading a review](/deep_review/reading) — what the three panels show.
- [The agent workflow](/deep_review/agent_workflow) — collecting from an agent
  session, and the end-of-turn hook.
- [`ct` CLI reference](/reference/ct_cli) — the full `ct review` flag tables.
- [Not yet available](/deep_review/not_yet_available) — the deferrals, named.

---

### Book Isonim Content Deep Review Not Yet Available (`docs/book-isonim/content/deep_review/not_yet_available.md`)

---
title: Not yet available
order: 4
---

# Not yet available

Everything on this page is real behaviour you will meet, written down so you do
not read it as a bug or, worse, as a measurement. It is deliberately a page of
its own rather than a footnote at the end of a longer one: the fastest way to
mislead a reviewer is to describe a surface that shows execution data without
saying which parts of it are not driven by the data yet.

## The trace-context selector names the recordings; it does not filter yet

Choosing a different context is remembered, but it does not re-drive the overlay
from that recording. Coverage counts are the **sum across every recording** in
the dataset, and the invocation stepper walks every recorded call of a function
regardless of which run it came from. The selector's entries also show labels
only (`run-1`) — both collectors leave each context's recording id empty.

## `ct review inspect` reads native datasets only

The materialized collector writes a single `review.json` and no `.dr` chunks, so
`inspect` over a materialized dataset reports a missing `manifest.dr`. On a
machine with no native backend installed it reports that instead. Open the
dataset with `ct review`, or read `review.json` directly — it is plain JSON.
[Collecting a review](/deep_review/collecting) shows both transcripts.

## Values are one strip per line, not the debugger's parallel value columns

A review renders every value a step recorded as chips at the end of the line.
Values also arrive from the collectors as pre-rendered strings, so a struct or a
vector shows its rendered text and does not expand.

## The loop control is a stepper, not a dragged slider

It has previous/next buttons and a counter. The debugger's draggable loop slider
is sized from measurements a review has no live flow component to take.

## Draggable context-boundary edge lines are not implemented

The `Expand N lines` buttons are the supported control.

## Coverage is what the recording observed

The materialized collector reports the lines a run actually executed, so the
badge reads `N/N` on a fully-covered file; a line no recording touched is absent
from the coverage list rather than listed as uncovered. `unreachable` is always
`false`: a trace can say a line was not observed, never that it cannot be
reached.

## Symbols carry no declared type or visibility

A materialized trace records that a function ran, not how it was declared, so
`typeDesc` and `visibility` are emitted empty rather than guessed.

## Flow is collected for functions the diff *changed*, not for everything they call

A changed line `let x = helper(y);` gets flow for its caller; `helper` gets a
call count and coverage, but no flow overlay of its own. Following callees would
pull an arbitrary depth of untouched code into the review.

## A review carries no test results

`DeepReviewData` has no test name, status or duration field, so nothing in a
review reports test results — and nothing invents a zero in their place.
`ct test` ships (`ct test discover` works from `ct`; `ct test run` needs the
standalone runner binary, which `ct` tells you when it cannot run it) but
issues **no test certificates** — and nothing in the review surface, or in
the text `ct agent prompt` prints, claims otherwise.

## Sharing a review is still handing over a directory

`ct review` has exactly three forms — the launch form, `collect` and `inspect`.
None of them packages a dataset into a single file, uploads one, or produces a
link; handing a review to somebody means handing over the directory `collect`
wrote. Note what that hands over: a dataset carries the source text of every
changed file and the recorded variable values, so whoever receives it gets all
of it.

## See also

- [Introduction](/deep_review) — what DeepReview is for.
- [Reading a review](/deep_review/reading) — the surfaces these limits apply to.

---

### Book Isonim Content Deep Review Reading (`docs/book-isonim/content/deep_review/reading.md`)

---
title: Reading a review
order: 2
---

# Reading a review

A review adds **no panel of its own**. It routes its data into three panels
CodeTracer already has — the VCS panel, the Editor, and the Agent Activity panel
— and leaves your layout otherwise alone.

## The VCS panel

The VCS panel is the review's navigation surface, and it is the visible tab of
whatever stack hosts it when the review opens.



- **Header** — `Review: <commit>` and the review's totals (`1 file +3 -2`).
- **Trace-context selector** — a dropdown naming the recordings the dataset was
  collected from (`run-1`, `run-2`). It appears only when the dataset carries
  more than one, and today it **names** them without filtering by them — see
  [Not yet available](/deep_review/not_yet_available).
- **`Unified Diff` toggle** — active means clicking a file opens its **diff
  tab**; inactive means clicking opens the **full file**. It changes what a
  click does, not what the panel renders.
- **Changed files** — one row per file with its status letter (`M`, `A`, `D`,
  `R`), its `+`/`−` counts, and a coverage badge (`8/8` above).

Clicking a row opens that file. A review does not concatenate every file into
one scrolling document; cross-file navigation is this list.

The changeset is immutable, so a review offers no refresh and no file watching.

## The diff tab

The diff tab is an ordinary Monaco editor tab, so search, selection, copy,
minimap and keyboard navigation all work inside it. It shows one file: a header
line with the path and its counts, `@@` hunk headers, added lines in green with
`+`, removed lines in red with `−`, and context lines on the normal background.



**Context expansion.** `... Expand 10 lines above` / `... Expand 10 lines below`
reveal more of the surrounding file, ten lines at a time, and repeat. In a
review this is a local slice: the dataset already carries each changed file's
full source, so nothing is fetched. Revealed lines are normal code lines and
pick up the flow overlay like any other.

**Hunk selection.** Click a hunk header to select it, shift-click for a range,
ctrl-click to toggle one. The toolbar shows the count and offers **Copy** (the
selection as a patch) and **Clear**. Staging, discarding and moving hunks
between commits are *not* offered in a review — there is no working tree to
stage into.

## The flow overlay

This is the part a diff on its own cannot give you. Every line the recording
executed carries the standard Omniscience annotations, produced by the same code
that renders them during ordinary debugging:

- **Executed / not executed shading** on each line inside a recorded function.
- **Inline value chips** at the end of the line — a name chip (`<contribution>`)
  and a value chip (`0`) — in the same style the debugger's value boxes use.
  Values are never rendered as `//` comments.
- **An invocation stepper** anchored above each recorded function
  (`‹ main: call 1 / 2 ›`). A function that ran more than once has more than one
  invocation, and the stepper chooses which one the values describe.
- **A loop-iteration stepper** anchored above each loop the selected invocation
  entered (`‹ iteration 1 / 5 ›`), so a line inside a loop shows the values of
  the pass you asked for. It clamps at both ends and disappears for a loop
  entered only once.

In the example from [Collecting a review](/deep_review/collecting), stepping the
loop control walks `contribution` through `0`, `5`, `10`, `15` — the four terms
the change introduced — and `sum` through their running total, on the same lines
the diff added.

:::note
The loop stepper counts the **passes over the loop's header line** that this
invocation recorded. For the `for i in 0..4` above that is five, not four: four
iterations plus the final exit test, which is a real recorded step with no `i`.
:::

Lines with no recorded steps — including comments, which no collector reports —
are shaded as not executed.

Both controls are **steppers**: previous/next buttons and a counter. The
debugger's draggable loop slider is not offered here.

## The Agent Activity panel

The third pillar. Its primary content in a review is **the agent session that
produced the dataset**, so you can read what the agent actually did in the run
leading up to it.

The session is loaded **by reference**: a dataset stores the session id and
enough context to resolve it against the agent backend (Agent Harbor, or an ACP
agent that advertises `session/load`). No transcript is copied into the file, so
a dataset never goes stale against the session and sharing a dataset does not
share the conversation.

`ct review collect` records the reference automatically when it is run inside an
agent session — it reads `CODETRACER_AGENT_SESSION_ID` and friends, the
variables the harness already exports:

```console
Review dataset ready: .ct/review/review.json
Associated with agent session: rv8-demo-session (acp)
Open it with: ct review .ct/review
```

A dataset with no session reference is a complete review; the panel simply shows
no conversation. When a reference cannot be resolved the panel says which of the
three things went wrong, in one sentence, rather than rendering an empty
conversation that reads as "the agent did nothing":

- the agent cannot replay sessions,
- the session could not be fetched (quoting the backend's own reason),
- the session loaded but contains no messages.

The panel shows the session and nothing else — there is no summary of the
dataset beside it. Coverage is reported where you are already choosing a file:
as a badge on the VCS panel's changed-file rows, reading `15/17` when the
dataset has coverage for that file and staying **empty** when it does not. An
empty badge means *not measured*; you will not see a `0/0` standing in for it.

## Full Files mode

Turning the VCS panel's `Unified Diff` toggle off makes a click open the whole
file in the normal editor instead, with diff highlights on the modified lines
and the same flow overlay — the same value chips, the same shading — wherever
the dataset has data for the loaded file.

Full Files mode has **no controls of its own**. It follows the invocation and
loop-iteration choices made with the diff tab's steppers, so the two surfaces
always agree about which call they are showing.

## See also

- [Collecting a review](/deep_review/collecting) — producing the dataset the
  panels above are reading.
- [Not yet available](/deep_review/not_yet_available) — what these surfaces do
  not do yet.
- [Graphical User Interface](/usage_guide/gui) — the panels outside a review.

---

### Book Isonim Content Getting Started Aiken (`docs/book-isonim/content/getting_started/aiken.md`)

---
title: Aiken (Cardano)
order: 112
---
## Aiken (Cardano)

CodeTracer supports tracing Aiken smart contracts on Cardano. The recorder steps through the UPLC (Untyped Plutus Core) CEK machine at every evaluation step and maps execution back to Aiken source code.

### Prerequisites

- **Aiken CLI** (`aiken`) v1.1.21+ for compiling contracts
- **codetracer-cardano-recorder** binary installed or built from source

### Recording a Trace

1. Create an Aiken project with a test function:

   ```aiken
   // validators/example.ak

   test test_flow() {
     let a = 10
     let b = 32
     let sum_val = a + b
     expect sum_val == 42
     let doubled = sum_val * 2
     let final_result = doubled + a
     final_result == 94
   }
   ```

2. Record the trace:

   ```bash
   codetracer-cardano-recorder record validators/example.ak -o ./ct-traces/
   ```

   The recorder will:
   - Compile the Aiken source to UPLC via `aiken build`
   - Execute the test function through the stepping CEK machine
   - Capture machine state (environment, budget, term) at every step
   - Map UPLC term indices back to Aiken source locations
   - Write the trace files to `./ct-traces/`

### Replaying an On-Chain Transaction

To debug a Cardano transaction that invoked a validator script:

```bash
codetracer-cardano-recorder replay \
    --tx-hash abc123...def456 \
    --blockfrost-key your-api-key \
    -o ./ct-traces/
```

The replayer fetches the transaction via Blockfrost, extracts the validator script and its datum/redeemer/context arguments, reconstructs the full applied UPLC program, and executes it through the stepping recorder.

:::note
On-chain scripts are flat-encoded UPLC bytecode. For source-level mapping, provide the original Aiken project source alongside the replay.
:::

### Viewing the Trace

```bash
ct replay --trace-folder ./ct-traces/
```

### Key Flags

| Flag                      | Description                                           |
| ------------------------- | ----------------------------------------------------- |
| `record <file>`           | Record an Aiken source file (`.ak`)                   |
| `replay --tx-hash <HASH>` | Replay an on-chain Cardano transaction                |
| `--blockfrost-key <KEY>`  | Blockfrost API key (or `BLOCKFROST_API_KEY` env var)  |
| `-o, --out-dir <DIR>`     | Output directory (default: `./ct-traces/`)            |
| `-f, --format <FMT>`      | Output format: `binary` or `json` (default: `binary`) |

### Execution Budget

The trace includes Cardano execution budget (CPU and memory units) at every step, visible in the CodeTracer variable pane. This helps identify which parts of your validator consume the most resources.

---

### Book Isonim Content Getting Started Cadence (`docs/book-isonim/content/getting_started/cadence.md`)

---
title: Cadence (Flow)
order: 113
---
## Cadence (Flow)

CodeTracer supports tracing Cadence smart contracts on the Flow blockchain. The recorder instruments the Cadence interpreter's `OnStatement` hook to capture per-statement execution state, producing structured trace data.

### Prerequisites

- **Flow CLI** (`flow`) v2.x+ for running the emulator and tests
- **Go toolchain** (Go 1.22+) for building the Go tracer helper
- **codetracer-flow-recorder** binary installed or built from source

### Recording a Trace

1. Write a Cadence contract:

   ```cadence
   access(all) contract FlowTest {
       access(all) fun compute(): Int {
           let a: Int = 10
           let b: Int = 32
           let sumVal: Int = a + b
           let doubled: Int = sumVal * 2
           let finalResult: Int = doubled + a
           return finalResult
       }
   }
   ```

2. Record the trace:

   ```bash
   codetracer-flow-recorder record FlowTest.cdc -o ./ct-traces/
   ```

   The recorder will:
   - Parse the Cadence source
   - Configure the interpreter with an `OnStatement` callback
   - Execute the program, capturing source location, variable state, and call stack at every statement
   - Convert the execution trace to CodeTracer's format

### Replaying an On-Chain Transaction

To debug a Flow transaction from mainnet or testnet:

```bash
codetracer-flow-recorder replay \
    --tx-hash abc123...def \
    --access-node access.mainnet.nodes.onflow.org:9000 \
    --source-dir ./cadence/contracts/ \
    -o ./ct-traces/
```

The replayer uses the Flow emulator's fork mode to recreate historical state and re-execute the transaction with tracing enabled. Cadence contracts are stored as source code on-chain, so source is always available via the Access API.

### Viewing the Trace

```bash
ct replay --trace-folder ./ct-traces/
```

### Key Flags

| Flag                      | Description                                           |
| ------------------------- | ----------------------------------------------------- |
| `record <file>`           | Record a Cadence source file (`.cdc`)                 |
| `replay --tx-hash <HASH>` | Replay an on-chain Flow transaction                   |
| `--access-node <URL>`     | Flow Access Node gRPC endpoint                        |
| `--source-dir <PATH>`     | Directory with Cadence source files                   |
| `-o, --out-dir <DIR>`     | Output directory (default: `./ct-traces/`)            |
| `-f, --format <FMT>`      | Output format: `binary` or `json` (default: `binary`) |

### Resource Tracking

Cadence uses a resource-oriented programming model where resources cannot be copied, only moved (`<-`). The trace captures resource lifecycle events (creation, moves, destruction), visible in the variable pane with resource type and UUID.

---

### Book Isonim Content Getting Started Cairo (`docs/book-isonim/content/getting_started/cairo.md`)

---
title: Cairo (StarkNet)
order: 111
---
## Cairo (StarkNet)

CodeTracer supports tracing Cairo programs and StarkNet smart contracts. The recorder consumes execution traces from the Cairo VM and maps them to Cairo source code via Sierra debug info.

### Prerequisites

- **Cairo compiler** (`cairo-compile`, `cairo-run`) or **Scarb** build tool
- **snforge** (for StarkNet contract testing)
- **codetracer-cairo-recorder** binary installed or built from source

### Recording a Standalone Cairo Program

1. Write a Cairo program:

   ```cairo
   fn main() {
       let a: felt252 = 10;
       let b: felt252 = 32;
       let sum_val = a + b;
       let doubled = sum_val * 2;
       let final_result = doubled + a;
       println!("Result: {}", final_result);
   }
   ```

2. Record the trace:

   ```bash
   codetracer-cairo-recorder record program.cairo -o ./ct-traces/
   ```

### Recording a StarkNet Contract Test

1. Set up a Scarb project with `snforge` tests, then record a test trace:

   ```bash
   codetracer-cairo-recorder trace-starknet trace.json -o ./ct-traces/
   ```

   Where `trace.json` is the execution trace output from `snforge test --trace`.

### Replaying an On-Chain Transaction

```bash
codetracer-cairo-recorder replay \
    --tx-hash 0x04a3c... \
    --rpc-url https://starknet-mainnet.public.blastapi.io \
    --source-dir ./src/ \
    -o ./ct-traces/
```

### Viewing the Trace

```bash
ct replay --trace-folder ./ct-traces/
```

### Key Flags

| Flag                      | Description                                           |
| ------------------------- | ----------------------------------------------------- |
| `record <file>`           | Record a standalone `.cairo` program                  |
| `trace-starknet <trace>`  | Convert an snforge trace JSON to CodeTracer format    |
| `replay --tx-hash <HASH>` | Replay an on-chain StarkNet transaction               |
| `--rpc-url <URL>`         | StarkNet JSON-RPC endpoint                            |
| `--source-dir <PATH>`     | Directory with contract source code                   |
| `-o, --out-dir <DIR>`     | Output directory (default: `./ct-traces/`)            |
| `-f, --format <FMT>`      | Output format: `binary` or `json` (default: `binary`) |

---

### Book Isonim Content Getting Started Circom (`docs/book-isonim/content/getting_started/circom.md`)

---
title: Circom
order: 106
---
## Circom

CodeTracer supports tracing Circom zero-knowledge circuits during witness generation. The recorder traces the witness computation and maps signal assignments back to your `.circom` source code.

### Prerequisites

- **Circom compiler** (`circom`) available in your PATH
- **Node.js** (for the WASM witness generator) or **C++ toolchain** (gcc, make, gmp) for the C++ backend
- **codetracer-circom-recorder** binary installed or built from source

### Recording a Trace

1. Write a Circom circuit:

   ```circom
   pragma circom 2.0.0;

   template FlowTest() {
       signal input a;
       signal input b;
       signal output result;

       signal sumVal;
       sumVal <== a + b;

       signal doubled;
       doubled <== sumVal * 2;

       result <== doubled + a;
   }

   component main = FlowTest();
   ```

2. Record the trace using the WASM backend (default):

   ```bash
   codetracer-circom-recorder record flow_test.circom -o ./ct-traces/
   ```

3. Or use the C++ witness generator backend for larger circuits:

   ```bash
   codetracer-circom-recorder record flow_test.circom -o ./ct-traces/ --backend cpp
   ```

   The recorder will:
   - Compile the circuit with `circom` (using `--wasm` or `--c` depending on backend)
   - Generate a source map if using the [metacraft-labs circom fork](https://github.com/metacraft-labs/circom)
   - Execute the witness generator with default inputs
   - Capture signal assignments and map them to source locations
   - Write the trace files to `./ct-traces/`

### Viewing the Trace

```bash
ct replay --trace-folder ./ct-traces/
```

### Key Flags

| Flag                  | Description                                           |
| --------------------- | ----------------------------------------------------- |
| `<circom-file>`       | Path to the `.circom` source file                     |
| `-o, --out-dir <DIR>` | Output directory (default: `./ct-traces/`)            |
| `-f, --format <FMT>`  | Output format: `binary` or `json` (default: `binary`) |
| `-b, --backend <BE>`  | Witness generator: `wasm` or `cpp` (default: `wasm`)  |

### Source Mapping

For the best debugging experience, use the [metacraft-labs circom fork](https://github.com/metacraft-labs/circom) which adds a `--srcmap` flag for precise source-level mapping. Without it, the recorder falls back to heuristic source mapping.

---

### Book Isonim Content Getting Started Elixir (`docs/book-isonim/content/getting_started/elixir.md`)

---
title: Elixir & Erlang
order: 105
---
## Elixir & Erlang

CodeTracer supports Elixir 1.15 and newer, and Erlang/OTP 26 and newer.

The recorder is hosted in the
[codetracer-beam-recorder](https://github.com/metacraft-labs/codetracer-beam-recorder)
repo; `ct` finds it for you.

## How to record an Elixir program

```bash
ct run hello.exs
```

Arguments after the filename are passed through unchanged:

```bash
ct run script.exs --count 10
```

For a Mix project, record the command you would normally run:

```bash
ct record -o ./trace mix run -e 'MyApp.main()'
ct replay ./trace
```

## Recording a Phoenix or Plug application

Web applications are recorded as a running server. That has its own guide:
[Live requests — Elixir & Erlang](/usage_guide/live-requests-elixir).

In short:

```bash
ct record --server --lang elixir -o ./trace -- mix phx.server
```

Then `ct replay -t ./trace` in another terminal shows requests as they
arrive.

## BEAM processes in a recording

Elixir code tends to spawn many lightweight processes. These are recorded as
*threads* within a single recording rather than as separate recordings, so
message passing between them stays visible in one place, and you can follow
work as it moves from one process to another.

---

### Book Isonim Content Getting Started Installation (`docs/book-isonim/content/getting_started/installation.md`)

---
title: Installation
order: 1
---
# Installation

:::caution
CodeTracer can only be installed on Linux and macOS currently.
:::

## Download binaries
### Recommended: automatically
We provide a automatic installer that installs CodeTracer in the best possible way for your current system. You can run it by running the following in a terminal:
```
user $ curl -fsSL https://get.codetracer.com/sh | sh
```

On Debian, Ubuntu, Fedora and RHEL the installer registers the Metacraft Labs package
repository (`deb.metacraft-labs.com` / `rpm.metacraft-labs.com`) and installs the `codetracer`
package, so later updates arrive with `apt upgrade` / `dnf upgrade`. On Arch it uses your AUR
helper, on Gentoo the metacraft-overlay, and on macOS the signed DMG from downloads.codetracer.com.

### Manually on Linux
Here is a list of our Linux packages:

- [Ubuntu (`deb.metacraft-labs.com`)](https://deb.metacraft-labs.com/)
- [Debian (`deb.metacraft-labs.com`)](https://deb.metacraft-labs.com/)
- [Red Hat (`rpm.metacraft-labs.com`)](https://rpm.metacraft-labs.com/)
- [Fedora (`rpm.metacraft-labs.com`)](https://rpm.metacraft-labs.com/)
- [Gentoo (metacraft-overlay)](https://github.com/metacraft-labs/metacraft-overlay)
- [Arch Linux (AUR)](https://aur.archlinux.org/packages/codetracer)
- [AppImage (amd64)](https://downloads.codetracer.com/CodeTracer-latest-amd64.AppImage)

:::tip
You can place the downloaded app in a location of your choosing (e.g. `~/.local/bin`)
:::

### Manually on macOS
You can download a `.dmg` app bundle from our website:

- [Download CodeTracer for macOS (`.dmg`, arm64)](https://downloads.codetracer.com/CodeTracer-latest-arm64.dmg)

:::tip
You can place the downloaded app in a location of your choosing (e.g., the `Applications` folder on macOS).
When you launch CodeTracer for the first time, it will prompt you to complete the remaining installation steps, such as adding the command-line utilities to your PATH.
:::

:::caution
Upon the first launch, macOS users will see the error message "CodeTracer is damaged and can't be opened". To resolve this problem, please execute the command `xattr -c <path/to/CodeTracer.app>`.

We expect this inconvenience will be remedied soon through our enrollment in the Apple Developer program that will ensure CodeTracer is properly signed and whitelisted by Apple. See [this discussion](https://discussions.apple.com/thread/253714860?sortBy=rank) for more details.
:::

:::caution
Recording ruby on macOS requires you to install ruby through [homebrew](https://brew.sh), otherwise trying to record ruby programs will fail due to the built-in ruby binary on macOS being more than 7 years old.

Once homebrew is installed, simply install ruby with `user $ brew install ruby`.
:::

:::note
Python recordings reuse whichever interpreter resolves when you run `python` (or `python3`) in your shell. Install the `codetracer_python_recorder` package in that environment (for example `pip install codetracer_python_recorder`) before running `ct record my_script.py`. You can point CodeTracer at a specific interpreter by setting `CODETRACER_PYTHON_INTERPRETER`. See the [Python getting-started guide](../getting_started/python.md) for a full walkthrough covering installation, recording, and replay.
:::

## Installation from source

### Linux (and other Nix-based systems)

#### Prerequisites

On systems that are not NixOS, you need to install `direnv` and `nix`.

Nix should not be installed through your distribution's package manager, but from [here](https://nixos.org/download/).

Direnv should be set up in your shell, as shown [here](https://direnv.net/docs/hook.html).

#### Building from source
1. Clone the repository with submodules: `git clone https://github.com/metacraft-labs/codetracer.git --recursive`
2. Enter the created directory
3. Add the following text to `~/.config/nix/nix.conf` if it doesn't already exist:
   ```
   experimental-features = nix-command flakes
   ```
4. Run `nix develop`
5. Run `direnv allow`
6. To build codetracer simply run `just build`. The location of the resulting binary will be `./src/build-debug/bin/ct`
7. Now every time you enter the `codetracer` directory your environment should be updated

:::tip
Users of Visual Studio Code might encounter issues when using `code .`. To fix them do the following:
:::

1. Run `direnv deny`
1. Run `code .`
1. Run `direnv allow`

### macOS

#### Prerequisites

The only dependencies for the macOS build are `git`, `bash` and `homebrew`.

#### Building from source
1. Clone the repository with submodules: `git clone https://github.com/metacraft-labs/codetracer.git --recursive`
2. Enter the created directory
3. Run `./non-nix-build/build.sh` from the root of the cloned repository. This will install all prerequisites like Rust, Nim and others using homebrew
4. The resulting binary can be found at `./non-nix-build/CodeTracer.app/Contents/MacOS/bin/ct`, and a DMG installer is created at `./non-nix-build/CodeTracer.dmg`.

### Building and running the tests

Currently, you can run the db-backend (Rust) tests:

```bash
# inside src/db-backend:
cargo test --release --bin db-backend # test most cases: non-ignored
cargo test --release --bin db-backend -- --ignored # test the ignored cases: ignored by default as they're slower
```

some initial simple end to end Playwright GUI tests:

```bash
just test-gui
```

---

### Book Isonim Content Getting Started Introduction (`docs/book-isonim/content/getting_started/introduction.md`)

---
title: Introduction
order: 0
---
# Introduction

CodeTracer is a user-friendly time-traveling debugger designed to support a wide range of programming languages.

It records the execution of a program into a sharable self-contained trace file. You can load the produced trace files in a GUI environment that allows you to move forward and backward through the execution and to examine the history of all memory locations. They say a picture is worth a thousand words — well, a video is even better! Watch the demo below to see CodeTracer in action:

  

[Watch on YouTube](https://www.youtube.com/watch?v=xZsJ55JVqmU)

## The benefits of time-travel

Compared to traditional debuggers, CodeTracer gives you two major superpowers:

* **Once you capture a bug in a recording, consider it squashed!**

  Bugs that are hard to reproduce can be painful to fix — you’ve surely been there. Once such a bug is captured with CodeTracer, you'll rarely need more than 30 minutes to track it down! This is largely a consequence of the next superpower:

* **Most bugs are easily revealed when you know the origin of any value in the program.**

  All programs produce output. Some examples are bytes generated as a response to a web request, pixels being drawn on your screen or perhaps a simple log line written to the terminal.

  When CodeTracer creates a recording, it captures a user-extensible set of output events relevant to the program. The GUI displays these events in a searchable chronological event log.

  Consider a misbehaving program that prints unexpected output to a log file midway through its execution. Clicking on the specific output event in CodeTracer will take you to the precise moment and code line where it was generated.

  The unexpected value must be originating from some variable that's being passed to the logging function. With CodeTracer, you can now ask the question "Where did this value come from?". CodeTracer will find another moment in the execution, potentially multiple seconds earlier, in a completely different part of the program where this particular memory location was last written to.

  This could be memory corruption or a genuine logical error. Either way, CodeTracer will report the origin. Let's say that you end up in the correct function that is responsible for computing the problematic value, but another input there leads to the issue. You can continue the search by repeating the question "Where did this input come from"? It usually takes just a few of these jumps to earlier moments in time to arrive at the root cause for the bug.

  Every time you jump to a new moment in the execution, you can fearlessly explore your surroundings by stepping forward or backwards, having access to a back button that can always get you to any previous point of interest. At every point of the journey, our novel UI shows you details about the past and future program states at a glance and you know your position in the precisely recorded call trace of the program.

  These features combined, make for a truly powerful debugging experience.

---

### Book Isonim Content Getting Started Javascript (`docs/book-isonim/content/getting_started/javascript.md`)

---
title: JavaScript / TypeScript
order: 103
---
## JavaScript / TypeScript

CodeTracer supports tracing JavaScript and TypeScript programs. The recorder instruments your source code and captures execution state at every statement.

### Prerequisites

- **Node.js** (v18+)
- **codetracer-js-recorder** installed via npm or built from source

### Recording a Trace

1. Write a JavaScript program:

   ```javascript
   function compute() {
     const a = 10;
     const b = 32;
     const sumVal = a + b;
     const doubled = sumVal * 2;
     const finalResult = doubled + a;
     return finalResult;
   }

   console.log(compute());
   ```

2. Record the trace:

   ```bash
   codetracer-js-recorder record script.js --out-dir ./ct-traces/
   ```

3. With application arguments:

   ```bash
   codetracer-js-recorder record script.js --out-dir ./ct-traces/ -- --port 3000
   ```

4. Filter which files are instrumented:

   ```bash
   codetracer-js-recorder record src/index.ts \
       --include "src/**/*.ts" \
       --exclude "node_modules/**" \
       --out-dir ./ct-traces/
   ```

### Instrumenting Without Recording

You can instrument source files separately (useful for build pipelines):

```bash
codetracer-js-recorder instrument src/ --out dist/ --source-maps
```

### Viewing the Trace

```bash
ct replay --trace-folder ./ct-traces/
```

### Key Flags

| Flag               | Description                                        |
| ------------------ | -------------------------------------------------- |
| `record <file>`    | Entry file or directory to record                  |
| `instrument <src>` | Instrument source files without executing          |
| `--out <DIR>`      | Output directory for instrumented files            |
| `--out-dir <DIR>`  | Trace output directory (default: `./ct-traces/`)   |
| `--format <FMT>`   | Trace format: `json` or `binary` (default: `json`) |
| `--include <GLOB>` | Include glob pattern (repeatable)                  |
| `--exclude <GLOB>` | Exclude glob pattern (repeatable)                  |
| `--source-maps`    | Emit source maps during instrumentation            |
| `-- <args>`        | Arguments passed to the instrumented program       |

---

### Book Isonim Content Getting Started Leo (`docs/book-isonim/content/getting_started/leo.md`)

---
title: Leo (Aleo)
order: 108
---
## Leo (Aleo)

CodeTracer supports tracing Leo smart contracts on the Aleo blockchain. The recorder captures execution state from the Leo interpreter and snarkVM, mapping it back to Leo source code.

### Prerequisites

- **Leo CLI** (`leo`) for building and testing programs
- **codetracer-leo-recorder** binary installed or built from source

### Recording a Trace

1. Write a Leo program:

   ```leo
   program flow_test.aleo {
       transition compute(a: u32, b: u32) -> u32 {
           let sum_val: u32 = a + b;
           let doubled: u32 = sum_val * 2u32;
           let final_result: u32 = doubled + a;
           return final_result;
       }
   }
   ```

2. Record the trace:

   ```bash
   codetracer-leo-recorder record main.leo -o ./ct-traces/
   ```

### Replaying an On-Chain Program

To execute and trace a deployed Aleo program:

```bash
codetracer-leo-recorder replay \
    --program-id credits.aleo \
    --function transfer_public \
    --input aleo1abc...xyz \
    --input 100u64 \
    --endpoint https://api.explorer.aleo.org/v1 \
    -o ./ct-traces/
```

### Viewing the Trace

```bash
ct replay --trace-folder ./ct-traces/
```

### Key Flags

| Flag                       | Description                                                      |
| -------------------------- | ---------------------------------------------------------------- |
| `record <file>`            | Record a Leo source file (`.leo`)                                |
| `replay --program-id <ID>` | On-chain program ID (e.g., `credits.aleo`)                       |
| `--function <NAME>`        | Function to execute                                              |
| `--input <VALUE>`          | Input values (repeatable, e.g., `--input 10u32`)                 |
| `--endpoint <URL>`         | Aleo node REST API (default: `https://api.explorer.aleo.org/v1`) |
| `-o, --out-dir <DIR>`      | Output directory (default: `./ct-traces/`)                       |
| `-f, --format <FMT>`       | Output format: `binary` or `json` (default: `binary`)            |

### Note on Zero-Knowledge Execution

Leo transitions execute off-chain with zero-knowledge proving. The recorder uses the Leo interpreter for source-level tracing of transition logic, while finalize functions (which execute on-chain) are traced at the AVM instruction level.

---

### Book Isonim Content Getting Started Miden (`docs/book-isonim/content/getting_started/miden.md`)

---
title: Miden
order: 107
---
## Miden

CodeTracer supports tracing Miden VM programs and smart contracts. The recorder captures per-instruction execution state from the Miden VM (a STARK-based zero-knowledge virtual machine) and maps it back to MASM source code.

### Prerequisites

- **Miden toolchain** for writing and compiling MASM programs
- **codetracer-miden-recorder** binary installed or built from source

### Recording a Standalone Program

1. Write a MASM program:

   ```masm
   # flow_test.masm
   begin
       push.10       # a = 10
       push.32       # b = 32
       add           # sum_val = 42
       dup           # keep sum_val
       push.2
       mul           # doubled = 84
       swap
       push.10
       add           # final_result = 94 (but actually doubled + a)
   end
   ```

2. Record the trace:

   ```bash
   codetracer-miden-recorder record flow_test.masm -o ./ct-traces/
   ```

### Recording a MockChain Contract

For testing contract interactions with the MockChain simulator:

```bash
codetracer-miden-recorder contract \
    --wallet-id 0x1000 \
    --faucet-id 0x2000 \
    --symbol TEST \
    --debug \
    -o ./ct-traces/
```

### Replaying an On-Chain Transaction

```bash
codetracer-miden-recorder replay \
    --node-url http://localhost:57291 \
    --account-id 0x1234abcd \
    --transaction-id 0xdeadbeef... \
    -o ./ct-traces/
```

### Viewing the Trace

```bash
ct replay --trace-folder ./ct-traces/
```

### Key Flags

| Flag                       | Description                                              |
| -------------------------- | -------------------------------------------------------- |
| `record <PROGRAM>`         | Record a MASM source file (`.masm`) or package (`.masp`) |
| `contract`                 | Record a MockChain contract simulation                   |
| `--wallet-id <ID>`         | Account ID for wallet (hex, default: `0x1000`)           |
| `--faucet-id <ID>`         | Account ID for faucet (hex, default: `0x2000`)           |
| `--symbol <SYM>`           | Faucet token symbol (default: `TEST`)                    |
| `replay --node-url <URL>`  | Miden node RPC endpoint                                  |
| `--account-id <ID>`        | Account ID (hex)                                         |
| `--transaction-id <ID>`    | Transaction ID (hex)                                     |
| `--captured-inputs <FILE>` | Path to captured TransactionInputs JSON                  |
| `-o, --out-dir <DIR>`      | Output directory (default: `./ct-traces/`)               |
| `-f, --format <FMT>`       | Output format: `binary` or `json` (default: `binary`)    |

---

### Book Isonim Content Getting Started Move (`docs/book-isonim/content/getting_started/move.md`)

---
title: Move (Sui / Aptos)
order: 114
---
## Move (Sui / Aptos)

CodeTracer supports tracing Move smart contracts on both Sui and Aptos. The recorder captures per-instruction execution state from the Move VM and maps it back to Move source code.

### Prerequisites

- **Sui CLI** (`sui`) or **Aptos CLI** (`aptos`) for building and deploying
- **codetracer-move-recorder** binary installed or built from source

### Recording from a Trace File

The Move recorder converts execution trace files (produced by the instrumented Move VM) to CodeTracer format:

```bash
codetracer-move-recorder record trace.json -o ./ct-traces/
```

For compressed traces:

```bash
codetracer-move-recorder record trace.json.zst -o ./ct-traces/
```

Optionally provide the Move source file for enhanced source mapping:

```bash
codetracer-move-recorder record trace.json -s sources/my_module.move -o ./ct-traces/
```

### Replaying a Sui Transaction

```bash
codetracer-move-recorder replay \
    --digest <TRANSACTION_DIGEST> \
    --rpc-url https://fullnode.mainnet.sui.io:443 \
    --source-dir ./sources/ \
    -o ./ct-traces/
```

For local development:

```bash
codetracer-move-recorder replay \
    --digest <DIGEST> \
    --rpc-url http://localhost:9000 \
    --source-dir ./sources/ \
    -o ./ct-traces/
```

### Replaying an Aptos Transaction

```bash
codetracer-move-recorder aptosreplay \
    --txn-version 123456789 \
    --node-url https://fullnode.mainnet.aptoslabs.com/v1 \
    --source-dir ./sources/ \
    -o ./ct-traces/
```

### Viewing the Trace

```bash
ct replay --trace-folder ./ct-traces/
```

### Key Flags

| Flag                            | Description                                                           |
| ------------------------------- | --------------------------------------------------------------------- |
| `record <trace>`                | Convert a Move trace file (`.json` or `.json.zst`)                    |
| `-s, --source <FILE>`           | Move source file for enhanced mapping                                 |
| `replay --digest <DIGEST>`      | Replay a Sui transaction by digest                                    |
| `--rpc-url <URL>`               | Sui RPC endpoint (default: `http://localhost:9000`)                   |
| `aptosreplay --txn-version <N>` | Replay an Aptos transaction by ledger version                         |
| `--node-url <URL>`              | Aptos REST API (default: `https://fullnode.mainnet.aptoslabs.com/v1`) |
| `--source-dir <PATH>`           | Directory with Move source files                                      |
| `--profile-gas`                 | Run gas profiling (Aptos, default: `true`)                            |
| `-o, --out-dir <DIR>`           | Output directory (default: `./ct-traces/`)                            |
| `-f, --format <FMT>`            | Output format: `binary` or `json` (default: `binary`)                 |

---

### Book Isonim Content Getting Started Noir (`docs/book-isonim/content/getting_started/noir.md`)

---
title: Noir
order: 2
---
## Noir

The initial release of CodeTracer has MVP support for the Noir programming language. It has been developed in collaboration with the Blocksense team and currently requires the use of the [Blocksense Noir Compiler](https://github.com/blocksense-network/noir), which is included in the CodeTracer distribution.

We support many of Noir's features, but not all: e.g. we don't support mutable references currently, we don't serialize struct values and some other cases.

We are planning on adding support for the missing features in the future.

## How to Trace a Noir Program

Before tracing your Noir program, you must compile it and provide the inputs. CodeTracer expects your project to have a `Prover.toml` file containing the inputs for your program.

1.  Navigate to your Noir project's directory.
2.  Generate a `Prover.toml` file:
    ```sh
    nargo check
    ```
3.  Edit the necessary inputs in your `Prover.toml` file.

Once your project is ready, you can use CodeTracer's commands from the CodeTracer directory.

### Running a Program (`ct run`)

This command executes your Noir program and displays the visual trace in the CodeTracer UI. This is the most direct way to see your code's execution.

To run the `noir_space_ship` example included with CodeTracer:
```sh
# Make sure you are in the root CodeTracer directory
ct run examples/noir_space_ship
```

### Recording a Trace (`ct record`)

This command runs your program and saves its execution trace.

```sh
ct record examples/noir_space_ship
```

### Replaying a Trace (`ct replay`)

This command loads a previously recorded trace and displays it in the CodeTracer UI.

```sh
# First, record a trace if you haven't already
ct record examples/noir_space_ship

# Then, replay it
ct replay
```
You will be asked to choose which trace to replay from a list of recent traces.

---

### Book Isonim Content Getting Started Php (`docs/book-isonim/content/getting_started/php.md`)

---
title: PHP
order: 104
---
## PHP

CodeTracer supports PHP 8.1 and newer.

The recorder for PHP is hosted in the
[codetracer-php-recorder](https://github.com/metacraft-labs/codetracer-php-recorder)
repo, but you do not install or invoke it yourself — `ct` finds it.

## How to record a PHP program

Use `ct record` to record and `ct replay` to open the result, or `ct run` to
do both in one step:

```bash
ct run hello.php
```

To pass arguments to your script, put them after the filename. They reach PHP
unchanged, including flags:

```bash
ct run script.php --verbose input.txt
```

## Recording a web application

PHP web applications are recorded as a running server rather than a
single script. That has its own guide:
[Live requests — PHP](/usage_guide/live-requests-php).

In short:

```bash
ct record --server --lang php -o ./trace -- php -S 127.0.0.1:8000 -t public/
```

Then, in another terminal, `ct replay -t ./trace` shows each HTTP request as
it arrives, and double-clicking one jumps to the code that served it. This
works with the built-in server and with php-fpm pools; frameworks such as
Laravel, Symfony and WordPress need no special setup.

## What you do not need to do

- You do not need to edit `php.ini` or pass `-d extension=…`. The CodeTracer
  extension is loaded for you.
- You do not need to install the recorder separately. If it is genuinely
  missing, `ct` tells you what is absent and how to install it, and exits
  with a failure rather than leaving an empty recording behind.

---

### Book Isonim Content Getting Started Polkavm (`docs/book-isonim/content/getting_started/polkavm.md`)

---
title: PolkaVM (ink!)
order: 117
---
## PolkaVM (ink!)

CodeTracer supports tracing programs running on PolkaVM, Polkadot's RISC-V based virtual machine. The recorder uses PolkaVM's built-in step tracing and LineProgram debug info for source-level mapping.

### Prerequisites

- **Rust toolchain** with `riscv32em-unknown-none-elf` target for compiling PolkaVM programs
- **ink!** framework (v6+) for smart contracts
- **codetracer-polkavm-recorder** binary installed or built from source

### Recording a PolkaVM Program

```bash
codetracer-polkavm-recorder record program.polkavm -o ./ct-traces/
```

### Recording an ink! Contract

1. Build your ink! contract:

   ```bash
   cd my_ink_contract
   cargo contract build
   ```

2. Record a contract message execution:

   ```bash
   codetracer-polkavm-recorder trace-ink \
       --contract target/ink/my_contract.contract \
       --constructor new \
       --message get \
       -o ./ct-traces/
   ```

3. With message arguments:

   ```bash
   codetracer-polkavm-recorder trace-ink \
       --contract target/ink/my_contract.contract \
       --message flip \
       --arg 0x01 \
       -o ./ct-traces/
   ```

### Replaying an On-Chain Contract

To debug a contract call on a Substrate chain:

```bash
codetracer-polkavm-recorder replay \
    --address 5GrwvaEF5... \
    --selector get \
    --endpoint ws://127.0.0.1:9944 \
    --source-dir ./src/ \
    -o ./ct-traces/
```

### Viewing the Trace

```bash
ct replay --trace-folder ./ct-traces/
```

### Key Flags

| Flag                          | Description                                             |
| ----------------------------- | ------------------------------------------------------- |
| `record <program>`            | Record a PolkaVM program blob (`.polkavm`)              |
| `trace-ink --contract <PATH>` | Record an ink! contract execution                       |
| `--constructor <NAME>`        | Constructor to call (e.g., `new`, `default`)            |
| `--message <NAME>`            | ink! message name (e.g., `flip`, `get`)                 |
| `--arg <VALUE>`               | Message arguments (repeatable, hex-encoded SCALE)       |
| `replay --address <ADDR>`     | On-chain contract address (SS58 or hex)                 |
| `--selector <NAME>`           | ink! message selector name                              |
| `--endpoint <URL>`            | Substrate RPC endpoint (default: `ws://127.0.0.1:9944`) |
| `--block <HASH>`              | Block hash for state fetch (latest if omitted)          |
| `--source-dir <PATH>`         | Contract source directory                               |
| `-o, --out-dir <DIR>`         | Output directory (default: `./ct-traces/`)              |
| `-f, --format <FMT>`          | Output format: `binary` or `json` (default: `binary`)   |

---

### Book Isonim Content Getting Started Python (`docs/book-isonim/content/getting_started/python.md`)

---
title: Python
order: 6
---
## Python

This guide explains how to install the Python recorder wheel, capture a trace with your own interpreter, and open the resulting recording in CodeTracer.

### 1. Install the Python recorder package

CodeTracer's Python support runs inside your Python interpreter — the `codetracer_python_recorder` package must be installed in the same environment whose code you intend to trace.  `ct record` discovers and invokes it automatically.

1. Activate the environment that you normally use to run your Python code (virtualenv, `uv`, Conda, system Python, etc.). `ct record` always reuses whichever interpreter is active in your shell.
2. Install (or upgrade) the recorder package:

   ```bash
   python -m pip install --upgrade codetracer_python_recorder
   ```

   If you use a tool like `uv` or `pipx`, run the equivalent command inside that tool so the recorder ends up on the same `sys.path` as your application.
3. Optional: confirm the recorder is ready by running CodeTracer's readiness probe.

   ```bash
   ct doctor python
   ```

   The probe reports the interpreter it would invoke and whether the recorder package is importable from it.

:::caution
If `ct record` cannot import the recorder package, recording will fail with a module import error. Install the package in the interpreter you intend to trace or point Codetracer at a specific interpreter via `CODETRACER_PYTHON_INTERPRETER=/path/to/python`.
:::

### 2. Record a trace

1. From the same shell where the interpreter (and recorder wheel) are available, run:

   ```bash
   ct record path/to/script.py [-- <script arguments>]
   ```

   - Omit `--lang`; Python scripts are auto-detected.
   - Any arguments after `--` are passed directly to your script.
   - Use `CODETRACER_PYTHON_INTERPRETER` if you need to force a specific interpreter (for example, inside CI).
2. The command prints the trace identifier and the output directory (defaults to `.`, configurable with `-o`). The directory contains `trace.json`, `trace_paths.json`, and `trace_metadata.json` generated by the Python recorder via the db backend.

### 3. Open the trace

You can explore the recording immediately in the UI or later on:

- Launch the GUI with the most recent trace:

  ```bash
  ct replay
  ```

- Replay a specific Python trace by path:

  ```bash
  ct replay --trace-folder path/to/trace-output
  ```

- Or jump straight from recording to replay in one command:

  ```bash
  ct run path/to/script.py [-- <script arguments>]
  ```

All three commands replay the same trace artefacts `ct record` produced.

## Recording a web application

Flask, Django, FastAPI and any other WSGI/ASGI application are recorded as a
running server rather than a single script, and each HTTP request appears in
the Request Panel as it arrives. That has its own guide:
[Live requests — Python](/usage_guide/live-requests-python).

In short:

```bash
ct record --server --lang python -o ./trace -- flask --app app run --port 8000
```

Then `ct replay -t ./trace` in another terminal shows the requests live.

---

### Book Isonim Content Getting Started Ruby (`docs/book-isonim/content/getting_started/ruby.md`)

---
title: Ruby
order: 5
---
## Ruby

We currently have partial support for the Ruby programming language

The recorder for Ruby is currently hosted in the [codetracer-ruby-recorder](https://github.com/metacraft-labs/codetracer-ruby-recorder) repo.

## How to launch a program written in Ruby

Adjust the steps below for your use case or run the exact steps to launch the space_ship program which is included with the repo.

1. Navigate to CodeTracer's folder
2. Use ```ct record <path to rb file> [<args>]``` and ```ct replay <name of rb file>``` (or directly ```ct run <path to rb file> [<args>]```)

   Example: ```ct run examples/ruby_space_ship/main.rb```

:::caution
Recording ruby on macOS requires you to install ruby through [homebrew](https://brew.sh), otherwise trying to record ruby programs will fail due to the built-in ruby binary on macOS being more than 7 years old.

Once homebrew is installed, simply install ruby with `user $ brew install ruby`.
:::

## Recording a web application

Rack applications — including Ruby on Rails and Sinatra — are recorded as a
running server rather than a single script, and each HTTP request shows up
in the Request Panel. That has its own guide:
[Live requests — Ruby](/usage_guide/live-requests-ruby).

In short:

```bash
ct record --server --lang ruby -o ./trace -- rails server --port 3000
```

Then `ct replay -t ./trace` in another terminal shows requests as they
arrive.

:::note
Rails support is recent. If you tried Rails with CodeTracer some time ago and
it hung during boot, that was a recorder bug in integer conversion on large
values; it is fixed.
:::

---

### Book Isonim Content Getting Started Solana (`docs/book-isonim/content/getting_started/solana.md`)

---
title: Solana
order: 115
---
## Solana

CodeTracer supports tracing Solana programs (smart contracts) written in Rust. The recorder captures per-instruction register state from the SBF (Solana Bytecode Format) virtual machine and maps it back to Rust source code via DWARF debug info.

### Prerequisites

- **Solana CLI** and **Anchor** framework for building programs
- **codetracer-solana-recorder** binary installed or built from source

### Recording a Trace

1. Build your Solana program with debug info:

   ```bash
   cd my-solana-program
   anchor build
   ```

2. Record from a pre-generated register trace:

   ```bash
   codetracer-solana-recorder record target/deploy/my_program.so \
       --regs trace.regs \
       --elf target/deploy/my_program.so \
       -o ./ct-traces/
   ```

3. Or with an Anchor IDL for account data decoding:

   ```bash
   codetracer-solana-recorder record target/deploy/my_program.so \
       --regs trace.regs \
       --idl target/idl/my_program.json \
       -o ./ct-traces/
   ```

### Replaying an On-Chain Transaction

To debug a transaction from mainnet, devnet, or a local validator:

```bash
codetracer-solana-recorder replay \
    --signature 5abc123...xyz \
    --rpc-url https://api.mainnet-beta.solana.com \
    --program-dir target/deploy/ \
    -o ./ct-traces/
```

For local development with `solana-test-validator`:

```bash
codetracer-solana-recorder replay \
    --signature 5abc123...xyz \
    --rpc-url http://localhost:8899 \
    --program-dir target/deploy/ \
    -o ./ct-traces/
```

### Viewing the Trace

```bash
ct replay --trace-folder ./ct-traces/
```

### Key Flags

| Flag                       | Description                                                     |
| -------------------------- | --------------------------------------------------------------- |
| `record <ELF>`             | Record from a compiled `.so` ELF file                           |
| `--regs <FILE>`            | Pre-generated register trace file (`.regs` binary)              |
| `--elf <PATH>`             | Unstripped ELF for DWARF source mapping                         |
| `--idl <FILE>`             | Anchor IDL JSON for account data decoding                       |
| `replay --signature <SIG>` | Replay a transaction by its base-58 signature                   |
| `--rpc-url <URL>`          | Solana JSON-RPC endpoint (default: `http://localhost:8899`)     |
| `--program-dir <PATH>`     | Directory with compiled `.so` files (default: `target/deploy/`) |
| `-o, --out-dir <DIR>`      | Output directory (default: `./ct-traces/`)                      |
| `-f, --format <FMT>`       | Output format: `binary` or `json` (default: `binary`)           |

---

### Book Isonim Content Getting Started Solidity (`docs/book-isonim/content/getting_started/solidity.md`)

---
title: Solidity (EVM)
order: 109
---
## Solidity (EVM)

CodeTracer supports tracing Solidity smart contracts running on the Ethereum Virtual Machine. The recorder compiles your contract with `solc`, deploys it to a local Anvil node, and captures the full execution trace via `debug_traceTransaction`.

### Prerequisites

- **Solidity compiler** (`solc`) available in your PATH
- **Foundry** (`anvil`, `cast`) for local EVM execution
- **codetracer-evm-recorder** binary installed or built from source

### Recording a Trace

1. Write a Solidity contract with a `run()` function (or specify a custom entry point):

   ```solidity
   // SPDX-License-Identifier: MIT
   pragma solidity ^0.8.0;

   contract Example {
       function run() public pure returns (uint256) {
           uint256 a = 10;
           uint256 b = 32;
           uint256 result = a + b;
           return result;
       }
   }
   ```

2. Record the trace:

   ```bash
   codetracer-evm-recorder record example.sol --trace-dir ./ct-traces/
   ```

   This will:
   - Compile `example.sol` with `solc`
   - Start a local Anvil node
   - Deploy the contract and call `run()`
   - Capture the execution trace via `debug_traceTransaction`
   - Write the trace files to `./ct-traces/`

3. To call a different function:

   ```bash
   codetracer-evm-recorder record example.sol --trace-dir ./ct-traces/ --function myFunction
   ```

### Viewing the Trace

Open the recorded trace in CodeTracer:

```bash
ct replay --trace-folder ./ct-traces/
```

### Key Flags

| Flag                | Description                       |
| ------------------- | --------------------------------- |
| `<solidity-file>`   | Path to the `.sol` source file    |
| `--trace-dir <DIR>` | Output directory for trace files  |
| `--function <NAME>` | Function to call (default: `run`) |

---

### Book Isonim Content Getting Started Stylus (`docs/book-isonim/content/getting_started/stylus.md`)

---
title: Stylus
order: 3
---
## Getting Started with Stylus

This guide will walk you through tracing an Arbitrum Stylus smart contract with CodeTracer. The process involves deploying a contract and then sending transactions to it, which CodeTracer will automatically record.

The implementation is based on a `wazero` WASM interpreter and has been tested with Stylus contracts. You can find more information on `wazero` [here](https://wazero.io/).

### Prerequisites

*   **Rust `wasm32` Target:** You must have the `wasm32-unknown-unknown` target installed for Rust. You can add it by running:
    ```bash
    rustup target add wasm32-unknown-unknown
    ```
*   **Three Terminal Windows:** You will need three separate terminals to run all the necessary components.

### Environment Setup

To follow this guide, you need a local Arbitrum development node and Foundry's `cast` tool. We provide instructions for two setups: a Nix-based environment (recommended for Linux) and a manual setup for macOS.

<details>
<summary><b>Option 1: Nix-based Setup (Recommended for Linux)</b></summary>

This is the easiest way to get started if you have Nix installed.

In your **first terminal**, launch the Stylus development environment. This command will download all the necessary tools.

```bash
nix develop 'github:metacraft-labs/nix-blockchain-development?ref=stylus-tools#stylus'
```

Once inside the new shell, start the local Arbitrum node:

```bash
run-nitro-devnode
```

You should see it producing blocks. Keep this terminal running.

For sending transactions in Step 2, you will also use this Nix shell in your **third terminal** to get access to the `cast` command-line tool.

</details>

<details>
<summary><b>Option 2: Manual Setup (macOS)</b></summary>

If you are on macOS or do not use Nix, follow these steps.

**1. Install Foundry**

Install `foundryup` by following the instructions at https://getfoundry.sh. This will provide the `cast` command-line tool.

**2. Install Stylus**

Run the following command to install the Stylus CLI:
```bash
cargo install cargo-stylus
```
:::important
Make sure `cargo` is from a `rustup` installation, not from Homebrew. If you installed Rust via Homebrew, this step might fail.
:::

**3. Run the Local Devnode**

In your **first terminal**, clone and run the `nitro-devnode`:
```bash
git clone https://github.com/OffchainLabs/nitro-devnode.git
cd nitro-devnode
./run-dev-node.sh
```
You should see it producing blocks. Keep this terminal running.

</details>

### Step 1: Deploy the Demo Contract

This step is the same for both setups.

In your **second terminal**, navigate to the directory of the example program and use CodeTracer to deploy the smart contract. The `ct arb deploy` command handles this for you.

```bash
# Navigate to the example contract
cd test-programs/stylus_fund_tracker

# Deploy it to the local devnode
ct arb deploy
```

After a moment, CodeTracer will print the deployed smart contract address. **Copy this address**, as you will need it in the next step.

### Step 2: Send Transactions

In your **third terminal**, you will use the `cast` tool to send transactions.

-   **Nix users:** Enter the Stylus development shell first:
    ```bash
    nix develop 'github:metacraft-labs/nix-blockchain-development?ref=stylus-tools#stylus'
    ```
-   **macOS users:** The `cast` command should be available in your PATH if you installed Foundry correctly.

Next, set up some environment variables to make the commands easier to read. **Paste the contract address you copied from the previous step.**

```bash
# The private key for the pre-funded devnet account
export PK=0xb6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659

# The RPC URL for the local devnode
export RPC_URL=http://localhost:8547

# The address of your deployed contract
export CONTRACT_ADDR="<paste-your-contract-address-here>"
```

Now you can use `cast send` to call functions on your contract. These transactions will be automatically recorded by CodeTracer.

Let's send a few funding transactions and then trigger a custom event:

```bash
# Fund with 9
cast send --rpc-url "$RPC_URL" --private-key "$PK" "$CONTRACT_ADDR" "fund(uint256)" 9

# Fund with 6
cast send --rpc-url "$RPC_URL" --private-key "$PK" "$CONTRACT_ADDR" "fund(uint256)" 6

# Fund with 11
cast send --rpc-url "$RPC_URL" --private-key "$PK" "$CONTRACT_ADDR" "fund(uint256)" 11

# Trigger the largeIncomes function
cast send --rpc-url "$RPC_URL" --private-key "$PK" "$CONTRACT_ADDR" "largeIncomes(uint256)" 7
```

### Step 3: Explore the Recorded Traces

Go back to your **second terminal** (where you ran `ct arb deploy`). Now, run the CodeTracer explorer:

```bash
ct arb explorer
```

A window will open displaying the recent transactions. Select any transaction to view its full execution trace in CodeTracer.

---

### Book Isonim Content Getting Started Sway (`docs/book-isonim/content/getting_started/sway.md`)

---
title: Sway (FuelVM)
order: 116
---
## Sway (FuelVM)

CodeTracer supports tracing Sway smart contracts on the Fuel blockchain. The recorder captures per-instruction execution state from FuelVM and maps it back to Sway source code.

### Prerequisites

- **Fuel toolchain** (`forc`, `fuel-core`) for building and running contracts
- **codetracer-fuel-recorder** binary installed or built from source

### Recording a Trace

1. Create or navigate to a Sway project (must contain `Forc.toml`):

   ```bash
   forc new my_contract
   cd my_contract
   ```

2. Record the trace:

   ```bash
   codetracer-fuel-recorder record . -o ./ct-traces/
   ```

   Or from a pre-compiled bytecode file:

   ```bash
   codetracer-fuel-recorder record --bytecode out/debug/my_contract.bin -o ./ct-traces/
   ```

3. With an ABI file for variable enrichment:

   ```bash
   codetracer-fuel-recorder record . --abi out/debug/my_contract-abi.json -o ./ct-traces/
   ```

### Replaying an On-Chain Transaction

To debug a transaction from a Fuel node:

```bash
codetracer-fuel-recorder replay \
    --tx-id 0xabc123... \
    --rpc-url http://localhost:4000/v1/graphql \
    --source-dir ./out/debug/ \
    -o ./ct-traces/
```

### Viewing the Trace

```bash
ct replay --trace-folder ./ct-traces/
```

### Key Flags

| Flag                   | Description                                                              |
| ---------------------- | ------------------------------------------------------------------------ |
| `record [PROJECT_DIR]` | Record from a Sway project (must contain `Forc.toml`)                    |
| `--bytecode <FILE>`    | Raw FuelVM bytecode file                                                 |
| `--abi <FILE>`         | Sway ABI JSON for variable enrichment                                    |
| `replay --tx-id <ID>`  | Replay an on-chain transaction (hex with `0x` prefix)                    |
| `--rpc-url <URL>`      | fuel-core GraphQL endpoint (default: `http://localhost:4000/v1/graphql`) |
| `--source-dir <PATH>`  | Directory with forc build output                                         |
| `-o, --out-dir <DIR>`  | Output directory (default: `./ct-traces/`)                               |
| `-f, --format <FMT>`   | Output format: `binary` or `json` (default: `binary`)                    |

---

### Book Isonim Content Getting Started Tolk (`docs/book-isonim/content/getting_started/tolk.md`)

---
title: Tolk (TON)
order: 118
---
## Tolk (TON)

CodeTracer supports tracing Tolk (and FunC) smart contracts on the TON blockchain. The recorder instruments the TVM (TON Virtual Machine) to capture per-instruction execution state and maps TVM stack operations back to Tolk source code via debug sections.

### Prerequisites

- **Tolk compiler** for compiling contracts
- **Node.js** and **@ton/sandbox** for local contract testing
- **codetracer-ton-recorder** binary installed or built from source

### Recording a Trace

1. Write a Tolk contract:

   ```tolk
   fun compute(): int {
       var a: int = 10;
       var b: int = 32;
       var sumVal: int = a + b;
       var doubled: int = sumVal * 2;
       var finalResult: int = doubled + a;
       return finalResult;
   }
   ```

2. Record the trace:

   ```bash
   codetracer-ton-recorder record contract.tolk -o ./ct-traces/
   ```

### Recording from @ton/sandbox

If you use `@ton/sandbox` for contract testing, capture the VM logs and convert them:

```bash
codetracer-ton-recorder trace-sandbox \
    --vm-log vm_logs_full.txt \
    --source contract.tolk \
    -o ./ct-traces/
```

### Replaying an On-Chain Transaction

To debug a transaction from the TON blockchain:

```bash
codetracer-ton-recorder replay \
    --tx-hash abc123...def \
    --address EQBabc...xyz \
    --endpoint https://ton.org/global-config.json \
    --source-dir ./contracts/ \
    -o ./ct-traces/
```

### Viewing the Trace

```bash
ct replay --trace-folder ./ct-traces/
```

### Key Flags

| Flag                            | Description                                                         |
| ------------------------------- | ------------------------------------------------------------------- |
| `record <file>`                 | Record a Tolk source file (`.tolk`)                                 |
| `trace-sandbox --vm-log <FILE>` | Convert `@ton/sandbox` VM log output                                |
| `--source <FILE>`               | Path to Tolk source file (default: `contract.tolk`)                 |
| `replay --tx-hash <HASH>`       | Replay a TON transaction (hex-encoded)                              |
| `--address <ADDR>`              | Contract address that executed the transaction                      |
| `--endpoint <URL>`              | Liteserver endpoint (default: `https://ton.org/global-config.json`) |
| `--source-dir <PATH>`           | Contract source directory                                           |
| `-o, --out-dir <DIR>`           | Output directory (default: `./ct-traces/`)                          |
| `-f, --format <FMT>`            | Output format: `binary` or `json` (default: `binary`)               |

### Variable Reconstruction

The TVM has no named local variables — it uses a stack-based architecture. The recorder uses heuristic stack tracking combined with the Tolk compiler's debug sections to reconstruct variable names and values at each step.

---

### Book Isonim Content Getting Started Wasm (`docs/book-isonim/content/getting_started/wasm.md`)

---
title: WASM
order: 4
---
## WASM

The implementation is based on a [wazero]() WASM interpreter patch and can be used for various WASM languages.
Testing has been done using Sylus contracts and small rust-based WASM programs.

### Steps to record / replay a WASM program

Adjust the steps below for your use case or run the exact steps to launch the demo program which is included with the repo.

1. **Make sure you have a rust WASM toolchain** For example you can use the wasm32-wasip1 target, which we will use for the remainder of the guide

   1. Navigate to your WASM project directory.

   Example: ```cd codeTracer/examples/wasm_sample_atomic```

   2. Compile the rust source code to WASM

   Example: ```rustc --target=wasm32-wasip1 ./sample_atomic.rs -g```

   3. Use ```ct record <path to .wasm file> [<args>]``` and ```ct replay <name of .wasm file>``` (or directly ```ct run <path to .wasm file> [<args>]```)

   Example: ```ct run sample_atomic.wasm ```

---

### Book Isonim Content Reference Build Systems (`docs/book-isonim/content/reference/build_systems.md`)

---
title: Build systems
order: 0
---
## Build systems

Codetracer uses nix, just, tup and direnv as parts of its build system.

## Breakdown of the different components

### Nix

The [Nix](https://nixos.org/) package manager/build system deals with managing all dependencies, required by CodeTracer, as well as packaging
it for NixOS. It can package for other operating systems through an AppImage generator utility, however currently we are building our
appimages with custom shell scripts in `appimage-scripts`.

### Just

The [just](https://just.systems/) task runner is used to trigger build commands and various other actions. It's semi-analogous to a Makefile. The following commands are
the most widely-used:

1. `just build` - Builds the project with Tup and starts the automatic build process, which is used for active development
1. `just build-once` - Just builds the project using Tup
1. `just build-nix` - Builds the project and packages it for Nix
1. `just build-docs` - Builds the documentation. More info can be found [here](https://dev-docs.codetracer.com/Misc/BuildDocs)
1. `just attic-push-nix-package` - Pushes nix package artefacts to Attic
1. `just attic-push-devshell` - Pushes the current dev shell to Attic
1. `just reset-db` - Resets the local user's trace database
1. `just clear-local-traces` - Clears the local user's traces
1. `just reset-layout` - Resets the GUI window arrangements layout if your user's layout is incompatible with the latest version of CodeTracer. Further [documentation](https://dev-docs.codetracer.com/Introduction/Configuration)
1. `just reset-config` - Resets the user's configuration if it's incompatible with the latest version of CodeTracer. Further [documentation](https://dev-docs.codetracer.com/Introduction/Configuration)

### Tup

The [Tup](https://gittup.org/tup/) build system is used for local builds of CodeTracer and deals with calling the actual low-level build instructions.

### Direnv
The [direnv](https://direnv.net/) utility sets up your local environment for using CodeTracer.

## Packaging

### More detailed breakdown of the Nix package

Coming soon!

### Packaging for non-NixOS distributions

:::tip
If you're a user that wants a package for your distribution contact us. We're currently in the process of creating
packages for popular distributions, such as Debian/Ubuntu, Fedora/RHEL, Arch Linux, Gentoo, Void, etc.
:::

To package for another Linux distribution with a ports-based package manager, you can utilise our AppImage(currently unreleased),
which you can install to `/usr/bin`. Along with it, you should also install our icon and desktop file
from `resources/` to the needed directories, such as `/usr/share/pixmaps` and `/usr/share/applications`.

### Packaging for Windows(DB-backend only)

Coming soon!

### Packaging for macOS(DB-backend only)

Coming soon!

---

### Book Isonim Content Reference Building Docs (`docs/book-isonim/content/reference/building_docs.md`)

---
title: Building the documentation
order: 4
---
## Building the documentation

The documenation for codetracer is written in Markdown using [mdbook](https://rust-lang.github.io/mdBook/).
We use some extensions from [GitHub Flavoured Markdown](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax) : specifically alert boxes with [mdbook-alerts](https://crates.io/crates/mdbook-alerts).

To build the documentation run `just build-docs`. If you want to iterate on the documentation for local development, run `just serve-docs [<hostname>] [<port>]` or by going in the docs directory and running `mdbook serve [--hostname <hostname>] [--port <port>]` and a web server will be started, by default on http://localhost:3000 .

The built doc files are stored under `docs/experimental-documentation/build`, while the markdown files are under `docs/experimental-documentation` and its child directories.

---

### Book Isonim Content Reference Contributing (`docs/book-isonim/content/reference/contributing.md`)

---
title: Contributing
order: 1
---
## Contributing

We welcome contributors!

If you want to fix something smaller, feel free to open an issue or a a PR.

For bigger changes it's advised to first open an issue/discussion in the relevant Github repo or to discuss it our team in our [Discord chat server](https://discord.gg/aH5WTMnKHT).

### Contributors guide

Here are some recommendations, however if you want more info, our docs/contributor guide are hosted [on the CodeTracer site](https://contributors-guide.codetracer.com/) !

The guide is written as a set of markdown documents and built using [mdbook](https://rust-lang.github.io/mdBook/) and [mdbook-alerts](https://crates.io/crates/mdbook-alerts) .

You can contribute to the documentation itself, by editing it in `docs/book` and making a pull request. You can iterate on it locally , by cloning the repo, activating it's nix devshell and running `just serve-docs [hostname port]`: it will serve it locally(by default on http://localhost:3000).

### Style guide

For Rust, we are using `cargo fmt` to autoformat our code, and `cargo clippy` with some custom allow/deny rules inside the code.
We have a `cargo clippy` check in our CI, but one can also run it locally.

For Nim, we still haven't written down a guide or list of rules and principles that we agree on, so this is something that we hope to do.
We might also link to an existing document.

### Commits/Pull Requests

We are using [the "Conventional Commits" strategy](https://www.conventionalcommits.org/).

We use or are ok with using more "types", not only those included by default in their official page: e.g. `cleanup:`, `tooling:`, `examples:` etc.

We use `git rebase`, not merge and currently use the github pull requests as the main way to add code. Any pull request would need at least one review
from someone from the CodeTracer team.

---

### Book Isonim Content Reference Ct Cli (`docs/book-isonim/content/reference/ct_cli.md`)

---
title: ct CLI Reference
order: 101
---
## ct CLI Reference

The `ct` command is the main CodeTracer CLI. It records program executions, replays traces, and manages the CodeTracer environment.

### Synopsis

```
ct <command> [options] [<program>] [<args>]
```

### Commands

#### Recording and Replay

| Command                                       | Description                                                          |
| --------------------------------------------- | -------------------------------------------------------------------- |
| `ct run <program> [args]`                     | Record and immediately open in the GUI                               |
| `ct record <program> [args]`                  | Record a trace to disk                                               |
| `ct replay`                                   | Open a trace in the GUI                                              |
| `ct import <trace-folder>`                    | Import a trace from a folder                                         |
| `ct trace extract-gfx -o <dir> <trace>`       | Extract the graphics stream from a `.ct` container                   |
| `ct trace export --portable -o <out> <trace>` | Export a portable trace (embeds binaries and debug symbols)          |
| `ct gfx-replay --gfx-stream <dir>`            | Replay an extracted graphics stream (used by the visual replay GUI)  |

#### DeepReview

| Command                      | Description                                                          |
| ---------------------------- | -------------------------------------------------------------------- |
| `ct review <PATH>`           | Open a review over an exported review dataset                        |
| `ct review collect [options]`| Produce a review dataset from recordings + a diff                    |
| `ct review inspect <PATH>`   | Summarise a dataset without opening a GUI                            |
| `ct agent evidence <PATH>`   | Hand a review dataset to CodeTracer from inside an agent session     |
| `ct agent end-of-turn`       | Run `ct review collect` then `ct agent evidence`, for a project hook |
| `ct agent prompt`            | Print the prompt text that teaches an agent the pair                 |

See the [DeepReview](/deep_review) section for the workflow.

#### Stylus / EVM

| Command           | Description                                       |
| ----------------- | ------------------------------------------------- |
| `ct arb deploy`   | Deploy a Stylus contract to a local devnode       |
| `ct arb explorer` | Open the transaction explorer for recorded traces |
| `ct arb record`   | Record a Stylus contract execution                |
| `ct arb replay`   | Replay a Stylus trace                             |

#### CI Integration

| Command        | Description                           |
| -------------- | ------------------------------------- |
| `ct ci start`  | Start a CI recording session          |
| `ct ci attach` | Attach to a running CI session        |
| `ct ci exec`   | Execute a command within a CI session |
| `ct ci finish` | Finalize a CI session                 |
| `ct ci run`    | Run a command with CI recording       |
| `ct ci log`    | View CI session logs                  |
| `ct ci status` | Check CI session status               |
| `ct ci cancel` | Cancel a CI session                   |

#### Utility

| Command               | Description                                                                       |
| --------------------- | --------------------------------------------------------------------------------- |
| `ct install`          | Install the CLI tools                                                             |
| `ct version`          | Print the CodeTracer version                                                      |
| `ct help`             | Display help information                                                          |
| `ct list`             | List recorded traces                                                              |
| `ct console`          | Open the interactive console                                                      |
| `ct doctor <lang>`    | Probe recorder-readiness for a language (currently `python`; more languages soon) |

#### Online Sharing

| Command       | Description                           |
| ------------- | ------------------------------------- |
| `ct upload`   | Upload a trace                        |
| `ct download` | Download a trace                      |
| `ct login`    | Authenticate with the sharing service |

### ct record

Records the execution of a program into a trace.

```
ct record [options] <program> [-- <program-args>]
```

**Options:**

| Flag                          | Description                                                  |
| ----------------------------- | ------------------------------------------------------------ |
| `--lang <LANG>`               | Override language detection (e.g., `noir`, `python`, `ruby`) |
| `-o, --output-folder <DIR>`   | Output directory for trace files                             |
| `--backend <BACKEND>`         | Recording backend (e.g., `plonky2` for Noir)                 |
| `-e, --export <ZIP>`          | Export trace as a zip archive                                |
| `-c, --cleanup-output-folder` | Remove the output folder after export                        |
| `--trace-kind <KIND>`         | Trace kind: `db` (default), `rr`, or `ttd`                   |
| `--rr-support-path <PATH>`    | (internal) Override path for the RR-backend DAP binary       |
| `--python-interpreter <PATH>` | Path to the Python interpreter to use                        |
| `--pytest [ARGS]`             | Run pytest with the given arguments                          |
| `--unittest [ARGS]`           | Run unittest with the given arguments                        |
| `-t, --stylus-trace <PATH>`   | Path to a Stylus trace file                                  |
| `-a, --address <ADDR>`        | Contract address (Stylus)                                    |
| `--socket <PATH>`             | Unix socket path for event reporting                         |
| `--use-interpose`             | Record graphics API calls for visual replay (MCR backend only) |

**Language detection:** When `--lang` is not provided, `ct` detects the language from the file extension or project structure:

| Extension / Marker              | Language                  |
| ------------------------------- | ------------------------- |
| `.py`                           | Python                    |
| `.rb`                           | Ruby                      |
| `.nr` or `Nargo.toml`           | Noir                      |
| `.wasm`                         | WASM                      |
| `.small`                        | Small                     |
| `Cargo.toml` with wasm32 target | Rust WASM                 |
| `Cargo.toml`                    | Rust (native, via rr/TTD) |

:::note
Blockchain-specific languages (Circom, Cairo, Aiken, Cadence, Move, Sway, Miden, PolkaVM, Leo, Tolk) use their own recorder binaries. See the [Getting Started](/getting_started) guides for each language.
:::

:::note
Visual recordings for native graphics programs are MCR `.ct` traces produced with `ct record --use-interpose`. CodeTracer opens these traces in the GUI and starts the visual replay player automatically. See [Visual recordings](../usage_guide/visual_recordings.md).
:::

### ct replay

Opens a previously recorded trace in the CodeTracer GUI.

```
ct replay [options] [<program-name>]
```

**Options:**

| Flag                   | Description                                 |
| ---------------------- | ------------------------------------------- |
| `<program-name>`       | Open the most recent trace for this program |
| `--id=<TRACE_ID>`      | Open a trace by its numeric ID              |
| `--trace-folder=<DIR>` | Open a trace from a specific directory      |

When called without arguments, `ct replay` opens an interactive dialog to choose from recent traces.

### ct run

Records a program and immediately opens the trace in the GUI. Equivalent to `ct record` followed by `ct replay`.

```
ct run [options] <program> [-- <program-args>]
```

Accepts the same options as `ct record`.

### ct review

Opens a review over an exported review dataset — the diff, its recorded
executions, coverage and flow. See
[Reading a review](/deep_review/reading).

```
ct review <PATH>
```

| Argument | Description                                                                            |
| -------- | --------------------------------------------------------------------------------------- |
| `<PATH>` | A `review.json` file, or the directory `ct review collect` wrote it into. Required.     |

`ct review` is an ordinary `ct` command, so `ct`'s global options (`--cwd`,
`--inspect`, `--remote-debugging-port`, the `--env-file` family) work with it.
A path that does not exist is diagnosed by `ct` rather than by the renderer.

`ct review` on its own — and `ct review --help` — prints the whole group's
usage, not just the launch form.

:::note
`ct --deepreview <PATH>` and the `ct-native-replay deepreview
{collect,export,inspect}` subcommands are retired. They fail with a message
naming the `ct review` replacement rather than being kept as aliases.
:::

### ct review collect

Produces a review dataset from a set of recordings and a diff. The collector is
chosen by **inspecting the recordings** — native/rr recordings go to the native
replay backend, materialized traces (Python, Ruby, JavaScript, Noir, …) go to
the db-backend collector. There is no `--backend` flag.

```
ct review collect (--repo <DIR> --diff <BASE..HEAD> | --diff-file <PATH>) \
                  --recordings <DIR> --output <DIR> \
                  [--preset <NAME>] [--progress]
```

**Options:**

| Flag                   | Required                       | Default   | Description                                                                      |
| ---------------------- | ------------------------------ | --------- | ---------------------------------------------------------------------------------- |
| `--repo <DIR>`         | Yes (unless `--diff-file`)     | —         | Git repository the diff is read from                                             |
| `--diff <BASE..HEAD>`  | Yes (unless `--diff-file`)     | —         | Diff specification, e.g. `main..HEAD`. Must contain `..`                         |
| `--diff-file <PATH>`   | Yes (unless `--repo`+`--diff`) | —         | A unified diff file instead of `--repo`/`--diff`. Cannot be combined with either |
| `--recordings <DIR>`   | Yes                            | —         | Directory holding the recordings, one subdirectory per run                       |
| `--output <DIR>`, `-o` | Yes                            | —         | Directory the dataset is written to                                              |
| `--preset <NAME>`      | No                             | `default` | Collection preset: `default`, `minimal` or `comprehensive`                       |
| `--progress`           | No                             | off       | Emit JSON Lines progress events on stderr                                        |

Both `--output DIR` and `--output=DIR` are accepted. `collect` takes no
positional arguments.

`collect` writes `review.json` into `--output` beside the collector's own
chunks, so `ct review <the same DIR>` opens exactly what it produced. If the
export half fails, the command says so and exits non-zero rather than reporting
a partial success.

**Presets** differ in sampling intensity only: `minimal` keeps one sample per
line and one execution per function (fastest, smallest); `default` keeps 3 and
10; `comprehensive` keeps 10 and 50 with a deeper value limit.

**Progress events** (`--progress`) are one JSON object per line on stderr:

```
{"fileCount":1,"recordingCount":2,"type":"collection_started"}
{"percentage":0.0,"recordingIndex":0,"recordingTotal":2,"type":"recording_progress"}
{"percentage":50.0,"recordingIndex":1,"recordingTotal":2,"type":"recording_progress"}
{"coverageLines":8,"flowSteps":42,"path":"src/main.nr","symbols":1,"type":"file_collected"}
{"elapsedMs":68,"fileCount":1,"totalFlowSteps":42,"type":"collection_completed"}
```

One `recording_progress` is emitted per recording, before that recording is
collected, so the stream is as long as `--recordings` is deep.

**Diagnostics.** A recordings directory that mixes native and materialized
recordings is refused with a message naming both kinds. An empty directory, a
directory holding no recordings, and a `--recordings` path that does not exist
each get their own message and a non-zero status — never a silently empty
dataset.

### ct review inspect

Reads a dataset and prints summary statistics without opening a GUI.

```
ct review inspect <PATH> [--format text|json]
```

| Flag                | Default | Description                            |
| ------------------- | ------- | -------------------------------------- |
| `<PATH>`            | —       | The dataset directory. Required.       |
| `--format <FORMAT>` | `text`  | `text` (human-readable) or `json`      |

:::note
`inspect` reads the **native** collector's dataset layout (the directory
containing `manifest.dr`) and delegates to the native replay backend. A dataset
produced from a materialized trace is a single `review.json` with no chunks, so
`inspect` reports the missing `manifest.dr`; open it with `ct review`, or read
the JSON directly.
:::

### ct agent evidence

Hands a review dataset to CodeTracer from inside an agent session.

```
ct agent evidence <PATH> [--session <ID>] [--task <ID>] [--workspace <DIR>]
```

| Flag                | Description                                                                         |
| ------------------- | ------------------------------------------------------------------------------------- |
| `<PATH>`            | The dataset: a directory from `ct review collect`, or the `review.json` in it. Required. |
| `--session <ID>`    | The agent session this evidence belongs to                                          |
| `--task <ID>`       | The task the session is working on                                                  |
| `--workspace <DIR>` | The workspace the session is working in                                             |

All three are read from `CODETRACER_AGENT_SESSION_ID`,
`CODETRACER_AGENT_TASK_ID` and `CODETRACER_AGENT_WORKSPACE` — the variables the
agent harness already exports — and the flags override them. The session falls
back to the reference `ct review collect` stamped into the dataset. If none of
the three names a session, the command exits non-zero and says which to supply;
it never invents an id.

The changeset, the recordings and the status the notification carries are read
from the dataset, not restated by the agent.

### ct agent end-of-turn

Runs the two ordinary commands, in order, printing each as it runs it — for use
as a project's end-of-turn hook.

```
ct agent end-of-turn [collect options] [--session <ID>] [--task <ID>] [--workspace <DIR>]
```

It accepts all of `ct review collect`'s options (`--repo`, `--diff`,
`--diff-file`, `--recordings`, `--output`/`-o`, `--preset`, `--progress`) plus
the three above, and fills unset ones from the environment so a hook line can be
bare:

| Variable                       | Fills in       | Default        |
| ------------------------------ | -------------- | -------------- |
| `CODETRACER_REVIEW_REPO`       | `--repo`       | the workspace  |
| `CODETRACER_REVIEW_DIFF`       | `--diff`       | —              |
| `CODETRACER_REVIEW_DIFF_FILE`  | `--diff-file`  | —              |
| `CODETRACER_REVIEW_RECORDINGS` | `--recordings` | `.ct/runs`     |
| `CODETRACER_REVIEW_OUTPUT`     | `--output`     | `.ct/review`   |

### ct agent prompt

Prints the prompt text that teaches an agent the collect-then-hand-over pair.
Install it into a project's agent instructions with:

```bash
ct agent prompt >> AGENTS.md
```

The text describes only commands that ship. In particular it does not mention
test certificates, which `ct test` does not yet issue.

### ct trace origin

Prints the backward dataflow chain (an "origin chain") for a variable.
Walks assignments / parameter passes / return captures / field-or-index
accesses backward from the queried `(variable, step, frame)` until a
terminator (computational expression, literal, parameter at the
recording boundary, etc.) is reached. Drives the same `ct/originChain`
DAP request as the `Trace.value_origin(...)` Python binding and the
`get_value_origin` MCP tool.

```
ct trace origin <trace-path> --variable <NAME> [options]
```

**Options:**

| Flag                              | Description                                                                                       |
| --------------------------------- | ------------------------------------------------------------------------------------------------- |
| `<trace-path>`                    | Path to the trace directory (required positional argument).                                       |
| `--variable <NAME>`               | Variable identifier to query. V1 is identifier-only; dotted paths are reserved. **Required.**     |
| `--step <N>`                      | Step id at which to query. Defaults to the trace's current execution point.                       |
| `--frame <N>`                     | DAP frame id at which to query. Defaults to the topmost frame.                                    |
| `--max-hops <N>`                  | Maximum hops in this batch. Default: `16`.                                                        |
| `--format <json\|markdown\|text>` | Output renderer. Default: `text`.                                                                 |
| `--lazy`                          | Allow the backend to return a `continuationToken` instead of walking the full chain in one shot.  |

**Formats:**

- `text` — ASCII layout matching spec §3.2 (newest hop first, terminator
  at the bottom, frame-transition glyphs inline). Good for terminal
  copy-paste.
- `markdown` — fenced chain with classification badges and a per-hop
  table. Paste straight into a bug report.
- `json` — canonical `OriginChain` wire schema, pretty-printed. Use for
  scripting or downstream tooling.

**Example:**

```bash
ct trace origin /traces/my-bug.ct --variable total --step 137 --format text
```

For multi-step agent workflows that need to compose origin lookups with
locals/history/breakpoints, prefer `ct trace exec --script <file.py>
<trace-path>` and call `trace.value_origin(...)` inside the script — the
trace stays loaded across calls and the classifier's pattern cache is
reused.

See [Value Origin Tracking](../usage_guide/value-origin-tracking.md) for
the user-facing walkthrough.

### ct trace extract-gfx

Extracts the graphics stream from a `.ct` trace container into a directory the visual replay player can consume.

```
ct trace extract-gfx -o <output-dir> <trace>
```

**Options:**

| Flag                       | Description                                                            |
| -------------------------- | ---------------------------------------------------------------------- |
| `<trace>`                  | Path to the `.ct` trace container to extract (required positional).    |
| `-o, --output-dir <DIR>`   | Directory to extract the graphics stream into. Required.               |

See [Visual recordings](../usage_guide/visual_recordings.md) for the full workflow.

### ct trace export

Exports a recorded trace to a single file, optionally producing a portable bundle with embedded binaries and debug symbols.

```
ct trace export [--portable] -o <output> <trace>
```

**Options:**

| Flag                  | Description                                                        |
| --------------------- | ------------------------------------------------------------------ |
| `<trace>`             | Path to the source trace to export (required positional).          |
| `--portable`          | Produce a portable export with embedded binaries and debug symbols.|
| `-o, --output <PATH>` | Output path for the exported trace (required).                     |

### ct gfx-replay

Replays an extracted graphics stream. The CodeTracer GUI launches this automatically when opening a visual `.ct` trace; running it directly is useful for diagnostics.

```
ct gfx-replay --gfx-stream <dir> [--http --port <N>] [--backend <BACKEND>]
```

**Options:**

| Flag                  | Description                                                                |
| --------------------- | -------------------------------------------------------------------------- |
| `--gfx-stream <DIR>`  | Path to the extracted graphics-stream directory (required).                |
| `--http`              | Start the player as an HTTP server (used by the GUI).                      |
| `--port <N>`          | Port for the HTTP player (only meaningful with `--http`).                  |
| `--backend <BACKEND>` | Rendering backend selector — e.g. `software` or `hardware`.                |

### ct doctor

Probes recorder-readiness for a language: reports the interpreter or runtime that would be used and whether the matching recorder package is installed and importable.

```
ct doctor <language>
```

**Arguments:**

| Argument     | Description                                                                                  |
| ------------ | -------------------------------------------------------------------------------------------- |
| `<language>` | Recorder to probe. Currently `python` is wired; more recorders will land in future releases. |

Run this when `ct record` fails with a recorder import error to confirm which interpreter `ct` is targeting and whether the recorder package is present.

### ct trace exec

Runs a Python replay-script against a recording. The script body is the
same one consumed by the MCP `exec_script` tool, so the same logic runs
from a terminal session and from an agent workflow.

```
ct trace exec --script <file.py> <trace-path>
```

Inside the script a `trace` object is pre-bound to the loaded recording.
Methods include `trace.locals()`, `trace.history()`, navigation
(`trace.step_over()` etc.), and `trace.value_origin(...)` for origin
chains. See the
[Python API reference](https://github.com/metacraft-labs/codetracer/tree/main/python-api)
for the full surface.

### Environment Variables

| Variable                        | Description                                                             |
| ------------------------------- | ----------------------------------------------------------------------- |
| `CODETRACER_PYTHON_INTERPRETER` | Path to the Python interpreter for recording                            |
| `CODETRACER_NOIR_EXE_PATH`      | Path to the Noir tracer binary                                          |
| `CODETRACER_WASM_VM_PATH`       | Path to the WASM VM binary (wazero)                                     |
| `CODETRACER_RECORDING`          | Set to `1` during recording                                             |
| `CODETRACER_CALLTRACE_MODE`     | Call trace mode: `FullRecord`, `RawRecordNoValues`, `NoInstrumentation` |
| `CODETRACER_SHELL_ID`           | Shell session ID (for CodeTracer Shell)                                 |
| `CODETRACER_CT_MCR_CMD`         | Override the internal MCR binary that `ct trace extract-gfx` invokes     |
| `CODETRACER_CT_GFX_PLAYER_CMD`  | Override the internal player binary that `ct gfx-replay` launches        |
| `CODETRACER_CT_GFX_PLAYER_BACKEND` | Default backend for `ct gfx-replay` (equivalent to `--backend`), for example `software` |
| `CODETRACER_NATIVE_REPLAY_PATH` | Path to the `ct-native-replay` binary used by native `ct review collect` / `ct review inspect` |
| `CODETRACER_AGENT_SESSION_ID`   | Agent session `ct agent evidence` and `ct review collect` attribute a review to |
| `CODETRACER_AGENT_TASK_ID`      | Task the agent session is working on                                    |
| `CODETRACER_AGENT_WORKSPACE`    | Workspace the agent session is working in                               |
| `CODETRACER_REVIEW_REPO`        | `ct agent end-of-turn` default for `--repo`                             |
| `CODETRACER_REVIEW_DIFF`        | `ct agent end-of-turn` default for `--diff`                             |
| `CODETRACER_REVIEW_DIFF_FILE`   | `ct agent end-of-turn` default for `--diff-file`                        |
| `CODETRACER_REVIEW_RECORDINGS`  | `ct agent end-of-turn` default for `--recordings` (falls back to `.ct/runs`) |
| `CODETRACER_REVIEW_OUTPUT`      | `ct agent end-of-turn` default for `--output` (falls back to `.ct/review`) |

### Output Format

All recordings produce a trace directory containing:

| File                        | Description                                             |
| --------------------------- | ------------------------------------------------------- |
| `trace.bin` or `trace.json` | The trace data (binary or JSON format)                  |
| `trace_metadata.json`       | Metadata about the trace (language, program, timestamp) |
| `trace_paths.json`          | Source file paths referenced in the trace               |
| `symbols.json`              | Extracted symbols (for Noir and some languages)         |

---

### Book Isonim Content Reference Environment Variables (`docs/book-isonim/content/reference/environment_variables.md`)

---
title: Environment variables
order: 3
---
## Environment variables

CodeTracer exposes a number of environment variables that you can use to override some of its behaviours:

for many of the flags, we expect "1" to enable them

1. `CODETRACER_ELECTRON_ARGS` - adds arguments for launching Electron. Useful for debugging production builds
1. `CODETRACER_WRAPPER_PID` - overrides the process ID of the `ct` CodeTracer wrapper
1. `CODETRACER_CALLTRACE_MODE` - changes the calltrace mode
1. `CODETRACER_RECORD_CORE` - this does nothing as it is only related to the unreleased system backend
1. `CODETRACER_SHELL_SOCKET` - this sets the socket path for sending events to the CI integration from `ct record`(or eventually `ct shell`)
1. `CODETRACER_SHELL_ADDRESS` - this sets the address for sending events to the CI integration from `ct record`(or eventually `ct shell`)
1. `CODETRACER_SHELL_EXPORT` - this enables export mode for `ct record` on: exporting the traces into zip files in the folder that is the value of this env variables; (similarly to the `ct record -e=<zippath>` option, but for all records while the variable is enabled). The trace archives try to use a globally unique id in their filenames, from `std/oids` in the nim stdlib: https://nim-lang.org/docs/oids.html
1. `CODETRACER_DEBUG_CURL` - if "1", print debug output for the raw objects sent with curl for the CI integration from `ct record`(or eventually `ct shell`)
1. `CODETRACER_DEBUG_CT_REMOTE` - if "1", print debug output(for now process command name and arguments) for the trace sharing code and commands that call `ct-remote`(desktopclient), e.g. `ct upload`, `ct download`, `ct login`, `ct set-default-org`
1. `CODETRACER_RR_SOFT_MODE` - if we are using the `rr.soft` patch in the `rr` used by `ct`: if "1", runs `rr record` and `rr replay` with `-W` flag of `rr.soft`: software counters/dynamic
instrumentation. rr.soft is https://github.com/sidkshatriya/rr.soft
1. `CODETRACER_RECORD_BACKEND` - this is a newer version of `CODETRACER_RECORD_CORE`: if set, and if `ct` is in PATH, lets you record the internal db-backend process when doing `ct replay`
e.g. `env CODETRACER_RECORD_BACKEND=1 ct replay db-trace`
there might be problem if you try to replay a rr trace and record that:
it seems it might work, but i think `rr under rr` might be still problematic


## CodeTracer Shell
These are generally not functional right now, since they affect CodeTracer Shell, which is currently not stable/in very prototypical state:

1. `CODETRACER_SHELL_BASH_LOG_FILE` - overrides the log file
1. `CODETRACER_SHELL_ID` - overrides the shell ID
1. `CODETRACER_SESSION_ID` - overrides the CodeTracer Shell session ID so that the current commands affect a previous shell session
1. `CODETRACER_SHELL_REPORT_FILE` - overrides the report file of CodeTracer Shell
1. `CODETRACER_SHELL_USE_SCRIPT` - ?
1. `CODETRACER_SHELL_RECORDS_OUTPUT` - ?
1. `CODETRACER_SHELL_CLEANUP_OUTPUT_FOLDER` - ?

---

### Book Isonim Content Reference Logs (`docs/book-isonim/content/reference/logs.md`)

---
title: Logs and Diagnostics
order: 102
---
# Logs and Diagnostics

CodeTracer consists of several components that each produce their own log
files. When reporting a bug or asking for help, attaching the relevant logs
helps us diagnose the problem quickly.

## Log locations

All runtime logs are written under a temporary directory:

| OS | Base path |
|----|-----------|
| Linux | `/tmp/codetracer/` |
| macOS | `~/Library/Caches/com.codetracer.CodeTracer/` |
| Windows | `%TEMP%\codetracer\` |

Each CodeTracer session creates a `run-<PID>/` subdirectory containing logs
for that session. The most recent session has the highest PID number.

### Log files per component

| File | Component | Contents |
|------|-----------|----------|
| `run-<PID>/replay-server.log` | Replay server (trace loading, DAP handling) | Trace metadata, event loading, DAP request/response |
| `ct-native-replay-stable-0.log` | Native replay worker (main thread) | RR/MCR replay queries, register/memory reads |
| `ct-native-replay-flow-0.log` | Native replay worker (flow preloader) | Flow data preloading |
| `ct-native-replay-tracepoint-0.log` | Native replay worker (tracepoint evaluator) | Event loading, tracepoint evaluation |
| `rr-stderr-stable-0.log` | RR replay engine stderr | RR process output |
| `run-<PID>/session-manager.log` | Session manager (backend coordinator) | Session lifecycle, worker spawning |

### Finding the most recent logs

```bash
# Linux/macOS: list the most recent session directory
ls -td /tmp/codetracer/run-* | head -1

# Show all logs from the last session
ls -la $(ls -td /tmp/codetracer/run-* | head -1)

# View the replay server log
cat $(ls -td /tmp/codetracer/run-* | head -1)/replay-server.log
```

On Windows (PowerShell):
```powershell
$latest = Get-ChildItem "$env:TEMP\codetracer\run-*" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
Get-ChildItem $latest
Get-Content "$latest\replay-server.log"
```

## Enabling debug logs

By default, CodeTracer logs at the `info` level — only high-level progress
messages and errors. To get detailed diagnostic output for troubleshooting,
enable debug logging:

### Environment variable (recommended)

```bash
# Linux/macOS
RUST_LOG=debug ct replay my-trace

# Or for maximum detail
RUST_LOG=trace ct replay my-trace
```

On Windows:
```powershell
$env:RUST_LOG = "debug"
ct replay my-trace
```

### Selective component logging

You can enable debug logs for specific components:

```bash
# Only the replay server
RUST_LOG=replay_server=debug ct replay my-trace

# Only the session manager
RUST_LOG=session_manager=debug ct replay my-trace

# Both
RUST_LOG=replay_server=debug,session_manager=debug ct replay my-trace
```

## Collecting logs for a bug report

When filing a bug report, include:

1. **The replay server log** from the failing session:
   ```bash
   cat $(ls -td /tmp/codetracer/run-* | head -1)/replay-server.log
   ```

2. **The native replay worker log** (if the issue involves trace replay):
   ```bash
   cat /tmp/codetracer/ct-native-replay-stable-0.log
   ```

3. **Steps to reproduce** — what commands you ran, what you clicked

4. **The trace file** (if possible) — smaller traces are better. You can
   export a portable trace with:
   ```bash
   ct trace export --portable -o trace-portable.ct /path/to/trace.ct
   ```

### Quick log bundle

Collect all logs from the last session into a zip:

```bash
# Linux/macOS
LOGDIR=$(ls -td /tmp/codetracer/run-* | head -1)
zip -j codetracer-logs.zip \
  "$LOGDIR"/*.log \
  /tmp/codetracer/ct-native-replay-*.log \
  2>/dev/null
echo "Logs saved to codetracer-logs.zip"
```

## Frontend logs

The CodeTracer frontend (the GUI) logs to the browser/Electron console.
These logs are visible in the browser's Developer Tools (F12).

### Desktop (Electron)

Open the Developer Tools console:

- **Menu**: View → Toggle Developer Tools
- **Keyboard**: `Ctrl+Shift+I` (Linux/Windows) or `Cmd+Option+I` (macOS)

The console shows frontend log messages with timestamps, log levels
(DEBUG/WARN/ERROR), file locations, and task IDs.

To capture Electron console output to a file, launch CodeTracer with
the `--enable-logging` flag:

```bash
ct replay my-trace -- --enable-logging --log-file=/tmp/ct-frontend.log
```

### Browser mode (ct host)

Open the browser's Developer Tools (F12) and switch to the Console tab.
Frontend log messages appear with the same format as in Electron.

To filter CodeTracer messages from other page output, type `ct` or
`codetracer` in the console filter box.

### Index/server process (ct host)

The `ct host` process logs to stdout. When running `ct host` from a
terminal, these messages appear directly. In the Playwright test
infrastructure, they are captured via the process's stdout pipe.

## Configuration file

CodeTracer's configuration is stored at:

| OS | Path |
|----|------|
| Linux | `~/.config/codetracer/.config.yaml` |
| macOS | `~/Library/Application Support/codetracer/.config.yaml` |
| Windows | `%APPDATA%\codetracer\.config.yaml` |

Include this file in bug reports when the issue might be configuration-related.

## Trace database

The local trace database is stored at:

| OS | Path |
|----|------|
| Linux | `~/.local/share/codetracer/` |
| macOS | `~/Library/Application Support/codetracer/` |
| Windows | `%LOCALAPPDATA%\codetracer\` |

Each trace is stored in a `trace-<ID>/` subdirectory. To reset the database:

```bash
just reset-db
just clear-local-traces
```

---

### Book Isonim Content Reference Mcp Tools (`docs/book-isonim/content/reference/mcp-tools.md`)

---
title: MCP Tool Reference
order: 102
---
## MCP Tool Reference

CodeTracer ships an **MCP (Model Context Protocol) server** that exposes
trace querying as tools for LLM agents. The server is part of the
`backend-manager` binary and communicates over stdio using JSON-RPC 2.0.

Start it with:

```
backend-manager trace mcp
```

Any MCP-compatible client can spawn the process and exchange
newline-delimited JSON-RPC messages on stdin/stdout. See the
`backend-manager` setup guide for Claude Code / Claude Desktop wiring.

This page documents the **value-origin** tools registered by the M8
milestone. For the full tool list see the tool registration in
`src/backend-manager/src/mcp_server.rs`.

### `get_value_origin`

Returns the canonical `OriginChain` for a variable at a chosen source
location: where the value came from, hop by hop, down to the literal,
computation, or parameter that produced it.

The query is anchored at a **source location**, not at a step id. An MCP
call is stateless and `ct/open-trace` rewinds the replay to the program
entry, where no interesting variable is live yet; the tool therefore sets
a breakpoint at `path`:`line`, runs to it, and queries the step it
stopped on — the same sequence the per-language `origin_*_dap_test.rs`
suites use. If the line is never reached, the call fails rather than
answering about whatever step the replay ended on.

For multi-query sessions that reuse one loaded trace, prefer sending a
Python script through the `exec_script` tool and calling
`trace.value_origin("<variable>", step=..., frame=..., max_hops=...)`
inside it. The Python binding composes with `trace.locals()`,
`trace.history()`, breakpoints and watchpoints, and keeps the trace and
classifier pattern set loaded across calls.

#### Input schema

| Field        | Type      | Required | Description                                                                                                                                  |
| ------------ | --------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `trace_path` | `string`  | yes      | Either a local path to a `.ct` trace folder or an observability dive-in URL (see `exec_script` for URL format).                              |
| `path`       | `string`  | yes      | Source file of the query line. May be a suffix of the recorded path (`main.py`); it is resolved against the trace's source file list, and an ambiguous suffix is an error. |
| `line`       | `number`  | yes      | 1-based line to run to before querying. The variable must be live there.                                                                     |
| `variable`   | `string`  | yes      | Variable identifier to query. V1 is identifier-only; dotted paths are reserved for a future milestone.                                        |
| `max_hops`   | `number`  | no       | Maximum hops in this batch (default 16). Backends clamp it to their own tier limit.                                                          |

#### Output shape

The tool returns the spec §3.2 text rendering of the chain followed by
the canonical `OriginChain` JSON (spec §4.1) under a
`Canonical OriginChain JSON:` heading, so an agent can either read it or
parse it:

```
Origin chain for 'c' @ step=7
  hops=3 terminator=literal truncated=no

  0. [=] main.py:12
     c = b
  1. [=] main.py:11
     b = a
  2. [L] main.py:10
     a = 10
  [lit] 10
      @ main
```

Note that a recorded step carries the program state on *entering* a
line, so each hop's `location` is the step at which the value first
becomes observable — one step after the assignment named by that hop's
`sourceText`.

#### Example call

```json
{
  "jsonrpc": "2.0",
  "id": 7,
  "method": "tools/call",
  "params": {
    "name": "get_value_origin",
    "arguments": {
      "trace_path": "/traces/my-bug.ct",
      "path": "main.py",
      "line": 12,
      "variable": "c",
      "max_hops": 8
    }
  }
}
```

### `resolve_variable_step`

The first hop of the same chain: where the value a variable holds at
`path`:`line` came from, without walking the rest of the way back. Pair
it with `get_value_origin` when you want the full provenance.

#### Input schema

| Field        | Type     | Required | Description                                            |
| ------------ | -------- | -------- | ------------------------------------------------------ |
| `trace_path` | `string` | yes      | Path to the trace folder (or dive-in URL).             |
| `path`       | `string` | yes      | Source file of the query line (suffix match allowed).  |
| `line`       | `number` | yes      | 1-based line to run to before querying.                |
| `variable`   | `string` | yes      | Variable identifier to resolve.                        |

#### Output shape

```json
{
  "variable": "a",
  "stepId": 5,
  "location": { "path": "main.py", "line": 10, "functionName": "main" },
  "assignment": "    a = 10",
  "originKind": "literal",
  "sourceVariable": null
}
```

`stepId` / `location` are the earliest step at which the variable is
observed holding this value; `assignment` is the statement that produced
it. Because of the entering-a-line step semantics above, `location.line`
is the line *after* the assignment statement — both are reported so the
answer is not ambiguous.

When no assignment can be resolved the call fails with an explicit
message naming the variable. It never answers with an empty success: an
empty answer would be indistinguishable from an unimplemented tool.

#### Example call

```json
{
  "jsonrpc": "2.0",
  "id": 8,
  "method": "tools/call",
  "params": {
    "name": "resolve_variable_step",
    "arguments": {
      "trace_path": "/traces/my-bug.ct",
      "path": "main.py",
      "line": 12,
      "variable": "a"
    }
  }
}
```

### Implementation references

| Item                                       | Location                                                              |
| ------------------------------------------ | --------------------------------------------------------------------- |
| Tool table (schemas **and** dispatch)      | `src/backend-manager/src/mcp_server.rs::TOOLS`                        |
| Tool schemas                               | `src/backend-manager/src/mcp_server.rs::get_value_origin_tool`        |
|                                            | `src/backend-manager/src/mcp_server.rs::resolve_variable_step_tool`   |
| Tool handlers                              | `src/backend-manager/src/mcp_server.rs::handle_get_value_origin`      |
|                                            | `src/backend-manager/src/mcp_server.rs::handle_resolve_variable_step` |
| Shared origin query (open / break / walk)  | `src/backend-manager/src/mcp_server.rs::run_origin_query`             |
| Python bridge route (`trace.value_origin`) | `src/backend-manager/src/backend_manager.rs::handle_py_origin_chain`  |
| Response formatters                        | `src/backend-manager/src/python_bridge.rs`                            |
| Wire-shape definitions                     | `src/db-backend/src/task.rs` (Rust) / `python-api/codetracer/origin.py` (Python) |
| End-to-end tests                           | `src/backend-manager/tests/mcp_origin_test.rs`                        |
| Advertise/dispatch divergence check        | `src/backend-manager/tests/mcp_tool_surface_test.rs`                  |

`TOOLS` is the single source of truth for the tool surface: `tools/list`
advertises from it and `tools/call` dispatches from it, so a tool cannot
be advertised without being callable. Add new tools there, never to a
separate list.

For the user-facing walkthrough, see
[Value Origin Tracking](../usage_guide/value-origin-tracking.md).

---

### Book Isonim Content Reference Origin Kinds (`docs/book-isonim/content/reference/origin-kinds.md`)

---
title: Origin Kinds Reference
order: 103
---
# Origin Kinds Reference

This page enumerates the three closed enums that classify the rows of
an **origin chain**: `OriginKind` (per-hop classification),
`TerminatorKind` (why the backward walk stopped), and
`FrameTransitionKind` (the direction of a function-boundary crossing
on a hop). All three are part of the wire shape defined in spec §4.1
and travel verbatim across the DAP / MCP / CLI surfaces.

The canonical Rust definitions live in
`codetracer/src/db-backend/src/task.rs`; the Python mirror lives in
`python-api/codetracer/origin.py`.

For the user-facing walkthrough see
[Value Origin Tracking](../usage_guide/value-origin-tracking.md).

## `OriginKind`

Carried on every `OriginHop`. The classifier emits the variant that
best explains the dataflow step from the *previous* hop to *this* hop.

| Variant           | Meaning                                                                                                                        | Typical hop                                                                |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------- |
| `TrivialCopy`     | Source-level assignment that moves a value verbatim — `b = a`, `let y = x;`, `b := a`. Rendered de-emphasised in the chain.    | `b = a`                                                                    |
| `FieldAccess`     | RHS reads a field of a struct/object/record — `x = obj.field`.                                                                 | `x = obj.field`                                                            |
| `IndexAccess`     | RHS reads an element by index/key — `x = arr[i]`, `x = m["k"]`.                                                                | `x = arr[i]`                                                               |
| `Computational`   | RHS is an *expression* whose value derives from multiple sources — `r = a + b`, `s = fmt!("{x}+{y}", ...)`. Highlighted hop.   | `r = a + b`                                                                |
| `FunctionCall`    | RHS invokes a function whose return value is captured — `y = foo(...)`. When the chain *descends* into the callee, see below.  | `y = foo()`                                                                |
| `Literal`         | RHS is a literal value — `x = 10`, `name = "alice"`, `xs = []`.                                                                | `x = 10`                                                                   |
| `ReturnCapture`   | Hop captures the result of a function return (`await` / explicit return). Pairs with a `FrameTransitionKind::ReturnCapture`.   | `y = foo()` after `foo` returned                                           |
| `FunctionReturn`  | Reserved alias of `ReturnCapture` re-emitted on the callee side so future renderers can style the two halves separately.       | (callee-side mirror)                                                       |
| `ParameterPass`   | Hop crosses *into* a callee through a parameter binding. Pairs with a `FrameTransitionKind::ParameterPass`.                    | `def helper(a): ...` receiving the caller's argument                       |
| `CrossThreadCopy` | RR/MCR backends — the write that produced the value happened on a thread other than the querying thread. Confidence ≈ 0.6.    | Tagged with the cross-thread icon in the chain panel                       |
| `Unknown`         | The classifier could not parse the source line into a known shape. Treated as opaque; the chain typically terminates shortly. | Garbled / preprocessed line                                                |

Notes:

- `Computational` is the **classification of the hop**, not the
  reason the chain stopped. A computational hop can sit in the middle
  of a chain (the chain may keep walking the operands' own origins);
  it does not by itself end the walk. The chain ends when the walker
  produces a `Terminator` row — see below.
- `ParameterPass` / `ReturnCapture` hops are the only ones that carry
  a populated `FrameTransition`. All other hops have `frameTransition`
  set to `null`.
- The classifier returns `Unknown` (`confidence < 0.5`) for any line
  it cannot parse; the chain terminates with `TerminatorKind::UnknownSource`.

## `TerminatorKind`

The bottom row of every chain is a `Terminator`, not a hop. Its
`kind` is one of the variants below; the `expression` field carries
the terminator's source text and the `function` field carries the
containing function when known.

| Variant                  | Meaning                                                                                                                                                                                       |
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Literal`                | Walked back to a literal RHS — `x = 10`, `x = "alice"`, `xs = []`. The chain's ultimate source is a constant inside the recording.                                                            |
| `Computational`          | Walked back to a computational expression whose operands are themselves terminal (literals, parameters, externals). The expression is the chain's "root cause" in user-code.                  |
| `ParameterAtRecordStart` | Walked back to a parameter binding at the recording boundary — the value entered the trace as a function argument, with no recorded caller. Common when recording starts mid-execution.       |
| `ReadFromExternal`       | Walked back to a read from outside the recording — a system call, file I/O, network response, environment variable, randomness source.                                                        |
| `RecordingStart`         | The backward walk reached the start of the trace without finding an earlier write. Typical for partial recordings.                                                                            |
| `UnknownSource`          | The classifier could not parse the source line (e.g. macro-expanded, preprocessor output, missing source).                                                                                    |
| `UnknownVariable`        | The queried variable could not be resolved at the requested step (e.g. typo, out-of-scope).                                                                                                   |
| `DepthLimit`             | The walker hit the per-request `max_hops` cap before reaching a natural terminator. Use `--lazy` + `continuationToken` to resume.                                                             |
| `OutOfBudget`            | The walker exhausted a backend-specific wall-clock or per-hop budget. Common on release-mode native traces where the assignment was elided by the optimiser. Often paired with `truncated: true`. |

The terminator's SVG icon in the GUI maps one-to-one to these
variants (see spec §3.2.2 for the icon-set table).

## `FrameTransitionKind`

When an `OriginHop` crosses a function boundary the
`frameTransition` field is populated with one of:

| Variant         | Meaning                                                                                              |
| --------------- | ---------------------------------------------------------------------------------------------------- |
| `ParameterPass` | Chain descends *into* a callee. Rendered with the `↘` glyph on the hop's header line.                |
| `ReturnCapture` | Chain ascends *back into* the caller. Rendered with the `↗` glyph on the hop's header line.          |

Hops on the same `OriginKind` axis usually pair one-for-one:
`OriginKind::ParameterPass` carries `FrameTransitionKind::ParameterPass`;
`OriginKind::ReturnCapture` / `FunctionReturn` carry
`FrameTransitionKind::ReturnCapture`. Other hops carry no
`frameTransition`.

## See also

- [Value Origin Tracking](../usage_guide/value-origin-tracking.md) —
  user-facing walkthrough across all four surfaces (GUI, VS Code,
  CLI, MCP).
- [MCP tool reference](./mcp-tools.md) — `get_value_origin` /
  `resolve_variable_step` schemas (the same enums appear on the
  wire).
- [`ct` CLI reference](./ct_cli.md#ct-trace-origin) — `--format json`
  emits the canonical wire shape with these enum values.
- `codetracer-specs/GUI/Debugging-Features/Value-Origin-Tracking.md`
  §4.1 — the authoritative wire-schema definition.

---

### Book Isonim Content Reference Recorders (`docs/book-isonim/content/reference/recorders.md`)

---
title: Recorder CLI Reference
order: 104
---
## Recorder CLI Reference

Each supported blockchain language has its own recorder binary that produces trace files in CodeTracer's format. This page provides a comprehensive reference for all recorder CLIs.

All recorders share common conventions:

- Output directory: `-o, --out-dir <DIR>` (default: `./ct-traces/`)
- Output format: `-f, --format <FMT>` (`binary` or `json`, default: `binary`)
- Version: `version` subcommand
- Output files: `trace.bin` (or `trace.json`), `trace_metadata.json`, `trace_paths.json`

After recording, open the trace with `ct replay --trace-folder <DIR>`.

---

### codetracer-evm-recorder

Traces Solidity smart contracts on the EVM.

```
codetracer-evm-recorder record <solidity-file> [options]
```

| Flag                | Description                       |
| ------------------- | --------------------------------- |
| `<solidity-file>`   | Path to the `.sol` source file    |
| `--trace-dir <DIR>` | Output directory for trace files  |
| `--function <NAME>` | Function to call (default: `run`) |

---

### codetracer-circom-recorder

Traces Circom zero-knowledge circuits during witness generation.

```
codetracer-circom-recorder record <circom-file> [options]
codetracer-circom-recorder version
```

| Flag                  | Description                                          |
| --------------------- | ---------------------------------------------------- |
| `<circom-file>`       | Path to the `.circom` source file                    |
| `-o, --out-dir <DIR>` | Output directory (default: `./ct-traces/`)           |
| `-f, --format <FMT>`  | `binary` or `json` (default: `binary`)               |
| `-b, --backend <BE>`  | Witness generator: `wasm` or `cpp` (default: `wasm`) |

---

### codetracer-cairo-recorder

Traces Cairo programs and StarkNet contracts.

```
codetracer-cairo-recorder record <cairo-file> [options]
codetracer-cairo-recorder trace-starknet <trace-file> [options]
codetracer-cairo-recorder replay [options]
codetracer-cairo-recorder version
```

**record** — Record a standalone Cairo program.

| Flag                  | Description                                |
| --------------------- | ------------------------------------------ |
| `<cairo-file>`        | Path to the `.cairo` source file           |
| `-o, --out-dir <DIR>` | Output directory (default: `./ct-traces/`) |
| `-f, --format <FMT>`  | `binary` or `json` (default: `binary`)     |

**trace-starknet** — Convert an snforge trace JSON.

| Flag                  | Description                                |
| --------------------- | ------------------------------------------ |
| `<trace-file>`        | Path to snforge trace JSON                 |
| `-o, --out-dir <DIR>` | Output directory (default: `./ct-traces/`) |
| `-f, --format <FMT>`  | `binary` or `json` (default: `binary`)     |

**replay** — Replay an on-chain StarkNet transaction.

| Flag                  | Description                           |
| --------------------- | ------------------------------------- |
| `--tx-hash <HASH>`    | Transaction hash (e.g., `0x04a3c...`) |
| `--rpc-url <URL>`     | StarkNet JSON-RPC endpoint            |
| `--source-dir <PATH>` | Directory with contract source code   |

---

### codetracer-cardano-recorder

Traces Aiken smart contracts on Cardano (UPLC CEK machine).

```
codetracer-cardano-recorder record <aiken-file> [options]
codetracer-cardano-recorder replay [options]
codetracer-cardano-recorder version
```

**record** — Record an Aiken source file.

| Flag                  | Description                                |
| --------------------- | ------------------------------------------ |
| `<aiken-file>`        | Path to the `.ak` source file              |
| `-o, --out-dir <DIR>` | Output directory (default: `./ct-traces/`) |
| `-f, --format <FMT>`  | `binary` or `json` (default: `binary`)     |

**replay** — Replay an on-chain Cardano transaction.

| Flag                     | Description                                          |
| ------------------------ | ---------------------------------------------------- |
| `--tx-hash <HASH>`       | Transaction hash (64 hex characters)                 |
| `--blockfrost-key <KEY>` | Blockfrost API key (or `BLOCKFROST_API_KEY` env var) |
| `-o, --out-dir <DIR>`    | Output directory (default: `./ct-traces/`)           |
| `-f, --format <FMT>`     | `binary` or `json` (default: `binary`)               |

---

### codetracer-flow-recorder

Traces Cadence smart contracts on the Flow blockchain.

```
codetracer-flow-recorder record <cdc-file> [options]
codetracer-flow-recorder replay [options]
codetracer-flow-recorder version
```

**record** — Record a Cadence source file.

| Flag                  | Description                                |
| --------------------- | ------------------------------------------ |
| `<cdc-file>`          | Path to the `.cdc` source file             |
| `-o, --out-dir <DIR>` | Output directory (default: `./ct-traces/`) |
| `-f, --format <FMT>`  | `binary` or `json` (default: `binary`)     |

**replay** — Replay an on-chain Flow transaction.

| Flag                  | Description                                |
| --------------------- | ------------------------------------------ |
| `--tx-hash <HASH>`    | Flow transaction hash                      |
| `--access-node <URL>` | Flow Access Node gRPC endpoint             |
| `--source-dir <PATH>` | Directory with Cadence source files        |
| `-o, --out-dir <DIR>` | Output directory (default: `./ct-traces/`) |
| `-f, --format <FMT>`  | `binary` or `json` (default: `binary`)     |

---

### codetracer-solana-recorder

Traces Solana programs (SBF virtual machine).

```
codetracer-solana-recorder record <elf-file> [options]
codetracer-solana-recorder replay [options]
codetracer-solana-recorder version
```

**record** — Record from a compiled Solana program.

| Flag                  | Description                                   |
| --------------------- | --------------------------------------------- |
| `<elf-file>`          | Path to the compiled `.so` ELF file           |
| `--regs <FILE>`       | Pre-generated register trace (`.regs` binary) |
| `--elf <PATH>`        | Unstripped ELF for DWARF source mapping       |
| `--idl <FILE>`        | Anchor IDL JSON for account data decoding     |
| `-o, --out-dir <DIR>` | Output directory (default: `./ct-traces/`)    |
| `-f, --format <FMT>`  | `binary` or `json` (default: `binary`)        |

**replay** — Replay a Solana transaction.

| Flag                   | Description                                                     |
| ---------------------- | --------------------------------------------------------------- |
| `--signature <SIG>`    | Transaction signature (base-58)                                 |
| `--rpc-url <URL>`      | Solana JSON-RPC endpoint (default: `http://localhost:8899`)     |
| `--program-dir <PATH>` | Directory with compiled `.so` files (default: `target/deploy/`) |
| `-o, --out-dir <DIR>`  | Output directory (default: `./ct-traces/`)                      |
| `-f, --format <FMT>`   | `binary` or `json` (default: `binary`)                          |

---

### codetracer-move-recorder

Traces Move smart contracts on Sui and Aptos.

```
codetracer-move-recorder record <trace-file> [options]
codetracer-move-recorder replay [options]
codetracer-move-recorder aptosreplay [options]
codetracer-move-recorder version
```

**record** — Convert a Move trace file to CodeTracer format.

| Flag                  | Description                                |
| --------------------- | ------------------------------------------ |
| `<trace-file>`        | Trace file (`.json` or `.json.zst`)        |
| `-s, --source <FILE>` | Move source file                           |
| `-o, --out-dir <DIR>` | Output directory (default: `./ct-traces/`) |
| `-f, --format <FMT>`  | `binary` or `json` (default: `binary`)     |

**replay** — Replay a Sui transaction.

| Flag                  | Description                                         |
| --------------------- | --------------------------------------------------- |
| `--digest <DIGEST>`   | Sui transaction digest                              |
| `--rpc-url <URL>`     | Sui RPC endpoint (default: `http://localhost:9000`) |
| `--source-dir <PATH>` | Directory with Move source files                    |
| `-o, --out-dir <DIR>` | Output directory (default: `./ct-traces/`)          |

**aptosreplay** — Replay an Aptos transaction.

| Flag                  | Description                                                           |
| --------------------- | --------------------------------------------------------------------- |
| `--txn-version <N>`   | Transaction version (ledger version)                                  |
| `--node-url <URL>`    | Aptos REST API (default: `https://fullnode.mainnet.aptoslabs.com/v1`) |
| `--source-dir <PATH>` | Directory with Move source files                                      |
| `--profile-gas`       | Run gas profiling (default: `true`)                                   |
| `-o, --out-dir <DIR>` | Output directory (default: `./ct-traces/`)                            |

---

### codetracer-fuel-recorder

Traces Sway smart contracts on FuelVM.

```
codetracer-fuel-recorder record [project-dir] [options]
codetracer-fuel-recorder replay [options]
codetracer-fuel-recorder version
```

**record** — Record a Sway project.

| Flag                  | Description                                     |
| --------------------- | ----------------------------------------------- |
| `[project-dir]`       | Path to Sway project (must contain `Forc.toml`) |
| `--bytecode <FILE>`   | Raw FuelVM bytecode file                        |
| `--abi <FILE>`        | Sway ABI JSON for variable enrichment           |
| `-o, --out-dir <DIR>` | Output directory (default: `./ct-traces/`)      |
| `-f, --format <FMT>`  | `binary` or `json` (default: `binary`)          |

**replay** — Replay an on-chain Fuel transaction.

| Flag                  | Description                                                              |
| --------------------- | ------------------------------------------------------------------------ |
| `--tx-id <ID>`        | Transaction ID (hex with `0x` prefix)                                    |
| `--rpc-url <URL>`     | fuel-core GraphQL endpoint (default: `http://localhost:4000/v1/graphql`) |
| `--source-dir <PATH>` | Directory with forc build output                                         |
| `-o, --out-dir <DIR>` | Output directory (default: `./ct-traces/`)                               |

---

### codetracer-miden-recorder

Traces Miden VM programs and contracts.

```
codetracer-miden-recorder record <program> [options]
codetracer-miden-recorder contract [options]
codetracer-miden-recorder replay [options]
codetracer-miden-recorder version
```

**record** — Record a MASM program.

| Flag                  | Description                                        |
| --------------------- | -------------------------------------------------- |
| `<program>`           | Path to MASM source (`.masm`) or package (`.masp`) |
| `-o, --out-dir <DIR>` | Output directory (default: `./ct-traces/`)         |
| `-f, --format <FMT>`  | `binary` or `json` (default: `binary`)             |

**contract** — Record a MockChain contract simulation.

| Flag                  | Description                                    |
| --------------------- | ---------------------------------------------- |
| `--wallet-id <ID>`    | Account ID for wallet (hex, default: `0x1000`) |
| `--faucet-id <ID>`    | Account ID for faucet (hex, default: `0x2000`) |
| `--symbol <SYM>`      | Faucet token symbol (default: `TEST`)          |
| `--debug`             | Enable debug mode (default: `true`)            |
| `-o, --out-dir <DIR>` | Output directory (default: `./ct-traces/`)     |

**replay** — Replay an on-chain Miden transaction.

| Flag                       | Description                                |
| -------------------------- | ------------------------------------------ |
| `--node-url <URL>`         | Miden node RPC endpoint                    |
| `--account-id <ID>`        | Account ID (hex)                           |
| `--transaction-id <ID>`    | Transaction ID (hex)                       |
| `--captured-inputs <FILE>` | Path to captured TransactionInputs JSON    |
| `-o, --out-dir <DIR>`      | Output directory (default: `./ct-traces/`) |

---

### codetracer-polkavm-recorder

Traces PolkaVM programs and ink! smart contracts.

```
codetracer-polkavm-recorder record <program> [options]
codetracer-polkavm-recorder trace-ink [options]
codetracer-polkavm-recorder replay [options]
codetracer-polkavm-recorder version
```

**record** — Record a PolkaVM program.

| Flag                  | Description                                |
| --------------------- | ------------------------------------------ |
| `<program>`           | Path to PolkaVM program blob (`.polkavm`)  |
| `-o, --out-dir <DIR>` | Output directory (default: `./ct-traces/`) |
| `-f, --format <FMT>`  | `binary` or `json` (default: `binary`)     |

**trace-ink** — Record an ink! contract execution.

| Flag                   | Description                                       |
| ---------------------- | ------------------------------------------------- |
| `--contract <PATH>`    | ink! contract (`.polkavm` blob or `.contract`)    |
| `--constructor <NAME>` | Constructor to call (e.g., `new`, `default`)      |
| `--message <NAME>`     | ink! message name (e.g., `flip`, `get`)           |
| `--arg <VALUE>`        | Message arguments (repeatable, hex-encoded SCALE) |
| `-o, --out-dir <DIR>`  | Output directory (default: `./ct-traces/`)        |

**replay** — Replay an on-chain contract.

| Flag                  | Description                                             |
| --------------------- | ------------------------------------------------------- |
| `--address <ADDR>`    | Contract address (SS58 or hex)                          |
| `--selector <NAME>`   | ink! message selector name                              |
| `--calldata <HEX>`    | Hex-encoded calldata (default: empty)                   |
| `--endpoint <URL>`    | Substrate RPC endpoint (default: `ws://127.0.0.1:9944`) |
| `--block <HASH>`      | Block hash for state fetch (latest if omitted)          |
| `--source-dir <PATH>` | Contract source directory                               |
| `-o, --out-dir <DIR>` | Output directory (default: `./ct-traces/`)              |

---

### codetracer-leo-recorder

Traces Leo smart contracts on Aleo.

```
codetracer-leo-recorder record <leo-file> [options]
codetracer-leo-recorder replay [options]
codetracer-leo-recorder version
```

**record** — Record a Leo source file.

| Flag                  | Description                                |
| --------------------- | ------------------------------------------ |
| `<leo-file>`          | Path to the `.leo` source file             |
| `-o, --out-dir <DIR>` | Output directory (default: `./ct-traces/`) |
| `-f, --format <FMT>`  | `binary` or `json` (default: `binary`)     |

**replay** — Replay an on-chain Aleo program.

| Flag                  | Description                                                      |
| --------------------- | ---------------------------------------------------------------- |
| `--program-id <ID>`   | On-chain program ID (e.g., `credits.aleo`)                       |
| `--function <NAME>`   | Function to execute                                              |
| `--input <VALUE>`     | Input values (repeatable, e.g., `--input 10u32`)                 |
| `--endpoint <URL>`    | Aleo node REST API (default: `https://api.explorer.aleo.org/v1`) |
| `-o, --out-dir <DIR>` | Output directory (default: `./ct-traces/`)                       |

---

### codetracer-ton-recorder

Traces Tolk/FunC smart contracts on TON.

```
codetracer-ton-recorder record <tolk-file> [options]
codetracer-ton-recorder trace-sandbox [options]
codetracer-ton-recorder replay [options]
codetracer-ton-recorder version
```

**record** — Record a Tolk source file.

| Flag                  | Description                                |
| --------------------- | ------------------------------------------ |
| `<tolk-file>`         | Path to the `.tolk` source file            |
| `-o, --out-dir <DIR>` | Output directory (default: `./ct-traces/`) |
| `-f, --format <FMT>`  | `binary` or `json` (default: `binary`)     |

**trace-sandbox** — Convert `@ton/sandbox` VM log output.

| Flag                  | Description                                         |
| --------------------- | --------------------------------------------------- |
| `--vm-log <FILE>`     | Path to vm_logs_full output                         |
| `--source <FILE>`     | Path to Tolk source file (default: `contract.tolk`) |
| `-o, --out-dir <DIR>` | Output directory (default: `./ct-traces/`)          |

**replay** — Replay an on-chain TON transaction.

| Flag                  | Description                                                         |
| --------------------- | ------------------------------------------------------------------- |
| `--tx-hash <HASH>`    | Transaction hash (hex-encoded)                                      |
| `--address <ADDR>`    | Contract address                                                    |
| `--endpoint <URL>`    | Liteserver endpoint (default: `https://ton.org/global-config.json`) |
| `--source-dir <PATH>` | Contract source directory                                           |
| `-o, --out-dir <DIR>` | Output directory (default: `./ct-traces/`)                          |

---

### codetracer-js-recorder

Traces JavaScript and TypeScript programs.

```
codetracer-js-recorder record <file> [options] [-- app-args]
codetracer-js-recorder instrument <src> --out <dir> [options]
```

**record** — Record a JS/TS program.

| Flag               | Description                                      |
| ------------------ | ------------------------------------------------ |
| `<file>`           | Entry file or directory                          |
| `--out-dir <DIR>`  | Trace output directory (default: `./ct-traces/`) |
| `--format <FMT>`   | `json` or `binary` (default: `json`)             |
| `--include <GLOB>` | Include glob pattern (repeatable)                |
| `--exclude <GLOB>` | Exclude glob pattern (repeatable)                |
| `-- <args>`        | Arguments passed to the instrumented program     |

**instrument** — Instrument source files without executing.

| Flag               | Description                       |
| ------------------ | --------------------------------- |
| `<src>`            | Source file or directory          |
| `--out <DIR>`      | Output directory (required)       |
| `--source-maps`    | Emit source maps                  |
| `--include <GLOB>` | Include glob pattern (repeatable) |
| `--exclude <GLOB>` | Exclude glob pattern (repeatable) |

---

### Python Recorder

The Python recorder is a pip-installable package, not a standalone binary.

```
python -m codetracer_python_recorder [options] <script> [-- args]
python -m codetracer_python_recorder --pytest [pytest-args]
python -m codetracer_python_recorder --unittest [unittest-args]
```

Usually invoked automatically via `ct record script.py`. See the [Python Getting Started](../getting_started/python.md) guide.

| Flag                        | Description                                           |
| --------------------------- | ----------------------------------------------------- |
| `<script>`                  | Python script to trace                                |
| `-o, --out-dir <DIR>`       | Output directory (default: `./trace-out/`)            |
| `--format <FMT>`            | `binary` or `json`                                    |
| `--pytest [ARGS]`           | Run pytest with arguments                             |
| `--unittest [ARGS]`         | Run unittest with arguments                           |
| `--activation-path <PATH>`  | Gate tracing to specific path                         |
| `--trace-filter <PATH>`     | Trace filter file path (repeatable)                   |
| `--io-capture <MODE>`       | Stdout/stderr capture: `off`, `proxies`, `proxies+fd` |
| `--on-recorder-error <ACT>` | Response to errors: `abort` or `disable`              |
| `--require-trace`           | Exit 1 if no trace produced                           |
| `--keep-partial-trace`      | Preserve partial traces on failure                    |
| `--log-level <LEVEL>`       | Log verbosity (e.g., `info`, `debug`)                 |
| `--log-file <PATH>`         | Write logs to file instead of stderr                  |
| `--json-errors`             | Emit JSON error trailers on stderr                    |

---

### Ruby Recorder

The Ruby recorder is a gem installed alongside your Ruby environment.

```
codetracer-ruby-recorder <script.rb> [args]
```

Usually invoked automatically via `ct record script.rb`. See the [Ruby Getting Started](../getting_started/ruby.md) guide.

---

--- METRICS ---
- Files Extracted: 105
- Estimated Token Budget: ~126760 tokens
- Canonical Reference: https://codewiki.google/github.com/metacraft-labs/codetracer
