diff --git a/lib/apps/fabro-cli/src/commands/run/petri_stream.rs b/lib/apps/fabro-cli/src/commands/run/petri_stream.rs index 5539ad7cc..fb3269551 100644 --- a/lib/apps/fabro-cli/src/commands/run/petri_stream.rs +++ b/lib/apps/fabro-cli/src/commands/run/petri_stream.rs @@ -782,7 +782,8 @@ fn format_platform_record(ts: &str, record: &Value, styles: &Styles) -> Option { - 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 { + 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}")) diff --git a/lib/apps/fabro-cli/tests/it/cmd/attach.rs b/lib/apps/fabro-cli/tests/it/cmd/attach.rs index 94eb5d15e..54f9ecd15 100644 --- a/lib/apps/fabro-cli/tests/it/cmd/attach.rs +++ b/lib/apps/fabro-cli/tests/it/cmd/attach.rs @@ -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]", diff --git a/lib/apps/fabro-cli/tests/it/cmd/dump.rs b/lib/apps/fabro-cli/tests/it/cmd/dump.rs index 9beafb095..7e0335c11 100644 --- a/lib/apps/fabro-cli/tests/it/cmd/dump.rs +++ b/lib/apps/fabro-cli/tests/it/cmd/dump.rs @@ -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 "); diff --git a/lib/apps/fabro-cli/tests/it/cmd/events.rs b/lib/apps/fabro-cli/tests/it/cmd/events.rs index 8cd7a6155..15e8ff634 100644 --- a/lib/apps/fabro-cli/tests/it/cmd/events.rs +++ b/lib/apps/fabro-cli/tests/it/cmd/events.rs @@ -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 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 ----- diff --git a/lib/apps/fabro-cli/tests/it/cmd/run.rs b/lib/apps/fabro-cli/tests/it/cmd/run.rs index dc2212f04..04ef6015f 100644 --- a/lib/apps/fabro-cli/tests/it/cmd/run.rs +++ b/lib/apps/fabro-cli/tests/it/cmd/run.rs @@ -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 "); } diff --git a/lib/apps/fabro-cli/tests/it/cmd/support.rs b/lib/apps/fabro-cli/tests/it/cmd/support.rs index 405e39d69..1ff0929d8 100644 --- a/lib/apps/fabro-cli/tests/it/cmd/support.rs +++ b/lib/apps/fabro-cli/tests/it/cmd/support.rs @@ -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 diff --git a/lib/apps/fabro-cli/tests/it/scenario/petri.rs b/lib/apps/fabro-cli/tests/it/scenario/petri.rs index cac1da09b..d6a2be790 100644 --- a/lib/apps/fabro-cli/tests/it/scenario/petri.rs +++ b/lib/apps/fabro-cli/tests/it/scenario/petri.rs @@ -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 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 ----- diff --git a/lib/apps/fabro-cli/tests/it/workflow/dry_run_examples.rs b/lib/apps/fabro-cli/tests/it/workflow/dry_run_examples.rs index 35630a335..49170e99e 100644 --- a/lib/apps/fabro-cli/tests/it/workflow/dry_run_examples.rs +++ b/lib/apps/fabro-cli/tests/it/workflow/dry_run_examples.rs @@ -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] diff --git a/lib/foundation/fabro-test/src/lib.rs b/lib/foundation/fabro-test/src/lib.rs index e3345fca3..cef2d1a8c 100644 --- a/lib/foundation/fabro-test/src/lib.rs +++ b/lib/foundation/fabro-test/src/lib.rs @@ -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. (