Content
otrace
**Ethotrace** is a dynamic type checking and signature analysis system for Ruby observes method calls during test records "behavior (protocol)",agated exceptions", and " requirements" in three channels.
The design concept is inspired by the `Effect, Error, Requirements>` model of Effect-TS. For detailed design, see [`docs/ethotrace-design-handoff-v2.md`](docs/ethotrace-design-handoff-v2.md), and for the JSONL schema, which is a stable contract between gems, see [`docs/schema.md`](docs/schema.md).
> The observation results are the **observed contract (observed lower bound)**, which depends on test coverage.
> Untested paths' contracts are not included. This is not a defect but a specification.
## Monorepo Structure
The monorepo structure with multiple gems in a single repository (to be added gradually):
| gem ||---|---|
| `gems/ethotrace` | core: observation engine + stdlib adapter + JSONL writer + merge CLI |
gems/ethotrace-rspec` | RSpec lifecycle connection |
| `gems/ethotrace` | Minitest) |
| `gems/ethotrace-rails`tie / ActiveSupport::Notifications / Zeitwerk integration (planned) |
| `gems/ethotrace-mcp` | MCP server providing observation results (completely separated from the observation process) |
| `gems/ethotrace-rbs` | Projection to RBS interface (planned) |
## Implementation Status
| Content | Status |
|---|---|---|
| M0 | Scaffold v1 finalization | ✅ M1 | Prepend wrapper + Tracker + CallContext + reentrancy guard + JSONL writer (Success / Error channel) | ✅ |
| M2 | Adapter API + stdlib adapter (ENV/Time/Random/IO/Process) + Requirements attribution + effect span | ✅ |
| M3 | TracePoint engine + argument protocol observation | ✅ |
| M4 | `ethotrace-rspec` (suite lifecycle connection) + merge CLI (`ethotrace merge`) | ✅ |
| M5 | Self-hosting + isolation strategy (`NullIsolation` / `BoxIsolation`) + collector/probe separation | ✅ |
| M `ethotrace-m (MCP server referencing self data) | ✅ |
| M7 | `ethotrace` (Railtie / Notifications / Zeitwerk) | planned M8 | `ethotbs` (protocol → RBS `interface` projection) | planned |
> The roadmap has been revised in design document v2: **MCP is built before Rails**.
> M5 achieves self-hosting, and M6's MCP supports subsequent development with self-observation data in a bootstrap loop (§9).
For core usage, see [`gems/ethotrace/README.md`](gems/ethotrace/README.md).
## Usage (RSpec)
When `ethotrace-rspec` is tests, an observation session at the start/end of the suite,
and `tmp/ethotrace/<session>.jsonl written for each test worker. In `spec_helper.rb`:
```ruby
require "ethotrace"
Ethotrace::RSpec.setup do |config|
config.observe Order # All instance methods of Order itself
config.observe Tax, methods: %i[rate] # Specify only some methods
# config.options[:trace_c = true # Include C methods in the protocol (high cost)
end
```
In parallel tests (parallel_tests), a session ID is derived from `TEST_ENV_NUMBER` and PID (e.g., `rspec-w2-pid4242`),
and written to a separate file.
## Merge (`race merge`)
Multiple JSONL files output by parallel workers are integrated.
The protocol, exceptions, and Requirements are unionedowner, name, kind)` as the key, and `samples added (→ [`docs/schema.md`](docs/schema.md) §7).
```bash
bundle exec ethotrace merge tmp/eth/*.jsonl -o ethotrace/observations.jsonl
output maintains the `method_observation` format in one record, making it **reable**.
Omitting` outputs pure JSONL to stdout (progress and summary to stderr).
The merge result can be with `ethotrace## MCP Server (`ethotrace-mcp`)
The merged observation results are exposed as an ** server (stdio / JSON)**, allowing AI agents first user is Ethotrace's developer, Claude Code itself) to refer to the contracts of each method while developing.
It is a **completely separated** data consumer from the observation process, with no instrumentation started (design document §9 / M6).
```bash
# Read ethotrace/observations.jsonl and listen on stdio
bundle exec ethotrace-mcp
bundle exec ethotrace-mcp path/to/observations.jsonl # Specify the store to read
```
For Claude copying `.mcp.json.example` to `.mcp.json` registers it as a project-scope MCP server.
Available tools:
| Tool | Returns |
|---| `lookup_method` | All contracts of a method (protocol, return value, exceptions, Requirements) |
| `list_methods` | All observed methods |
| `pure_methods` | Methods with empty Requirements (R=∅ = suspicion of purity) |
| `flaky_suspects` | Methods touching non-deterministic Requirements (`time / `random.read`,| `requiring touching specific effect vocabulary (`db.query` / `env etc.) |
| `methods_raising` / `exceptions_of` | Methods that can escape exceptions / exception classes |
|_protocol` | Observed protocol for specified arguments |
The returned contracts are the **observed contract (lower bound of tested paths)**.
See `CLAUDE.md` for detailed self-observation loop procedures.
## Self-Hosting and Isolation Strategy
Ethotrace has a swappable isolation strategy (`Ethotrace::Isolation`) for **observation installation targets**.
The strategy decides where to run the probe (prepend hook), while the collector (recording, attribution, JSONL) and TracePoint are always on the root side.
| Strategy | Condition | Usage |
|---|---|---|
|olation::Null` | Default | Normal analysis (probe = collector in the same space) |
| `Isolation::Box` | Ruby >= 4.0 + `RUBY_BOX=1` | Self-hosting and intenseing |
`Isolation::Box` loads Ethotrace into a new [`Ruby::Box`](https://docs.ruby-lang.org/en/4.0/Ruby/Box.html) (Ruby 4.0 experimental)
and isolates the observation target from the root
This structurally resolves the self-referential contamination where "the instrumentation mechanism itself becomes the target of instrumentation".
Observation events are bridged from the probe in the box to the collector on the root.
Additionally, the active paths of recording are doubly protected by a name-based deny-list (`Wrapper::SELF_DENY_LIST`).
```bash
# Self-hosting demo: Observe Ethotrace's own methods with Ethotrace (launched from the main box)
RUBY_BOX=1 ruby gems/ethotrace/script/self_hosting.rb
```
The verification results of Ruby::Box (entity separation, cross-border observation, pedigree dependency, etc., E1-E6)
are in [`docs/box-semantics.md`](docs/box-semantics.md), and design details are in design document §4.8.
Since Ruby::Box is experimental and requires launching from the main box,
self-hosting verification is performed with the above standalone harness (bare `RUBY_BOX=1 ruby`).
**Rails adapter + BoxIsolation is not supported for now**.
## Development
Ruby >=.2 is required.
```bash
bundle install # Dependency resolution (root Gemfile bundles all gems)
bundle exec rake Specs + Rub for all gems
bundle spec # Tests only exec rake rubocop
```
MIT