Skip to content

Main event stream#519

Open
fkukuck wants to merge 6 commits into
fabro-sh:mainfrom
fkukuck:main-event-stream
Open

Main event stream#519
fkukuck wants to merge 6 commits into
fabro-sh:mainfrom
fkukuck:main-event-stream

Conversation

@fkukuck

@fkukuck fkukuck commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

PR Summary
This PR adds the first durable main event stream: a server-wide, coarse lifecycle log stored in SlateDB under events/main/<seq-ts>.

Today, RunEvents are durable but run-scoped: runs/<run_id>/events/<seq-ts>. They drive projections, run APIs, attach/SSE, and CLI progress, and they go away with the run. global_event_tx is only an in-memory live fanout. This branch keeps those systems unchanged and adds a separate internal MainEventStore for future automation/audit use.

Technically, the branch adds:

  • fabro-types::main_event: typed MainEvent, MainEventEnvelope, lifecycle bodies, and unknown-event preservation.
  • fabro-store::MainEventStore: append/list/subscribe, sequence recovery, and storage under global events/main/....
  • fabro-server::main_event_mirror: maps selected RunEvents into compact fabro.run.* main events.
  • Mirroring at two points:
    • run.created is mirrored immediately after run creation, before live forwarders exist.
    • Later lifecycle events are mirrored from forward_run_events_to_global.

No public API, CLI, SSE, webhook ingestion, automation matching, or listener cursor persistence is added here.

Reviewer FAQ
Is this replacing the run event stream?
No. Run events remain canonical for per-run detail, projections, APIs, attach/SSE, and CLI progress. Main events are a compact global mirror for cross-run consumers.

Why a second stream?
Run events are scoped to one run and deleted with that run. Automation triggers and audit need a durable server-wide stream that can survive run deletion and be consumed globally.

Why not use global_event_tx?
global_event_tx is live-only memory fanout. It has no replay, no persistence, and no recovery after restart.

What gets mirrored?
Only coarse lifecycle events: created, started, running, completed, failed, cancelled, paused, unpaused.

What is intentionally not mirrored?
Stage, agent, tool, transcript, sandbox, detailed progress events, full graphs/settings/patches/tool output, and run.cancel.requested.

How is cancellation represented?
Terminal cancellation still arrives as run.failed with FailureReason::Cancelled; the main stream maps that to fabro.run.cancelled.

Who owns filtering policy?
fabro-server. fabro-store only stores and replays MainEvents; it does not decide which run events matter.

What happens if mirroring fails during live execution?
The failure is logged and normal run event forwarding continues. This PR does not make automation correctness guarantees yet.

Does deleting a run delete main events?
No. Run deletion remains under runs/<run_id>/...; main events live under events/main/....

Verification
Focused tests cover serde, storage/recovery/delete behavior, mapper behavior, and server mirroring. Format and clippy passed.

@brynary brynary mentioned this pull request Jun 29, 2026
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