Skip to content

FE-763: Petrinaut event stream β€” initial markings + transition firings#158

Open
kostandinang wants to merge 3 commits into
ka/fe-762-petri-blueprint-exportfrom
ka/fe-763-petri-event-stream
Open

FE-763: Petrinaut event stream β€” initial markings + transition firings#158
kostandinang wants to merge 3 commits into
ka/fe-762-petri-blueprint-exportfrom
ka/fe-763-petri-event-stream

Conversation

@kostandinang
Copy link
Copy Markdown
Contributor

@kostandinang kostandinang commented May 27, 2026

Summary

  • New module petrinaut-events.ts β€” createPetrinautEventStream({ runId, filePath?, tokenIdFn?, onEvent? }) returns a NetEventSink adapter plus emitInitialMarking(blueprint) helper. Writes one JSON object per line to filePath when provided and fans out to onEvent for in-process consumers.
  • Cook writes <runDir>/petrinaut-events.jsonl on every run: initial_marking up-front, then transition_fired per fire, then any net_halted / net_deadlocked terminal event.
  • Wire format matches the cross-team agreement (2026-05-26): tokens are { id: <UUID>, ...payload }; input / output are Record<place, tokens[]>; every event carries runId.

Context

  • Pairs with FE-762 (blueprint export) β€” together they give Petrinaut everything it needs to visualize a live cook run. Blueprint is the static topology; this PR is the runtime overlay.
  • Stacks on FE-762 because both writers extend engine.ts with input.runDir-conditional behavior; chaining them avoids a self-conflict in that file. Stack ordering is a Graphite concern; the underlying frontier items remain "parallel" per memory/PLAN.md.
  • Decouples visualization from reports.jsonl β€” Petrinaut consumes the new stream directly; reports stay the orchestrator's internal log.
  • Halt outcomes (FE-761 Slice 2b halted-as-place) appear naturally as halt tokens landing on slice:<sid>:halted / epic:<eid>:halted places via transition_fired events, plus a terminal net_halted from the engine.

What changed

  • New src/orchestrator/src/petrinaut-events.ts: pure adapter from the orchestrator's NetEvent stream to Petrinaut's wire format.
    • Types: PetrinautEvent discriminated union over initial_marking / transition_fired / net_halted / net_deadlocked; PetrinautToken { id, sliceId?, epicId?, retryCount?, reworkCount?, haltReason? }.
    • emitInitialMarking(blueprint) derives the up-front marking from blueprint.initialTokens, assigns fresh UUIDs, and groups by place.
    • sink.emit(event) translates each NetEvent into the corresponding Petrinaut event, building input / output records from the parallel consumedTokens / producedTokens arrays.
  • petri-net.ts: NetEvent gains parallel optional consumedTokens?: Token[][] and producedTokens?: Token[][] arrays β€” populated for transition_fired so the adapter can include per-arc token payload without re-reading the net. scheduleDeferred gains a consumedTokens parameter alongside consumedPlaces so async fires emit the same shape as sync fires.
  • net-compiler.ts: all four scheduleDeferred call sites pass the captured consumed tokens through.
  • engine.ts: when input.runDir is present, opens a Petrinaut event stream writing to <runDir>/petrinaut-events.jsonl, emits initial_marking from the compiled blueprint, then passes the sink to net.run().

Verification

  • 4 new unit tests in petrinaut-events.test.ts: initial_marking grouping, transition_fired adapter shape, terminal-event forwarding, JSONL file roundtrip via mkdtempSync + readFileSync.
  • 1 new end-to-end test in engine-contract.test.ts running simplePlan happy path with the Petrinaut sink β€” asserts initial_marking first, runId on every event, the FE-761 Slice 4 dispatch + complete transition names both appear in the stream, every token carries an id, and happy paths emit no net_halted / net_deadlocked.
  • All 130 orchestrator tests pass; full npm run verify (check + test + build) green.
  • Outer-loop end-to-end replay against Petrinaut's live consumer deferred to FE-764 transport work and cross-team validation.

Out of scope

  • Transport / sync server β€” wiring the event stream to a live Petrinaut session (FE-764).
  • Token UUID lifecycle across consume β†’ emit (lineage tracing) β€” v1 generates fresh UUIDs per emission; pending Petrinaut team confirmation on whether identity should persist.
  • Petrinaut blueprint export β€” sibling sub-issue (FE-762), already in the stack.
  • Final JSON envelope shape per Petrinaut's loader β€” v1 best-guess; expect cross-team revisions once Petrinaut publishes their schema.

