Render the branch, identity, diff and artifact records and re-record the run snapshots

`run events --pretty` reads the flattened `git.identity` fields, and
shows a `run.diff` record as its summary and an `artifact.collected`
record as its path and size. The snapshot filters redact the base commit a
`Branch:` line names. The CLI snapshots now carry the `Base:` line, the
`run.branch` and `git.identity` stream items, the dry run's simulated
response and the two response files a dry run dumps.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-09-18 18:39:58 -04:00
parent baa2fc8b5c
commit b354a2f948
No known key found for this signature in database
9 changed files with 114 additions and 27 deletions

View file

@ -782,7 +782,8 @@ fn format_platform_record(ts: &str, record: &Value, styles: &Styles) -> Option<S
))
}
"git.identity" => {
let identity = record.get("identity")?;
// The identity's fields are flattened into the record.
let identity = record;
let name = identity.get("name").and_then(Value::as_str).unwrap_or("?");
let email = identity.get("email").and_then(Value::as_str).unwrap_or("?");
let source = identity
@ -794,6 +795,26 @@ fn format_platform_record(ts: &str, record: &Value, styles: &Styles) -> Option<S
styles.dim.apply_to(source)
))
}
"run.diff" => {
let summary = record.get("diff_summary")?;
let count = |key: &str| summary.get(key).and_then(Value::as_i64).unwrap_or(0);
Some(format!(
"{ts} Diff: {} in {} file(s)",
styles
.dim
.apply_to(format!("+{} -{}", count("additions"), count("deletions"))),
count("files_changed")
))
}
"artifact.collected" => {
let path = record.get("path").and_then(Value::as_str).unwrap_or("?");
let bytes = record.get("bytes").and_then(Value::as_u64).unwrap_or(0);
Some(format!(
"{ts} {} {path} {}",
styles.dim.apply_to("\u{2398}"),
styles.dim.apply_to(format!("({bytes} B)"))
))
}
other => Some(format!(
"{ts} {}",
styles.dim.apply_to(format!("\u{00b7} {other}"))

View file

@ -392,6 +392,7 @@ fn attach_replays_completed_detached_run() {
----- stdout -----
----- stderr -----
Web UI: http://localhost:3000/runs/[ULID]
Base: [BASE]
✓ Start [TIME]
✓ Run Tests [TIME]
✓ Report [TIME]
@ -636,6 +637,7 @@ fn attach_before_completion_streams_to_finished_state() {
----- stdout -----
----- stderr -----
Web UI: http://localhost:3000/runs/[ULID]
Base: [BASE]
✓ start [DURATION]
✓ wait [DURATION]
✓ exit [DURATION]
@ -1529,6 +1531,40 @@ fn attach_json_errors_without_prompting_for_human_input() {
{
"run_id": "[ULID]",
"stream_seq": 21,
"kind": "platform",
"id": "[EVENT_ID]",
"recorded_at": "[EPOCH_MS]",
"item": {
"seq": 7,
"recorded_at": "[EPOCH_MS]",
"record": {
"kind": "run.branch",
"run_branch": "fabro/run/[ULID]",
"base_sha": "[DIGEST]",
"workspace": "invocation-0-scope-0"
}
}
},
{
"run_id": "[ULID]",
"stream_seq": 22,
"kind": "platform",
"id": "[EVENT_ID]",
"recorded_at": "[EPOCH_MS]",
"item": {
"seq": 8,
"recorded_at": "[EPOCH_MS]",
"record": {
"kind": "git.identity",
"name": "Fabro",
"email": "noreply@fabro.sh",
"source": "default"
}
}
},
{
"run_id": "[ULID]",
"stream_seq": 23,
"kind": "petri",
"id": "execution 0/7/0",
"recorded_at": "[EPOCH_MS]",
@ -1620,7 +1656,7 @@ fn attach_json_errors_without_prompting_for_human_input() {
},
{
"run_id": "[ULID]",
"stream_seq": 22,
"stream_seq": 24,
"kind": "petri",
"id": "execution 0/8/0",
"recorded_at": "[EPOCH_MS]",
@ -1700,7 +1736,7 @@ fn attach_json_errors_without_prompting_for_human_input() {
},
{
"run_id": "[ULID]",
"stream_seq": 23,
"stream_seq": 25,
"kind": "petri",
"id": "execution 0/8/1",
"recorded_at": "[EPOCH_MS]",
@ -1771,12 +1807,12 @@ fn attach_json_errors_without_prompting_for_human_input() {
},
{
"run_id": "[ULID]",
"stream_seq": 24,
"stream_seq": 26,
"kind": "platform",
"id": "[EVENT_ID]",
"recorded_at": "[EPOCH_MS]",
"item": {
"seq": 7,
"seq": 9,
"recorded_at": "[EPOCH_MS]",
"record": {
"kind": "checkpoint",
@ -1804,7 +1840,7 @@ fn attach_json_errors_without_prompting_for_human_input() {
},
{
"run_id": "[ULID]",
"stream_seq": 25,
"stream_seq": 27,
"kind": "petri",
"id": "execution 0/9/0",
"recorded_at": "[EPOCH_MS]",
@ -1912,7 +1948,7 @@ fn attach_json_errors_without_prompting_for_human_input() {
},
{
"run_id": "[ULID]",
"stream_seq": 26,
"stream_seq": 28,
"kind": "petri",
"id": "execution 0/9/1",
"recorded_at": "[EPOCH_MS]",
@ -1981,7 +2017,7 @@ fn attach_json_errors_without_prompting_for_human_input() {
},
{
"run_id": "[ULID]",
"stream_seq": 27,
"stream_seq": 29,
"kind": "petri",
"id": "execution 0/9/2",
"recorded_at": "[EPOCH_MS]",
@ -2040,7 +2076,7 @@ fn attach_json_errors_without_prompting_for_human_input() {
},
{
"run_id": "[ULID]",
"stream_seq": 28,
"stream_seq": 30,
"kind": "petri",
"id": "execution 0/10/0",
"recorded_at": "[EPOCH_MS]",
@ -2133,7 +2169,7 @@ fn attach_json_errors_without_prompting_for_human_input() {
},
{
"run_id": "[ULID]",
"stream_seq": 29,
"stream_seq": 31,
"kind": "petri",
"id": "execution 0/11/0",
"recorded_at": "[EPOCH_MS]",
@ -2200,7 +2236,7 @@ fn attach_json_errors_without_prompting_for_human_input() {
},
{
"run_id": "[ULID]",
"stream_seq": 30,
"stream_seq": 32,
"kind": "petri",
"id": "execution 0/12/0",
"recorded_at": "[EPOCH_MS]",
@ -2271,7 +2307,7 @@ fn attach_json_errors_without_prompting_for_human_input() {
},
{
"run_id": "[ULID]",
"stream_seq": 31,
"stream_seq": 33,
"kind": "petri",
"id": "execution 0/13/0",
"recorded_at": "[EPOCH_MS]",
@ -2336,7 +2372,7 @@ fn attach_json_errors_without_prompting_for_human_input() {
},
{
"run_id": "[ULID]",
"stream_seq": 32,
"stream_seq": 34,
"kind": "petri",
"id": "execution 0/13/1",
"recorded_at": "[EPOCH_MS]",
@ -2395,7 +2431,7 @@ fn attach_json_errors_without_prompting_for_human_input() {
},
{
"run_id": "[ULID]",
"stream_seq": 33,
"stream_seq": 35,
"kind": "petri",
"id": "execution 0/14/0",
"recorded_at": "[EPOCH_MS]",
@ -2502,7 +2538,7 @@ fn attach_json_errors_without_prompting_for_human_input() {
},
{
"run_id": "[ULID]",
"stream_seq": 34,
"stream_seq": 36,
"kind": "petri",
"id": "execution 0/14/1",
"recorded_at": "[EPOCH_MS]",
@ -2561,7 +2597,7 @@ fn attach_json_errors_without_prompting_for_human_input() {
},
{
"run_id": "[ULID]",
"stream_seq": 35,
"stream_seq": 37,
"kind": "petri",
"id": "execution 0/15/0",
"recorded_at": "[EPOCH_MS]",

View file

@ -259,21 +259,23 @@ fn dump_exports_completed_run_snapshot() {
success: true
exit_code: 0
----- stdout -----
Exported 12 files for run [ULID] to [TEMP_DIR]/export
Exported 14 files for run [ULID] to [TEMP_DIR]/export
----- stderr -----
");
assert_snapshot!(dump_file_summary(&output_dir), @"
checkpoints/0022.json
checkpoints/0034.json
checkpoints/0046.json
checkpoints/0058.json
checkpoints/0025.json
checkpoints/0037.json
checkpoints/0049.json
checkpoints/0061.json
events.jsonl
graph.fabro
run.json
run.log
stages/001-start@1/status.json
stages/002-run_tests@1/response.md
stages/002-run_tests@1/status.json
stages/003-report@1/response.md
stages/003-report@1/status.json
stages/004-exit@1/status.json
");

View file

@ -135,8 +135,8 @@ fn events_completed_run_reads_store_without_progress_jsonl() {
success: true
exit_code: 0
----- stdout -----
{"run_id":"[ULID]","stream_seq":62,"kind":"petri","id":"coordinator/6/0","recorded_at":[EPOCH_MS],"item":{"id":{"log":"coordinator","seq":6,"index":0},"origin":"external","context":{},"recorded_at":[EPOCH_MS],"record":{"seq":6,"origin":"external","recorded_at":[EPOCH_MS],"body":{"event":"run.finished","status":"success"}}}}
{"run_id":"[ULID]","stream_seq":63,"kind":"platform","id":"[EVENT_ID]","recorded_at":[EPOCH_MS],"item":{"seq":11,"recorded_at":[EPOCH_MS],"record":{"kind":"run.lifecycle","transition":"succeeded","status":{"kind":"succeeded","reason":"completed"}}}}
{"run_id":"[ULID]","stream_seq":67,"kind":"petri","id":"coordinator/7/0","recorded_at":[EPOCH_MS],"item":{"id":{"log":"coordinator","seq":7,"index":0},"origin":"external","context":{},"recorded_at":[EPOCH_MS],"record":{"seq":7,"origin":"external","recorded_at":[EPOCH_MS],"body":{"event":"run.finished","status":"success"}}}}
{"run_id":"[ULID]","stream_seq":68,"kind":"platform","id":"[EVENT_ID]","recorded_at":[EPOCH_MS],"item":{"seq":14,"recorded_at":[EPOCH_MS],"record":{"kind":"run.lifecycle","transition":"succeeded","status":{"kind":"succeeded","reason":"completed"}}}}
----- stderr -----
"#);
}
@ -165,8 +165,8 @@ fn events_tail_limits_output() {
success: true
exit_code: 0
----- stdout -----
{"run_id":"[ULID]","stream_seq":62,"kind":"petri","id":"coordinator/6/0","recorded_at":[EPOCH_MS],"item":{"id":{"log":"coordinator","seq":6,"index":0},"origin":"external","context":{},"recorded_at":[EPOCH_MS],"record":{"seq":6,"origin":"external","recorded_at":[EPOCH_MS],"body":{"event":"run.finished","status":"success"}}}}
{"run_id":"[ULID]","stream_seq":63,"kind":"platform","id":"[EVENT_ID]","recorded_at":[EPOCH_MS],"item":{"seq":11,"recorded_at":[EPOCH_MS],"record":{"kind":"run.lifecycle","transition":"succeeded","status":{"kind":"succeeded","reason":"completed"}}}}
{"run_id":"[ULID]","stream_seq":67,"kind":"petri","id":"coordinator/7/0","recorded_at":[EPOCH_MS],"item":{"id":{"log":"coordinator","seq":7,"index":0},"origin":"external","context":{},"recorded_at":[EPOCH_MS],"record":{"seq":7,"origin":"external","recorded_at":[EPOCH_MS],"body":{"event":"run.finished","status":"success"}}}}
{"run_id":"[ULID]","stream_seq":68,"kind":"platform","id":"[EVENT_ID]","recorded_at":[EPOCH_MS],"item":{"seq":14,"recorded_at":[EPOCH_MS],"record":{"kind":"run.lifecycle","transition":"succeeded","status":{"kind":"succeeded","reason":"completed"}}}}
----- stderr -----
"#);
}
@ -215,6 +215,8 @@ fn events_pretty_formats_small_run() {
[CLOCK] · running
[CLOCK] Engine: petri run started
[CLOCK] ▶ Start
[CLOCK] Branch: fabro/run/[ULID] from [SHA]
[CLOCK] Git identity: Fabro <noreply@fabro.sh> default
[CLOCK] ✓ Start [DURATION]
[CLOCK] ⎘ Checkpoint [SHA]
[CLOCK] ▶ Run Tests
@ -229,6 +231,7 @@ fn events_pretty_formats_small_run() {
[CLOCK] report → exit continue
[CLOCK] ✓ Exit [DURATION]
[CLOCK] ⎘ Checkpoint [SHA]
[CLOCK] Diff: +0 -0 in 0 file(s)
[CLOCK] ✓ SUCCEEDED [DURATION]
[CLOCK] · succeeded
----- stderr -----

View file

@ -836,6 +836,7 @@ fn dry_run_simple() {
----- stderr -----
Run: [ULID]
Web UI: http://localhost:3000/runs/[ULID]
Base: [BASE]
✓ Start [TIME]
✓ Run Tests [TIME]
✓ Report [TIME]
@ -845,6 +846,9 @@ fn dry_run_simple() {
Run: [ULID]
Status: SUCCEEDED
Duration: [DURATION]
=== Output ===
[Simulated] report
");
}

View file

@ -555,7 +555,7 @@ fn git_backed_run(
);
init_remote_fixture(&workspace_dir, "main");
let run = run_local_workflow(context, &workspace_dir, "workflow.toml");
WorkspaceRunSetup { run, workspace_dir }
WorkspaceRunSetup { run }
}
/// The run output filters plus one for commit shas, which a patch names in

View file

@ -1172,7 +1172,7 @@ async fn a_finished_petri_run_reads_back_through_the_cli() {
// Pretty: stages and platform records.
let mut cmd = context.command();
cmd.args(["events", "--pretty", "--server", &target, &run_id]);
fabro_snapshot!(pretty_filters(&context), cmd, @r"
fabro_snapshot!(pretty_filters(&context), cmd, @"
success: true
exit_code: 0
----- stdout -----
@ -1185,6 +1185,8 @@ async fn a_finished_petri_run_reads_back_through_the_cli() {
[CLOCK] Engine: petri run started
[CLOCK] ▶ start
[CLOCK] │ checkout: [TEMP_DIR]/petri-workspace is not a Git repository; the workspace starts empty
[CLOCK] Branch: fabro/run/[ULID] from [SHA]
[CLOCK] Git identity: Fabro <noreply@fabro.sh> default
[CLOCK] ✓ start [DURATION]
[CLOCK] ⎘ Checkpoint [SHA]
[CLOCK] ▶ say
@ -1196,6 +1198,7 @@ async fn a_finished_petri_run_reads_back_through_the_cli() {
[CLOCK] say → exit continue
[CLOCK] ✓ exit [DURATION]
[CLOCK] ⎘ Checkpoint [SHA]
[CLOCK] Diff: +0 -0 in 0 file(s)
[CLOCK] ✓ SUCCEEDED [DURATION]
[CLOCK] · succeeded
----- stderr -----

View file

@ -16,6 +16,7 @@ fn dry_run_branching() {
----- stderr -----
Run: [ULID]
Web UI: http://localhost:3000/runs/[ULID]
Base: [BASE]
✓ Start [TIME]
✓ Plan [TIME]
✓ Implement [TIME]
@ -27,6 +28,9 @@ fn dry_run_branching() {
Run: [ULID]
Status: SUCCEEDED
Duration: [DURATION]
=== Output ===
[Simulated] validate
");
}
@ -44,6 +48,7 @@ fn dry_run_conditions() {
----- stderr -----
Run: [ULID]
Web UI: http://localhost:3000/runs/[ULID]
Base: [BASE]
✓ start [TIME]
✓ Decide [TIME]
✓ Path B [TIME]
@ -53,6 +58,9 @@ fn dry_run_conditions() {
Run: [ULID]
Status: SUCCEEDED
Duration: [DURATION]
=== Output ===
[Simulated] path_b
");
}
@ -73,6 +81,7 @@ fn dry_run_parallel() {
----- stderr -----
Run: [ULID]
Web UI: http://localhost:3000/runs/[ULID]
Base: [BASE]
✓ start [TIME]
✓ Fork Work [TIME]
✓ Branch [N] [TIME]
@ -85,6 +94,9 @@ fn dry_run_parallel() {
Run: [ULID]
Status: SUCCEEDED
Duration: [DURATION]
=== Output ===
[Simulated] review
");
}
@ -102,6 +114,7 @@ fn dry_run_styled() {
----- stderr -----
Run: [ULID]
Web UI: http://localhost:3000/runs/[ULID]
Base: [BASE]
✓ start [TIME]
✓ Plan [TIME]
✓ Implement [TIME]
@ -112,6 +125,9 @@ fn dry_run_styled() {
Run: [ULID]
Status: SUCCEEDED
Duration: [DURATION]
=== Output ===
[Simulated] critical_review
");
}
@ -129,6 +145,7 @@ fn dry_run_inferred_command() {
----- stderr -----
Run: [ULID]
Web UI: http://localhost:3000/runs/[ULID]
Base: [BASE]
✓ Start [TIME]
✓ Echo [TIME]
✓ Exit [TIME]

View file

@ -74,6 +74,7 @@ static INSTA_FILTERS: &[(&str, &str)] = &[
"Duration: [DURATION]",
),
(r"Base: [^\n]+ \([0-9a-f]{7,40}\)", "Base: [BASE]"),
(r"(Branch: [^\n]+ from )[0-9a-f]{7,40}", "${1}[SHA]"),
// The sandbox driver's events: per-process event source ids, operation
// ids, sub-second durations, and a local sandbox's path-derived id.
(