Artifact contract
Every doctor_frankentui flow writes into a known layout under a run
root (defaults under /tmp/doctor_frankentui/…). The files are the
contract: CI relies on these exact names and schemas, and humans grep
them with operations/evidence-grep-patterns
recipes. This page documents the contract verbatim.
Source: README + crates/doctor_frankentui/src/runmeta.rs +
the scripts/doctor_frankentui_*_e2e.sh scripts.
Timestamps in run-root directory names are UTC in
%Y%m%dT%H%M%SZ form — e.g. happy_20260423T000000Z.
Happy path
/tmp/doctor_frankentui/e2e/happy_<TIMESTAMP>/
├── logs/ (per-step stdout/stderr logs)
├── project/ (captured project state, if any)
└── meta/
├── summary.json
├── summary.txt
├── artifact_manifest.json
├── events.jsonl
├── events_validation_report.json
├── step_results.tsv
├── command_manifest.txt
├── env_snapshot.txt
└── tool_versions.txtmeta/summary.json
Outcome summary for the run.
{
"status": "ok",
"run_id": "doctor_happy_seed0",
"started_at": "T000000",
"finished_at": "T000042",
"profile": "modern",
"stage_outcomes": [
{ "stage": "seed-demo", "status": "ok", "duration_ms": 812 },
{ "stage": "replay", "status": "ok", "duration_ms": 34121 },
{ "stage": "report", "status": "ok", "duration_ms": 411 }
],
"artifact_manifest": "meta/artifact_manifest.json",
"events": "meta/events.jsonl"
}status:"ok"or"degraded"or"failed".stage_outcomes[]: ordered list with per-stage timing.profile: the capture profile used (one oflist-profiles).
meta/artifact_manifest.json
Full file inventory for the run.
{
"schema_version": "artifact-manifest-v1",
"run_id": "doctor_happy_seed0",
"root": "/tmp/doctor_frankentui/e2e/happy_20260423T000000Z",
"files": [
{
"path": "meta/events.jsonl",
"size_bytes": 45120,
"sha256": "…"
},
{
"path": "logs/seed_demo.stdout.log",
"size_bytes": 1240,
"sha256": "…"
}
]
}Checksums are SHA-256 and are stable across identical runs when
E2E_DETERMINISTIC=1 is active.
meta/events.jsonl
Append-only JSONL ledger. Each line is one event. Schema is versioned —
current is schema_version: "e2e-events-v1". Validated by
scripts/doctor_frankentui_validate_jsonl.py and mirrored into
meta/events_validation_report.json.
Common event kinds:
stage_start/stage_endstep_start/step_endframe_captured— includesframe_idx,checksumdiff_decision— diff-strategy selectionresize_decision— BOCPD-driven resize coalescingconformal_frame_guard— frame-budget guard verdictdegradation_event— degradation cascade transitionvoi_decision— VOI sampling outcome
See operations/evidence-grep-patterns
for jq recipes per event type.
meta/events_validation_report.json
Output of the JSONL validator. ok: true plus a count of warnings if
E2E_JSONL_VALIDATE_MODE=warn, or a hard ok: false in strict mode.
meta/step_results.tsv
One row per step: step_id, status, duration_ms, notes. Useful
for quick scans without a JSON parser.
meta/command_manifest.txt
Exact commands executed, in order. Reproduces the run.
meta/env_snapshot.txt
Captured environment at start. Includes TERM, COLORTERM, NO_COLOR,
TMUX, ZELLIJ, E2E_DETERMINISTIC, E2E_SEED, E2E_TIME_STEP_MS,
and the git SHA.
meta/tool_versions.txt
Versions for cargo, rustc, python3, jq, rg, cargo-llvm-cov.
Failure path
/tmp/doctor_frankentui/e2e/failure_<TIMESTAMP>/
├── cases/<case_id>/… (per-case captures + artifacts)
├── logs/
└── meta/
├── summary.json
├── summary.txt
├── case_results.json
├── case_results.tsv
├── replay_triage_report.json
├── events.jsonl
├── events_validation_report.json
├── command_manifest.txt
├── env_snapshot.txt
└── tool_versions.txtmeta/summary.json
{
"status": "failed",
"run_id": "doctor_failure_seed0",
"failed_stage": "replay",
"first_failing_case": "case_003",
"failure_reason": "frame_checksum_mismatch",
"duration_ms": 28310,
"stage_outcomes": [ /* … */ ]
}meta/case_results.json
Per-case details for every adversarial case the failure suite ran.
{
"schema_version": "case-results-v1",
"cases": [
{
"case_id": "case_001",
"description": "inline mode under 40-column viewport",
"status": "ok",
"duration_ms": 3120
},
{
"case_id": "case_003",
"description": "alt screen + rapid resize storm",
"status": "failed",
"duration_ms": 4820,
"failure_signal": "frame_checksum_mismatch",
"artifact_dir": "cases/case_003",
"events_pointer": "meta/events.jsonl#L4812"
}
]
}meta/replay_triage_report.json
Output of scripts/doctor_frankentui_replay_triage.py run at end of
the failure flow. Top-N failure signals with JSON pointers to the
evidence. See replay triage for
the signal schema.
{
"schema_version": "replay-triage-v1",
"run_root": "/tmp/doctor_frankentui/e2e/failure_20260423T000000Z",
"signals": [
{
"severity": 2,
"message": "frame checksum diverged at index 42",
"event_index": 4812,
"event_type": "frame_captured",
"case_id": "case_003",
"step_id": "resize_storm",
"pointers": ["meta/events.jsonl#L4812", "cases/case_003/frame_42.txt"],
"expected": { "checksum": "0xdeadbeef00000000" },
"actual": { "checksum": "0xcafef00d00000000" }
}
],
"timeline": [ /* compacted timeline entries */ ]
}meta/events.jsonl (failure variant)
Same schema as happy-path, but may be truncated at the failure point.
Validate with events_validation_report.json.
Determinism path
/tmp/doctor_frankentui/determinism_soak_<TIMESTAMP>/
├── happy_run_<iter>/ (full happy-path artifacts per iter)
├── failure_run_<iter>/ (full failure-path artifacts per iter)
├── logs/
└── meta/
├── run_index.tsv
├── determinism_report.json
└── determinism_report.txtmeta/determinism_report.json
Frame-checksum parity across iterations. Non-volatile divergence fails the gate.
{
"schema_version": "determinism-report-v1",
"iterations": 3,
"total_frames": 2148,
"matches": 2148,
"divergences": 0,
"divergence_ratio": 0.0,
"per_iteration": [
{ "iter": 1, "run_id": "doctor_happy_seed0", "frames": 716 },
{ "iter": 2, "run_id": "doctor_happy_seed0", "frames": 716 },
{ "iter": 3, "run_id": "doctor_happy_seed0", "frames": 716 }
],
"volatile_events": []
}divergence_ratio=divergences / total_frames. Gate fails at any non-zero value outside thevolatile_eventsallowlist.
meta/determinism_report.txt
Human-readable summary. Suitable for PR comment bots.
Full guide: determinism soak.
Coverage path
target/doctor_frankentui_coverage/
├── coverage_summary.json
├── coverage_gate_report.json
└── coverage_gate_report.txtcoverage_gate_report.json
Output of scripts/doctor_frankentui_coverage.sh after comparing
coverage_summary.json against
crates/doctor_frankentui/coverage/thresholds.toml.
{
"schema_version": "coverage-gate-v1",
"passed": true,
"totals": {
"lines": { "actual": 92.1, "threshold": 89.0, "verdict": "pass" },
"branches": { "actual": 72.4, "threshold": 69.0, "verdict": "pass" },
"functions": { "actual": 88.3, "threshold": 86.0, "verdict": "pass" }
},
"groups": {
"orchestration": { "lines": { "actual": 90.1, "threshold": 88.0, "verdict": "pass" }, /* … */ },
"foundations": { /* … */ },
"module_capture":{ /* … */ }
}
}verdict:"pass"or"fail"per metric.- The whole gate
passediff every listed metric passed.
Full guide: coverage gate.
Troubleshooting map
From the doctor_frankentui README:
| Symptom | Likely cause | Next step |
|---|---|---|
certify exits 30 | Capture stack degraded (no raw mode / no PTY). | Inspect meta/doctor_summary.json.degraded_reason; re-run with --allow-degraded if acceptable. |
events.jsonl absent | Child process crashed before first event. | Read logs/<step>.stderr.log; check meta/command_manifest.txt to reproduce. |
events_validation_report.json.ok == false | Schema drift. | Compare against tests/e2e/lib/e2e_jsonl_schema.json; file an issue if the schema changed intentionally. |
determinism_report.json.divergences > 0 | Non-volatile divergence. | Open meta/run_index.tsv, find the diverging iter, diff happy_run_<iter>/meta/events.jsonl across iters. |
coverage_gate_report.json.passed == false | A group or module dropped below threshold. | Inspect groups.<name> for the failing metric; use cargo llvm-cov --html to drill into uncovered lines. |
artifact_manifest.json missing files | Capture interrupted. | Re-run; if reproducible, open an issue with the interrupted run root attached. |
| All checksums match but snapshot tests fail | Profile drift. | Re-run with FTUI_TEST_PROFILE pinned; check snapshot tests. |