tera-pilot

P0 — Implementation Journal

Working document: what has been done within the P0 priorities from TERA_PILOT_PRODUCT_STRATEGY.md and how. Status: August 2026. Update as implementation proceeds.

0. Context: Clew → Tera Pilot rename

The project was fully renamed (user-facing name, packages, classes, env variables, config paths, CLI):

Was Became
package clew/ tera_pilot/
package clew_tui/ tera_pilot_tui/
bin/clew.js, bin/clew-tui.js bin/tera-pilot.js, bin/tera-pilot-tui.js (+ bin/tera-pilot-daemon.js, bin/tera-pilot-acp.js)
clew (CLI), clew_tui, clew-acp, clew-daemon tera-pilot, tera-pilot-tui, tera-pilot-acp, tera-pilot-daemon
ClewAPIServer, ClewBridge, ClewDaemon, ClewMainWindow TeraPilotAPIServer, TeraPilotBridge, TeraPilotDaemon, TeraPilotMainWindow
CLEW_* env vars, ~/.clew, CLEW.md TERA_PILOT_*, ~/.tera_pilot, TERA_PILOT.md
GitHub zai-shop/clew ilyaosovskoi/tera-pilot

The README gained a “Migrating from Clew” section with config migration (mv ~/.clew ~/.tera_pilot, mv CLEW.md TERA_PILOT.md); the banner was replaced with tera_pilot.png.


1. Environment Doctor — tera-pilot doctor (P0 onboarding)

What: one command answers “is this machine ready to run Tera Pilot?”.

Files: tera_pilot/environment_doctor.py, tera_pilot/cli.py, tera_pilot/__main__.py, entry point tera-pilot = "tera_pilot.cli:main".

Checks (each with ok/warn/fail status):

Usage:

tera-pilot doctor                 # human-readable report (rich)
tera-pilot doctor --json          # machine-readable (schema v1) for CI
tera-pilot doctor --project DIR   # check a specific directory

Exit code: 0 — no blocking issues, 1 — a fail exists (warn does not fail: a local setup without cloud keys is valid).

Verification: tests/test_environment_doctor.py (4 tests). Network probes are limited to localhost; nothing is sent externally.


2. Audit export / verification — tera-pilot audit (P0)

What: documented export and verification of the signed audit trail.

Files: tera_pilot/audit_cli.py, tera_pilot/cli.py.

How it works: every activity log record is signed with a local Ed25519 key (~/.tera_pilot/audit_key, chmod 0600) and chained to the previous record by a SHA-256 hash (prev_hash + payload). Tampering with content, deletion and reordering of records are detected on verification.

tera-pilot audit export --out audit.json   # signed (Ed25519 + hash chain)
tera-pilot audit verify audit.json         # 0 = intact, 1 = tampering detected

The public key ~/.tera_pilot/audit_key.pub allows verifying an export on another machine. Inside a running TUI/Web, the slash commands /audit and /audit-signed are available.

Verification: tests/test_audit_cli.py — export→verify roundtrip, record tamper detection, reordering detection, invalid subcommand handling.


3. Threat Model — THREAT_MODEL.md (P0)

What: the public threat model: assets, trust boundaries, 8 threat scenarios (prompt injection, malicious commands, workspace escape, key theft, code leak, audit tampering, social engineering, supply chain), a controls mapping with module references, user verification procedures, residual risks and claims discipline (what we do NOT claim).

Linked from the README via the “Trust and Control” section.


4. Rust acceleration — tera_pilot_native (P0 readiness / performance)

4.1. Why this is a P0-relevant area

The project is designed around native acceleration (tera_pilot/agent/native.py), but the Rust part did not exist — everything ran on pure-Python fallbacks. Hot-path analysis (what is slowest under real load):

Path When it runs Why it is slow in Python
circuit_breaker on EVERY LLM/MCP call threading.Lock + O(window) window scans (sum(...) over deque) on every record()
sandbox.path_would_be_writable on every agent file write Path.resolve() + is_relative_to — object overhead and repeated syscalls
compaction (code/intra/inter) on context overflow orchestration and prompt building in Python (the LLM call itself dominates; the win is moderate)
interjection, actor mid-turn messages, cancellation cheap, but trivially portable

4.2. What was implemented

Crate tera-pilot-native/ (Cargo workspace + PyO3 extension tera_pilot_native, 5 submodules mirroring the fallback APIs 1-to-1):

Loading goes through the existing tera_pilot/agent/native.py; without a build the project continues to run on fallbacks.

4.3. Benchmark results (Mac arm64, Python 3.12, release build)

