FE-763: Petrinaut event stream β initial markings + transition firings#158
FE-763: Petrinaut event stream β initial markings + transition firings#158kostandinang wants to merge 3 commits into
Conversation
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
dc53d4c to
f6356f7
Compare
0d89acd to
7590a6f
Compare
PR SummaryLow Risk Overview When Coverage adds unit tests for the adapter/JSONL and an engine contract test wiring Reviewed by Cursor Bugbot for commit a26f784. Bugbot is set up for automated code reviews on this repo. Configure here. |
π€ Augment PR SummarySummary: 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:
Technical Notes: The stream writes one JSON object per line (JSONL) and fans out to an optional in-process π€ Was this summary useful? React with π or π |
f6356f7 to
ff3504c
Compare
7590a6f to
9c0641c
Compare
ff3504c to
2827948
Compare
12887cc to
944e8b1
Compare
ee6ae76 to
e535169
Compare
944e8b1 to
74e4df4
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
β 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.
74e4df4 to
934a70b
Compare
e535169 to
5fd0d14
Compare
934a70b to
cf76d37
Compare
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>
cf76d37 to
a26f784
Compare


Summary
petrinaut-events.tsβcreatePetrinautEventStream({ runId, filePath?, tokenIdFn?, onEvent? })returns aNetEventSinkadapter plusemitInitialMarking(blueprint)helper. Writes one JSON object per line tofilePathwhen provided and fans out toonEventfor in-process consumers.<runDir>/petrinaut-events.jsonlon every run:initial_markingup-front, thentransition_firedper fire, then anynet_halted/net_deadlockedterminal event.{ id: <UUID>, ...payload };input/outputareRecord<place, tokens[]>; every event carriesrunId.Context
engine.tswithinput.runDir-conditional behavior; chaining them avoids a self-conflict in that file. Stack ordering is a Graphite concern; the underlying frontier items remain "parallel" permemory/PLAN.md.reports.jsonlβ Petrinaut consumes the new stream directly; reports stay the orchestrator's internal log.slice:<sid>:halted/epic:<eid>:haltedplaces viatransition_firedevents, plus a terminalnet_haltedfrom the engine.What changed
src/orchestrator/src/petrinaut-events.ts: pure adapter from the orchestrator'sNetEventstream to Petrinaut's wire format.PetrinautEventdiscriminated union overinitial_marking/transition_fired/net_halted/net_deadlocked;PetrinautToken { id, sliceId?, epicId?, retryCount?, reworkCount?, haltReason? }.emitInitialMarking(blueprint)derives the up-front marking fromblueprint.initialTokens, assigns fresh UUIDs, and groups by place.sink.emit(event)translates eachNetEventinto the corresponding Petrinaut event, buildinginput/outputrecords from the parallelconsumedTokens/producedTokensarrays.petri-net.ts:NetEventgains parallel optionalconsumedTokens?: Token[][]andproducedTokens?: Token[][]arrays β populated fortransition_firedso the adapter can include per-arc token payload without re-reading the net.scheduleDeferredgains aconsumedTokensparameter alongsideconsumedPlacesso async fires emit the same shape as sync fires.net-compiler.ts: all fourscheduleDeferredcall sites pass the capturedconsumedtokens through.engine.ts: wheninput.runDiris present, opens a Petrinaut event stream writing to<runDir>/petrinaut-events.jsonl, emitsinitial_markingfrom the compiled blueprint, then passes the sink tonet.run().Verification
petrinaut-events.test.ts: initial_marking grouping, transition_fired adapter shape, terminal-event forwarding, JSONL file roundtrip viamkdtempSync+readFileSync.engine-contract.test.tsrunningsimplePlanhappy path with the Petrinaut sink β assertsinitial_markingfirst,runIdon every event, the FE-761 Slice 4 dispatch + complete transition names both appear in the stream, every token carries anid, and happy paths emit nonet_halted/net_deadlocked.npm run verify(check + test + build) green.Out of scope
Traceability
petri-event-streaminmemory/PLAN.md; stacks on FE-762 (which stacks on FE-761).