Traceability

  • Linear FE-763 (parent FE-760); pairs with FE-762 and feeds FE-764.
  • Frontier petri-event-stream in memory/PLAN.md; stacks on FE-762 (which stacks on FE-761).

@kostandinang kostandinang force-pushed the ka/fe-762-petri-blueprint-export branch from dc53d4c to f6356f7 Compare May 27, 2026 23:16
@kostandinang kostandinang force-pushed the ka/fe-763-petri-event-stream branch from 0d89acd to 7590a6f Compare May 27, 2026 23:16
@kostandinang kostandinang marked this pull request as ready for review May 27, 2026 23:17
@cursor
Copy link
Copy Markdown

cursor Bot commented May 27, 2026

PR Summary

Low Risk
Observability-only integration behind runDir with best-effort I/O; interpreter event shape changes are localized to the new adapter and existing optional sinks.

Overview
Adds a Petrinaut-shaped runtime event stream so live cook runs can be visualized without parsing reports.jsonl. A new createPetrinautEventStream adapter turns internal net events into initial_marking, transition_fired (per-place input/output with { id, ...payload } tokens), and terminal net_halted / net_deadlocked events, optionally appending JSONL to <runDir>/petrinaut-events.jsonl and fanning out via onEvent.

When input.runDir is set, the orchestrator emits initial_marking from the compiled blueprint, then passes the stream’s NetEventSink into net.run() (best-effort; failures do not halt the cook). The Petri interpreter now attaches consumedTokens / producedTokens on transition_fired events (including deferred completions via an extended scheduleDeferred), and deferred handler call sites pass through the consumed token snapshot.

Coverage adds unit tests for the adapter/JSONL and an engine contract test wiring wireHandlers + the stream on a happy-path plan.

Reviewed by Cursor Bugbot for commit a26f784. Bugbot is set up for automated code reviews on this repo. Configure here.

@augmentcode
Copy link
Copy Markdown

augmentcode Bot commented May 27, 2026

πŸ€– Augment PR Summary

Summary: Adds a Petrinaut-focused runtime event stream to the orchestrator so Petrinaut can visualize live cook runs (initial marking + per-transition firings + terminal outcomes).

Changes:

  • Introduced createPetrinautEventStream() (petrinaut-events.ts) that adapts internal NetEvents into the cross-team JSONL wire format and can optionally write petrinaut-events.jsonl.
  • Added emitInitialMarking(blueprint) helper to emit a single up-front initial_marking event derived from the compiled blueprint.
  • Extended NetEvent to include per-arc consumedTokens/producedTokens so downstream adapters can include token payloads without re-reading net state.
  • Threaded consumed token data through deferred transition completion (scheduleDeferred) and through net-compiler call sites.
  • When input.runDir is present, the orchestrator now opens the Petrinaut JSONL stream, emits initial_marking, and passes the sink into net.run().
  • Added unit tests for adapter shape + JSONL roundtrip, plus an end-to-end contract test asserting ordering and per-event runId.

Technical Notes: The stream writes one JSON object per line (JSONL) and fans out to an optional in-process onEvent callback; token IDs are generated per emission (lineage persistence is explicitly deferred).

πŸ€– Was this summary useful? React with πŸ‘ or πŸ‘Ž

Copy link
Copy Markdown

@augmentcode augmentcode Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed. 3 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

Comment thread src/orchestrator/src/petrinaut-events.ts Outdated
Comment thread src/orchestrator/src/petrinaut-events.ts Outdated
Comment thread src/orchestrator/src/engine-contract.test.ts Outdated
@kostandinang kostandinang force-pushed the ka/fe-762-petri-blueprint-export branch from f6356f7 to ff3504c Compare May 27, 2026 23:21
@kostandinang kostandinang force-pushed the ka/fe-763-petri-event-stream branch from 7590a6f to 9c0641c Compare May 27, 2026 23:21
Comment thread src/orchestrator/src/petri-net.ts
@kostandinang kostandinang force-pushed the ka/fe-762-petri-blueprint-export branch from ff3504c to 2827948 Compare May 28, 2026 10:34
@kostandinang kostandinang force-pushed the ka/fe-763-petri-event-stream branch 2 times, most recently from 12887cc to 944e8b1 Compare May 28, 2026 10:52
@kostandinang kostandinang force-pushed the ka/fe-762-petri-blueprint-export branch 2 times, most recently from ee6ae76 to e535169 Compare May 28, 2026 11:33
@kostandinang kostandinang force-pushed the ka/fe-763-petri-event-stream branch from 944e8b1 to 74e4df4 Compare May 28, 2026 11:33
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 74e4df4. Configure here.

