Repository navigation
feat(kr0ki-core): Phase 5 — DecisionNode/ForkNode/JoinNode/MergeNode in ProcessChart (closes #268) - #273
Conversation
Extend ProcessChart.from_sysml to handle all four KerML Activity control-flow node kinds (added to ufo-types in rev 6c8e9cc): - DecisionNode: routes via guard:<id>_true / guard:<id>_false events; decide(node_id, guards) evaluates the <id>_cond guard and dispatches - ForkNode: fires fork:<id> event → first outgoing branch; callers handle remaining parallel branches outside the chart - JoinNode: pass-through atomic state; advances on "next" (lazily handles the synchronisation contract at the chart level) - MergeNode: same pass-through semantics as JoinNode Control nodes pick up parent assignment via FeatureMembership (same as ActionUsage) to avoid "more than one root" chart build error. Also bumps ufo-types dep pin from 5f8d018 to 6c8e9cc (feat/control-flow- nodes-rebased on ufo-types — awaiting upstream merge as PR #35/#36). 7 tests: 3 existing + 4 new (decision true/false, fork, merge). All pass. Closes #268 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| let true_tgt = targets.first().copied().unwrap_or(sid); | ||
| let false_tgt = targets.get(1).copied().unwrap_or(sid); | ||
| let mut sb = StateBuilder::atomic(sid) | ||
| .on(&format!("guard:{sid}_true"), true_tgt) |
| let true_tgt = targets.first().copied().unwrap_or(sid); | ||
| let false_tgt = targets.get(1).copied().unwrap_or(sid); | ||
| let mut sb = StateBuilder::atomic(sid) | ||
| .on(&format!("guard:{sid}_true"), true_tgt) |
| let false_tgt = targets.get(1).copied().unwrap_or(sid); | ||
| let mut sb = StateBuilder::atomic(sid) | ||
| .on(&format!("guard:{sid}_true"), true_tgt) | ||
| .on(&format!("guard:{sid}_false"), false_tgt); |
| let false_tgt = targets.get(1).copied().unwrap_or(sid); | ||
| let mut sb = StateBuilder::atomic(sid) | ||
| .on(&format!("guard:{sid}_true"), true_tgt) | ||
| .on(&format!("guard:{sid}_false"), false_tgt); |
| let targets = successors.get(sid).cloned().unwrap_or_default(); | ||
| let first_tgt = targets.first().copied().unwrap_or(sid); | ||
| let mut sb = | ||
| StateBuilder::atomic(sid).on(&format!("fork:{sid}"), first_tgt); |
| let targets = successors.get(sid).cloned().unwrap_or_default(); | ||
| let first_tgt = targets.first().copied().unwrap_or(sid); | ||
| let mut sb = | ||
| StateBuilder::atomic(sid).on(&format!("fork:{sid}"), first_tgt); |
Adversarial Review: feat(kr0ki-core): Phase 5 — DecisionNode/ForkNode/JoinNode/MergeNode in ProcessChart (closes #268)Provider: sm3lly/neo-coder | Generated: 2026-10-13T13:48Z 🟢 AdvocateApproach AssessmentThis PR correctly extends
Strong Points
Evidence
Recommended to Merge If...
The implementation is clean, well-tested, and follows established patterns in the codebase. No architectural concerns. 🔴 ChallengerConcernsDecisionNode routing is positional, not guard-driven. The PR maps the first outgoing Succession edge to the "true" branch and the second to "false" — but SysML v2 DecisionNode guards are attached to edges, not nodes. If the diagram author reorders edges in the tool, routing silently swaps. The PR's own table says ForkNode only advances one branch. JoinNode and MergeNode are conflated. The PR treats both as pass-through on "next", but in SysML v2 they have different semantics: JoinNode requires all incoming branches to arrive (synchronization), while MergeNode passes through on any incoming branch (choice merge). The PR's own table says JoinNode "waits for 'next' from any incoming" which is actually MergeNode semantics. This will produce incorrect behavior for true join scenarios. Guard naming convention is undocumented magic. The guard name Missing Coverage
Alternative ApproachesFor DecisionNode: Instead of positional edge mapping, require guard expressions on edges and evaluate them at runtime. If that's too complex for this phase, at least validate that exactly two outgoing edges exist and document the positional assumption prominently. For ForkNode/JoinNode: Consider whether a single-threaded state machine is the right model for SysML activity diagrams at all. Activity diagrams are inherently concurrent. If concurrency is out of scope, rename the API to make that explicit (e.g., For JoinNode vs MergeNode: Either implement the correct semantics (track incoming branch count for JoinNode) or pick one and document why the other is deferred. Don't silently conflate them. Blocking IssuesThe DecisionNode positional routing is a correctness issue that should be fixed before merge. The JoinNode/MergeNode conflation is also a correctness issue. These aren't just "nice to have" improvements — they mean the implementation doesn't actually match the SysML v2 semantics it claims to support. |
Adversarial Review: feat(kr0ki-core): Phase 5 — DecisionNode/ForkNode/JoinNode/MergeNode in ProcessChart (closes #268)Provider: sm3lly/neo-coder | Generated: 2026-10-15T17:48Z 🟢 AdvocateApproach AssessmentThe PR correctly extends the existing
The PR also correctly identifies its dependency on the upstream Strong Points
Evidence
Recommended to Merge If...
The implementation is clean, well-documented, and correctly scoped. The design decisions (especially around ForkNode concurrency) are pragmatic and well-justified. 🔴 ChallengerConcernsDecisionNode guard naming convention is fragile. The PR hardcodes ForkNode semantics are incomplete. The PR advances only to the first outgoing branch and says "caller handles concurrency." This pushes the hard part of fork semantics (parallel execution) onto the caller while the chart itself only tracks one branch. If the caller forgets to fire events for other branches, the chart state becomes inconsistent. The JoinNode vs MergeNode distinction is lost. Both map to identical pass-through behavior on Succession edge ordering assumption. For DecisionNode, the PR assumes the first outgoing edge is the true branch and the second is the false branch. SysML doesn't guarantee edge ordering — this is an arbitrary convention that will break when edges are defined in different order. No validation of DecisionNode arity. The PR expects exactly two outgoing edges from a DecisionNode but doesn't validate this. A decision with one or three edges will build successfully but behave incorrectly. Missing Coverage
Alternative ApproachesFor DecisionNode guards: Instead of hardcoding For JoinNode vs MergeNode: Implement actual JoinNode semantics using a counter or set of arrived branches. This requires tracking which incoming edges have fired, which is more complex but correct. Alternatively, rename the methods to be explicit about the simplified semantics. For edge ordering: Require explicit edge labels or a mapping from decision node + branch value to target, rather than relying on insertion order. Blocking IssuesJoinNode semantics are wrong. The PR claims to implement JoinNode but implements MergeNode semantics instead. This is a correctness bug, not just a missing feature. Either implement proper AND-join semantics or rename the node kind to avoid misleading users. This should block merge until resolved or documented as a known limitation with a clear distinction from true JoinNode behavior. |
396e3a2
into
task/259-p4-kr0ki-statechart
Summary
ProcessChart.from_sysmlto handle all four KerML Activity control-flow node kinds:DecisionNode,ForkNode,JoinNode,MergeNodedecide(node_id, guards)andfork(node_id)drive methods onProcessChartufo-typespin from5f8d018→6c8e9cc(feat/control-flow-nodes-rebased— awaiting upstream PR feat(sysml_model): add KerML Activity control-flow nodes (DecisionNode/ForkNode/JoinNode/MergeNode) ufo-types#35 merge; will update to squash SHA after merge)Control-flow node semantics
DecisionNode"guard:<id>_true/false"decide(id, guards)reads<id>_condguardForkNode"fork:<id>"JoinNode"next"MergeNode"next"Test plan
ufo-typespin updates to merged SHA after feat(sysml_model): add KerML Activity control-flow nodes (DecisionNode/ForkNode/JoinNode/MergeNode) ufo-types#35 landsDependencies
Stacked on PR #265 (
task/259-p4-kr0ki-statechart).Requires PromptExecution/ufo-types#35 to merge (or can be rebased to main once merged).
Closes #268
🤖 Generated with Claude Code