python3 benchmarks/bench_native.py:

Operation Native (Rust) Fallback (Python) Speedup
circuit_breaker: record + try_claim (window 60s, 5k ops) 0.4 µs/op 19.3 µs/op ~43x
sandbox path_would_be_writable (100k ops, against a real workspace) 19.8 µs/op 66.2 µs/op ~3.3x
interjection push + drain (50k ops) 0.4 µs/op 0.7 µs/op ~1.8x

Circuit breaker is the main win: it runs on every provider/MCP call, and the Python version degrades quadratically with long windows.

4.6. Security bugs found and fixed in the sandbox (.. escape)

Bug 1 — .. was silently dropped. The first resolve_path version built the path “tail” via Path::file_name(), which returns None in Rust for a trailing ... The walk-up loop silently skipped such components, and the path <workspace>/sub/../../etc/passwd (where sub does not exist) normalized to <workspace>/sub/etc/passwd — i.e. it was treated as writable, although it really leads outside the workspace. Additionally verified: std::path::absolute does not normalize .. at all, and naive “normalize against the filesystem” breaks symlink semantics.

Bug 2 — symlink + .. (found in the second review). Lexically collapsing .. before symlink resolution diverged from Python: for <ws>/link/../x.py where link is a symlink to a directory outside the workspace, Python first resolves the symlink and then applies .. (→ outside, not writable), while the lexical variant dropped link and treated the path as inside (→ writable).

Final fix (parity with Python Path.resolve()):

  1. existing path — canonicalize immediately;
  2. otherwise — walk up from the original path to the deepest existing ancestor: Path::exists() lets the OS resolve .. and symlinks against the existing prefix, so the stopping point matches where Python realpath would land;
  3. the remainder is built with .. components via components().next_back() (not file_name()!);
  4. the remainder is attached to the canonicalized ancestor, and finally normalize_lexically collapses ./...

Verification: 13 live native-vs-Python cases (including symlink-outside and inside + ..) — all match; the tests/test_native.py regression covers both dotdot escapes and symlink+..; the benchmark did not regress after the fix (3.3x vs 3.2x before). This is exactly the class of bug the sandbox checks are moved to Rust for: Python Path.resolve() does not surprise, a naive Rust port does.

4.4. Build

