Bundled pre-session doc + ops work: - docs/modules/**: sync across advisory-ai, airgap, cli, excititor, export-center, findings-ledger, notifier, notify, platform, router, sbom-service, ui, web (architectural + operational updates) - docs/features/**: updates to checked excititor vex pipeline, developer workspace, quick verify drawer - docs top-level: README, quickstart, API_CLI_REFERENCE, UI_GUIDE, code-of-conduct/TESTING_PRACTICES updates - docs/qa/feature-checks/: FLOW.md + excititor state update - docs/implplan/: remaining sprint updates + new Concelier source credentials sprint (SPRINT_20260422_003) - docs-archived/implplan/: 30 sprint archival moves (ElkSharp series, misc completed sprints) - devops/compose: .env + services compose + env example + router gateway config updates File-level granularity preserved. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3.1 KiB
3.1 KiB
ElkSharp — Node Slot Lattice (per-kind port metadata)
Status: Introduced by Sprint 20260420.013 (BK Phase A — port-slot lattice). Consumers:
ElkOrthogonalRouter(Sprint 16+), futureElkBrandesKopfPlacement(Sprint 14+). Source:src/__Libraries/StellaOps.ElkSharp/ElkNodeSlotLattice.cs.
What is the lattice?
For every ElkNode.Kind that the Stella Ops workflow engine emits, the
lattice declares a fixed set of slots on the node's boundary. Each slot
is a tuple (face, fractionAlongFace, direction) where:
face— one ofNORTH,SOUTH,EAST,WEST.fractionAlongFace—0.0 .. 1.0position along the face.direction—IncomingOnly,OutgoingOnly, orEither.
The lattice is static per kind. Slot counts, positions, and direction restrictions are hard-coded; no per-instance configuration. Unknown kinds fall through to the rectangular default.
Per-kind declarations
| Kind | Faces & slots | Direction |
|---|---|---|
Start |
1 slot on SOUTH, centred (0.5) | OutgoingOnly |
End |
1 slot on NORTH, centred (0.5) | IncomingOnly |
Task, SetState, BusinessReference, TransportCall, ServiceCall, Timer, Repeat |
3 slots each on E/W faces (0.25, 0.5, 0.75); 5 slots each on N/S faces (0.17, 0.33, 0.5, 0.67, 0.83) | Either |
Decision (diamond) |
N tip (0.5) incoming; S tip (0.5) outgoing; E tip (0.5) and W tip (0.5) incoming-only | mixed (see column) |
Fork, Join (hexagon) |
2 slots per N face (0.25, 0.75); 2 slots per S face (0.25, 0.75); E/W closed | Either |
Dummy |
point — 1 slot on every face at 0.5 | Either |
API surface
// Fetch all declared slots for a kind.
IReadOnlyList<ElkNodeSlot> ElkNodeSlotLattice.GetSlots(string kind);
// Resolve a slot to an absolute (x, y) point on a positioned node.
ElkPoint ElkNodeSlotLattice.ResolveSlotPoint(
ElkPositionedNode node,
ElkNodeSlot slot);
// Pick the best slot for an edge given its direction toward the other
// endpoint. Respects direction restrictions (IncomingOnly vs OutgoingOnly).
ElkNodeSlot? ElkNodeSlotLattice.ResolveSlotForEdge(
ElkPositionedNode node,
ElkPoint edgeDirection,
bool isIncoming);
Consumer contract
ElkOrthogonalRouter.TryRouteusesResolveSlotForEdgeto pick exit/entry slots on the source and target's primary-end / primary-start faces.- When the lattice returns no admissible slot, callers fall back to the legacy face-centre heuristic.
- Multiple edges landing on the same slot are expected: the router's per-group spread heuristic spaces them apart.
Design notes
- Slot sets are the minimum viable count for the current workflow set. Denser faces can be added without breaking the contract (the lattice API is additive).
- Direction restrictions on
Decisiontips encode the flow-control semantics of diamond gateways: the south tip is the sole outgoing anchor so the downstream branches radiate from a single point. Fork/Joinuse 2-slot N/S faces because parallel/merge branches are inherently binary-clustered in Stella Ops workflow semantics.