FE-784: Petrinaut export: colour-fold per-slice subnet#160
FE-784: Petrinaut export: colour-fold per-slice subnet#160kostandinang wants to merge 7 commits into
Conversation
PR SummaryHigh Risk Overview Brownfield cook replaces the reserved stub with Petri-net / Petrinaut alignment (FE-761–763) refactors branching into sibling transitions with Planning/spec docs ( Reviewed by Cursor Bugbot for commit bfb687f. Bugbot is set up for automated code reviews on this repo. Configure here. |
🤖 Augment PR SummarySummary: Implements FE-784 “color fold” for Petrinaut exports by collapsing per-slice subnets into a single slice-independent projection while preserving slice identity on per-token color. Changes:
Technical notes: Folding keeps certain dependency-gated transitions concrete (e.g. 🤖 Was this summary useful? React with 👍 or 👎 |
a26f784 to
ea6bae2
Compare
01f5f73 to
5303ecc
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 5303ecc. Configure here.
ea6bae2 to
4fad33b
Compare
1ff3253 to
aa37122
Compare
3242403 to
fc96a0b
Compare
aa37122 to
b7e96aa
Compare
fc96a0b to
e0331cd
Compare
b7e96aa to
b56f559
Compare
b56f559 to
5ced434
Compare
e0331cd to
1631109
Compare
1631109 to
6a67d0e
Compare
5ced434 to
1c21816
Compare
Fold the per-slice concrete subnet (slice:<sid>:*) N→1 in the Petrinaut export projection, carrying slice identity on the token colour instead of in the node id. Petrinaut's canvas is flat (no hierarchy/grouping), so collapsing the N structurally-identical slice subnets into one is the only way the imported net stays legible at scale — and it dissolves most of the per-slice naming problem. Projection only — runtime (petri-net.ts / net-compiler.ts) is untouched; the live event adapter maps concrete firings onto the same folded net. - petrinaut-fold.ts (new): pure fold rules. foldPlaceId strips slice:<sid>: (per-edge dep-signal:<dependent> places stay unique); foldTransitionId removes the owning slice-id segment; buildTransitionFoldMap collapses groups with identical folded shape but keeps divergent ones (dep-gated slice-ready, dep-signalling return-done) at concrete ids. Defines the SliceColour token type. - petrinaut-export.ts: serializeBlueprint folds places/transitions/arcs and the initial marking; adds tokenTypes + place typeId; schema 0.1.0 → 0.2.0. - petrinaut-events.ts: transition_fired / initial_marking fold concrete → folded ids (blueprint-derived map, with per-event fallback), keeping slice colour on the token. - SDCPN export stays count-fold (colorId: null) until Petrinaut supports discrete string token dimensions (H-6518/H-6519). net.json envelope is additive; folded counts pinned (depPlan 42→23 places, 37→21 transitions). Full gate green (check/test/build). Co-Authored-By: Claude <noreply@anthropic.com>
…stream The colour-fold was split across petrinaut-fold.ts (primitives), serializeBlueprint, and the event adapter, with the event stream capturing fold context as a side effect of emitInitialMarking (silently degrading if a firing arrived before it). Extract a single NetFolding object that owns the whole concrete→folded projection. - petrinaut-fold.ts: add createNetFolding(blueprint) → NetFolding with foldedPlaces / foldedTransitions / foldedMarking<T> / foldTransition / tokenTypes. The id maps stay private; foldPlaceId, foldTransitionId, collectSliceIds, buildTransitionFoldMap, foldedShapeSignature are no longer exported (no parallel API to drift from the folded net). - serializeBlueprint: consumes the folding; keeps schemaVersion, UUID mint, shortPlaceLabel, seedToToken. - createPetrinautEventStream: takes `folding` at construction; deletes the let sliceIds/transitionFoldMap capture and the per-event collectSliceIds fallback. foldedMarking<T> replaces the duplicated groupTokens + byPlace grouping in both consumers. - engine.ts threads one createNetFolding(blueprint) into the stream. Seam invariant: static net.json and the live stream fold identically because both derive from one NetFolding (engine-contract e2e covers it). Behaviour- preserving: export/events/engine-contract assertions unchanged; fold-rule tests re-expressed against the public surface. Full gate green. Co-Authored-By: Claude <noreply@anthropic.com>
The fold keeps divergent transitions concrete and folds uniform ones, but
nothing asserted *which* transitions diverge — a future compiler change that
made a uniform lifecycle transition split per slice would silently re-expand
the graph while reading as "fold worked".
Add an oracle over createNetFolding(compileTopology(depPlan)): the set of
foldedTransitions whose id still carries a slice-id segment must equal exactly
{slice-ready:slice-a, slice-ready:slice-b, slice-a:return-done,
slice-b:return-done}. Exact-set equality, so any extra divergence fails it.
Pure test addition. Full gate green (1504 tests).
Co-Authored-By: Claude <noreply@anthropic.com>
The fold's original motivation was clean SDCPN names — unfolded, every slice's `slice:slice-N:spec-ready` PascalCased to the same base and the name allocator appended collision counters (SliceSliceSpecReady2). Nothing tested that the fold dissolved this. Add oracles over toSdcpnFile(realNet(depPlan)): no place name carries an allocator digit suffix (pascalCaseLetters strips source digits, so any digit is a collision counter) and names are unique; the shared lifecycle place appears once and the 2-slice place count stays well under 2× the single-slice net; the file still satisfies the Petrinaut loader schema. Pure test addition. Gate green (orchestrator 161, check 0 errors, build ✓). Co-Authored-By: Claude <noreply@anthropic.com>
Brunch-owned fold identifiers were British (SLICE_COLOUR_TYPE, 'slice-colour', SliceColour) while the Petrinaut wire field is American (colorId). Rename to a single spelling matching the wire contract: SLICE_COLOR_TYPE / SLICE_COLOR_TYPE_ID / 'slice-color' / SliceColor, plus colour→color across the fold's comments. Petrinaut's own colorId field is unchanged. Add SPEC §Lexicon entries for the durable concepts: `color fold`, `token color`, `folded net`. Retire memory/CARDS.md — the FE-784 follow-up queue (#3, #4, #6) is exhausted. Pure rename + docs. Gate green (check 0 errors, orchestrator 161, build ✓). Co-Authored-By: Claude <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
1c21816 to
bfb687f
Compare


Summary
This PR makes the Petrinaut export readable at scale by color-folding repeated per-slice topology into one shared slice lifecycle. Slice identity moves from node IDs into token color metadata, so a two-slice plan drops from 42 to 23 places and from 37 to 21 transitions.
The fold is an export projection only. Runtime Petri-net execution stays concrete;
net.json,net.sdcpn.json, andpetrinaut-events.jsonlall map that concrete runtime into the same folded view.What Changed
NetFoldingprojection used by both static export and live events.net.jsonand bumps the export schema to0.2.0.colorIdwire vocabulary and updates the spec lexicon.Scope
The folded net is a faithful projection of the compiled blueprint, not a separate runtime model. Runtime files such as
petri-net.tsandnet-compiler.tsare not changed for execution semantics.Verification
npm run verifypassed: format and lint checks, full test suite, and production build.