chore(docs+devops): cross-module doc sync + sprint archival moves + compose updates

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>
This commit is contained in:
master
2026-04-22 16:06:39 +03:00
parent ad77711ac2
commit 7943cfb3af
121 changed files with 10483 additions and 387 deletions

View File

@@ -0,0 +1,69 @@
# ElkSharp — Node Slot Lattice (per-kind port metadata)
> Status: Introduced by Sprint 20260420.013 (BK Phase A — port-slot lattice).
> Consumers: `ElkOrthogonalRouter` (Sprint 16+), future `ElkBrandesKopfPlacement` (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 of `NORTH`, `SOUTH`, `EAST`, `WEST`.
- `fractionAlongFace``0.0 .. 1.0` position along the face.
- `direction``IncomingOnly`, `OutgoingOnly`, or `Either`.
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
```csharp
// 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.TryRoute` uses `ResolveSlotForEdge` to 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 `Decision` tips 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` / `Join` use 2-slot N/S faces because parallel/merge branches are
inherently binary-clustered in Stella Ops workflow semantics.