Skip to content

feat: run_completed/run_failed terminal events in progress.ndjson (#7)#87

Merged
mattleaverton merged 1 commit into
danshapiro:mainfrom
mattleaverton:fix/7-progress-ndjson-terminal-event
Apr 27, 2026
Merged

feat: run_completed/run_failed terminal events in progress.ndjson (#7)#87
mattleaverton merged 1 commit into
danshapiro:mainfrom
mattleaverton:fix/7-progress-ndjson-terminal-event

Conversation

@mattleaverton
Copy link
Copy Markdown
Collaborator

Summary

From docs/plans/2026-04-24-kilroy-fixes-from-feedback.md item #7.

Bug: progress.ndjson emits stage_attempt_*, edge_selected, input_materialization_*, tmux_session_* — but nothing for run completion. The canonical terminal signal today is the appearance of final.json, and that invariant was undocumented. Skills end up polling final.json by hand instead of tailing the stream.

Fix:

  1. emitTerminalProgressEvent() in internal/attractor/engine/engine.go, called as the very last action of persistTerminalOutcome() — after final.Save(), run.tgz, and gitPushIfConfigured(). Ordering guarantee: any reader observing the event can immediately open final.json.
  2. isCanceledError() helper distinguishes context.Canceled/DeadlineExceeded from hard failures. persistFatalOutcome() now sets FinalCanceled status on context cancel (new status added to runtime/final.go).
  3. docs/runs-layout.md (new) documents the run directory layout, progress.ndjson lifecycle events including the terminal event, and final.json as the public completion contract.

Event schema

{"event":"run_completed","status":"success","run_id":"01ABC...","id":"a1b2c3d4","ts":"2026-..."}
{"event":"run_failed","status":"fail","run_id":"...","reason":"...","ts":"..."}
{"event":"run_failed","status":"canceled","run_id":"...","reason":"context canceled","ts":"..."}

Test plan

  • TestProgressNDJSON_TerminalEvent_RunCompleted — minimal start → exit graph, asserts last line of progress.ndjson is run_completed and final.json exists on disk.
  • TestProgressNDJSON_TerminalEvent_RunFailed — fan-in with both branches exit-1, asserts run_failed emitted and final.json exists.
  • go test ./internal/attractor/engine/... passes (2 pre-existing unrelated failures confirmed on base commit too).

Risks / follow-ups

  • Resume early-exit path: If resume detects "nothing to do" it returns without persistTerminalOutcome, so no event emitted — but also no final.json written, so consistent.
  • Resume early-failure path: Manifest parse errors save final.json directly; these are startup errors not lifecycle events.
  • Stall watchdog: Surfaces as status=fail (not canceled) because the watchdog uses fmt.Errorf rather than context.Canceled. Could be changed if stall timeouts should report as canceled.
  • FinalCanceled is now a valid FinalStatus — consumers of final.json that branch on status should handle it.

Context

Produced by a dogfood quick-launch run against this repo.

🤖 Generated with Claude Code

danshapiro#7)

- Add FinalCanceled FinalStatus = "canceled" to runtime/final.go
- Add emitTerminalProgressEvent() method emitted as the final line of
  progress.ndjson, after final.json is fully written and git push completes
- Add isCanceledError() helper to distinguish externally-canceled runs
  (context.Canceled/DeadlineExceeded) from hard failures
- Modify persistFatalOutcome() to set FinalCanceled status on context cancel
- Add docs/runs-layout.md documenting run directory layout, progress.ndjson
  lifecycle events (including terminal events), and final.json contract
- Add TestProgressNDJSON_TerminalEvent_RunCompleted (success path)
- Add TestProgressNDJSON_TerminalEvent_RunFailed (fan-in all-fail path)

Ordering guarantee: terminal event is emitted AFTER final.json is written
and AFTER gitPushIfConfigured(), so it is always the last line in the stream.
@mattleaverton mattleaverton merged commit 0d78a04 into danshapiro:main Apr 27, 2026
1 check failed
@mattleaverton mattleaverton deleted the fix/7-progress-ndjson-terminal-event branch April 27, 2026 17:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant