For the high-level pipeline, trust loop, and safety model, see architecture.md.
This page is for people working in the code: the package map, naming, and the compatibility shim.
uv sync --extra dev --extra mcp
uv run --extra dev --extra mcp pytest # full suite must stay green
uv run etch --help # the CLI
ruff check . # lintThe engine is a monorepo of single-purpose packages under packages/. Each directory
packages/etchplan-<x>/ contains one importable module, etchplan_<x>:
| Package | Module | Role |
|---|---|---|
etchplan-core |
etchplan_core |
schemas (trace, pattern, plan, policy, manifest, audit), hashing, ids, paths |
etchplan-ingest |
etchplan_ingest |
trace import (JSONL, Claude Code / Codex / opencode hooks, benchmark fixtures), normalization, redaction, slotting |
etchplan-miner |
etchplan_miner |
gap-tolerant repeated-subsequence mining, parameter-flow + guard learning |
etchplan-compiler |
etchplan_compiler |
plan synthesis, candidate ranking, code-routine distillation, serialization |
etchplan-validator |
etchplan_validator |
executed-replay validation, static invariant checks, AST / osascript safety guards |
etchplan-runtime |
etchplan_runtime |
the deterministic executor, registry, guards, gated mutations, model backends, audit |
etchplan-store |
etchplan_store |
SQLite repository (traces, patterns, plans, shadow + plan stats) |
etchplan-cli |
etchplan_cli |
the etch command |
etchplan-mcp |
etchplan_mcp |
the etchplan-mcp MCP server |
etchplan-actions |
etchplan_actions |
the toy refund sandbox + executed-plan corpus used by the examples |
etchplan-evals |
etchplan_evals |
compile-safety reporting for the optional benchmark demo |
Packages import each other by their absolute module name (e.g. from etchplan_core.schemas.plan import ExecutionPlan).
The product, CLI, and import namespace are all etchplan / etch / etchplan_*. The packages were
previously named aec_* (from the project's earlier name); a few compatibility shims keep older artifacts
and integrations working through the rename:
- Import paths: legacy
aec_*imports (e.g.import aec_runtime.registry, or a--registry aec_runtime.registry:Registryspec) resolve to the renamedetchplan_*modules via a meta-path finder inetchplan_core/_compat.py, installed whenetchplan_coreis imported. - Plan format:
ExecutionPlan.apiVersionis nowetchplan/v1alpha1; the schema also accepts the legacyaec/v1alpha1so plans saved by older builds still load. - Report format: benchmark
schema_versiontags are nowetchplan.*; the importer canonicalizes the legacyaec.*prefix on read (seebenchmark_importers.py). - Local state: new captures live under
~/.etchplan/; reads fall back to the legacy~/.aec/, and a legacyaec_tscapture-timestamp field is still read.
These are temporary compatibility layers and will be removed in a future release. New code should use the
etchplan_* names throughout.
etchplan is pre-1.0. Treat the import surface and the ExecutionPlan / report schemas as unstable until 1.0;
the stable public contract today is the etch CLI and the documented --registry / --fallback
module:attr integration seam.
The most common contribution. Trace ingestion has a single seam: build a raw-trace dict and call
normalize_trace(raw, source="<harness>") (packages/etchplan-ingest/etchplan_ingest/normalizer.py), it
handles redaction, slotting, fingerprinting, and validation. Model a new adapter on
packages/etchplan-ingest/etchplan_ingest/claude_code_hook.py, and wire an etch import <harness> command in
packages/etchplan-cli/etchplan_cli/main.py.