Comment thread src/orchestrator/src/engine.ts
@kostandinang kostandinang force-pushed the ka/fe-763-petri-event-stream branch from 74e4df4 to 934a70b Compare May 28, 2026 12:59
@kostandinang kostandinang force-pushed the ka/fe-762-petri-blueprint-export branch from e535169 to 5fd0d14 Compare May 28, 2026 12:59
@kostandinang kostandinang force-pushed the ka/fe-763-petri-event-stream branch from 934a70b to cf76d37 Compare May 29, 2026 09:07
kostandinang and others added 2 commits May 29, 2026 14:26
Emits the runtime events Petrinaut needs to visualize a live cook run, in
the cross-team-agreed payload shape (2026-05-26 alignment):

  initial_marking:
    { kind, ts, runId, marking: { <place>: [{id, ...payload}] } }

  transition_fired:
    { kind, ts, runId, transitionName,
      input:  { <place>: [{id, ...payload}] },
      output: { <place>: [{id, ...payload}] } }

  net_halted / net_deadlocked:
    { kind, ts, runId }

What landed:

- New module src/orchestrator/src/petrinaut-events.ts: pure adapter
  createPetrinautEventStream({ runId, filePath?, tokenIdFn?, onEvent? })
  returning { sink: NetEventSink, emitInitialMarking(blueprint) }.
  Writes one JSONL object per line to filePath when set and fans out to
  onEvent for in-process consumers (tests, future sync-server forwarder).

- petri-net.ts: NetEvent gains parallel optional consumedTokens?: Token[][]
  and producedTokens?: Token[][] (one entry per arc, same indexing as the
  existing consumed/produced place-name lists). These are populated for
  transition_fired events so the adapter can render the per-place
  { id, ...payload } shape Petrinaut expects without re-reading the net.
  scheduleDeferred gains a consumedTokens parameter alongside the existing
  consumedPlaces so async fires emit the same shape as sync fires.

- net-compiler.ts: all four scheduleDeferred call sites pass the captured
  consumed tokens through to the deferred event.

- engine.ts: when input.runDir is present, opens a Petrinaut event stream
  writing to <runDir>/petrinaut-events.jsonl, emits initial_marking from
  the compiled blueprint up-front, then passes the sink to net.run(). The
  same gate that drives FE-762's <runDir>/net.json write β€” library callers
  without a runDir get the existing no-op behavior.

Halt outcomes (FE-761 Slice 2b halted-as-place):
- Petrinaut sees halt as a halt token traveling through the topology via
  transition_fired events landing on slice:<sid>:halted / epic:<eid>:halted,
  plus a terminal net_halted event from the engine.

Open coordination item: token UUID lifecycle across consume->emit (lineage
tracing). v1 generates fresh UUIDs per emission. When Petrinaut decides
whether identity should persist, this module is the seam to evolve.

Tests:
- 4 unit tests in petrinaut-events.test.ts covering initial_marking,
  transition_fired adapter shape, terminal events, and JSONL file
  roundtrip via mkdtempSync.
- 1 end-to-end test in engine-contract.test.ts running simplePlan happy
  path with the Petrinaut sink β€” asserts initial_marking first, runId on
  every event, the FE-761 Slice 4 dispatch + complete transition names
  both appear, every token carries an id, and happy paths emit no
  net_halted / net_deadlocked.

All 130 orchestrator tests pass; npm run fix + check + build all green.

Co-authored-by: Amp <amp@ampcode.com>
Fail fast on malformed transition_fired events, preserve per-place empty
arrays when arc metadata is partial, and align happy-path test naming.
Wrap stream creation in try/catch so disk failures match net.json export
semantics and cannot halt an otherwise valid cook run.

Co-authored-by: Cursor <cursoragent@cursor.com>
@kostandinang kostandinang force-pushed the ka/fe-763-petri-event-stream branch from cf76d37 to a26f784 Compare May 29, 2026 12:27
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