cd tera-pilot-native/pyo3
maturin build --release                    # wheel → ../target/wheels/
python3 -m pip install --force-reinstall ../target/wheels/*.whl
# or in a venv: maturin develop --release

4.5. Verification


5. Evaluation Harness — eval/ (P0.1)

What: a reproducible evaluation contour — tasks run in a clean copy of the repository, results are written against a machine-readable schema v1.

Files: eval/runner.py (CLI run/check/smoke/report, --version), eval/schema.py (manual validator, mirror of the schema), eval/results/schema.json (JSON Schema v1), eval/smoke.json (CI smoke set), eval/tasks/<id>/ (manifest task.json + fixture repo/ + reference gold/), eval/README.md.

Task set: 43 in 6 categories (bug_fix ×12, test_repair ×6, refactor ×5, feature ×9, code_review ×5, documentation ×6). Each task: a realistic mini-fixture (stdlib + pytest), test_command, a declared baseline_status, and a reference gold/. The doc-changelog-from-git fixture is a real git repository with tags v1.0.0/v1.1.0 to exercise workspace.commit.

How it works:

CI commands:

python3 -m eval.runner check    # structural check of all tasks (fast)
python3 -m eval.runner smoke    # fake driver over eval/smoke.json + baseline_status check
python3 -m pytest tests/test_evaluation_schema.py tests/test_eval_tasks.py -q

Schema v1 extended backward-compatibly: workspace.baseline (test results on the pristine repo) and optional metrics.tokens_in / tokens_out / request_count / cancelled. The required field set is unchanged.

Claims discipline: status (about the run), metrics.test_passed (about tests), metrics.verification_status (about verification) and workspace.baseline (about the pristine repo) are not conflated; test_output and final_output are marked as potentially sensitive.

Verification:

Status: implemented (43-task set, baseline, commit, usage-cost, check/smoke/report, quality-gate tests). Remaining: baseline runs on real providers and publishing measured metrics (see eval/README.md).


6. GUI v2.3.0 — SpaceX theme + Basic/Advanced modes

What: the web interface moved to SpaceX/Tesla aesthetics (black “launch-pad” minimalism) and gained a Basic/Advanced switcher.

spacex theme:

Basic/Advanced modes:

Verification: node --check app.js and tools_panels.js — clean; HTML parser — no unclosed tags; a live web server serves index.html (200) with all markers (uiModeToggle, data-ui-mode, 16× advanced-only), CSS/JS — 200. Visual browser check happens on a machine with Chrome / a built-in window (Chrome is not installed in this environment).


7. ToolEngine / Guardian security fixes (regression-tested)

Four sandbox/security bugs found and fixed in the tool layer, with regression tests in tests/test_tool_engine_sandbox.py:

  1. git --git-dir / --work-tree sandbox bypass. Every argument starting with - was skipped as a “flag”, so only git -C was validated. git --git-dir=/home/user/.git log could read git history from anywhere on disk, and git --work-tree=/etc add -A could stage arbitrary files. Now both the inline (--git-dir=<path>) and two-arg (--git-dir <path>) forms are validated against the workspace, including relative escapes.
  2. git_diff unvalidated pathspec. A relative ../outside or absolute path could show diffs of files outside the workspace. The pathspec is now validated against the workspace before the git call.
  3. Guardian dead code. The command-policy risk check called command_policy.is_dangerous_flag(binary, "") with an empty flag, which can never return True. It now flags binaries the resolved policy would refuse (deny list / not allowed).
  4. Guardian template path. The engine’s guardian prompt template was looked up at a path that never existed, so every Guardian LLM call silently used the generic fallback prompt. The template is now resolved to the real location (tera_pilot/agent/templates/guardian.md).

8. TUI bridge reliability fixes + integration tests

8.1. Cancel hang (TOCTOU) fix

request_stop() and answer_confirmation() previously set the cancel flag first and then the confirmation event, leaving a window where the agent checked _confirm_accepted before it was set — a pending approval could hang the turn even after the user pressed Stop. Both paths now set the event FIRST and the flag second, so a blocked approval unwinds immediately.

8.2. TUI integration test suite

tests/test_tui_integration.py (19 tests) drives the REAL AgentRuntime + ToolEngine through the REAL TeraPilotBridge with the deterministic FakeProvider (tests/fake_provider.py) — no network, no API keys. Covers the failure modes a user can actually hit:


9. One-command install (npm) — npm install -g tera-pilot

What: a single command installs the full product — no git clone, no manual pip install.

How it works:

Verification: tests/test_npm_install.py (9 tests) exercises the real postinstall/preuninstall/launchers against a fake Python interpreter — hermetic, no network, no real venv/pip. Covers venv bootstrap + marker, idempotent fast path, force rewrite, clean failure without Python, arg forwarding for all four launchers, friendly missing-module error, and uninstall cleanup (managed venv removed, unmanaged venv kept).


10. Backend report contract — schema v1

What: a stable machine-readable contract for one backend task run (TUI-backed automation adapter, CI, GitHub workflows, daemon).

Files: tera_pilot_tui/backend_report_schema.json (JSON Schema v1), tera_pilot_tui/backend_report.py (manual validator mirroring the schema, no jsonschema dependency), tera_pilot_tui/backend_runner.py (producer — validates every report before returning).

Fields: schema_version (1), ok / status (success|failed|error), actual provider/model, workspace identity (absolute path), iterations, duration_sec, tokens, cost_usd, tools (names + error + duration only — raw tool arguments are never exported), verification (self_verify_called + status in not_requested|ran|passed|failed|unknown), optional test_result (command/passed/exit_code/output), final_output (potentially sensitive), optional audit_ref, JSON-safe metadata.

Claims discipline: an unrun verification is never called passed; tool arguments never leak into the report; test_output/final_output are marked sensitive. The producer sanitizes runtime metadata recursively so a report is always json.dumps-able.

Verification: tests/test_tui_backend.py (9 tests) — JSON Schema and manual validator agree on required fields and vocabulary; sample report is schema-valid and JSON-serializable; raw tool args are rejected; missing required fields are rejected; passed without self_verify_called is rejected; non-JSON metadata serializes safely.


11. What remains (outside this P0 slice)


Final verification

python3 -m pytest tests/ -q        # 209 passed (incl. TUI integration + quality gate on 43 eval tasks)
python3 -m compileall eval tera_pilot tera_pilot_tui tests  # OK
python3 -m eval.runner check       # 43 tasks OK (structure + gold/)
python3 -m eval.runner smoke       # 10/10 tasks OK (fake driver, baseline verified)
python3 -m tera_pilot doctor --json                    # ready=true, counts ok=16/warn=4/fail=0, native=ok
python3 benchmarks/bench_native.py  # breaker ~43x, sandbox ~3.3x, interjection ~1.8x