Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/edda

crates/nornir-fleet/fleet.runa.ron

// fleet.runa.ron — the compose-of-the-CONSTELLATION descriptor (the full 5-app fleet)
//
// The North-Star full-system-autotest harness, grounded in code
// (`.nornir/full-system-autotest-design.md`). This runa pipeline lowers onto engines
// that ALREADY EXIST — jera's boot ledger + draupnir OCI, each app's robotui/web
// scenarios, and nornir-testmatrix's Gap==∅ gate — via the `fleet` crate's
// FleetHarness (the edda-side reference dwarves' runa_exec mirrors).
//
//   build ─▶ provision (jera→draupnir OCI) ─▶ drive (egui fat + web thin) ─▶ gate (Gap==∅)
//
// FIVE members — the constellation's UI apps (nornir · dwarves · holger · korp ·
// ordning). Each declares its FULL, CODE-SOURCED viz-tab surface (each tab ×
// {fat, thin}); the CoverageGate aggregates coverage ACROSS all five into ONE
// constellation-wide Gap==∅ verdict — a missing surface on ANY app reds the gate,
// naming `app:tab@leg`.
//
// Surface sourcing (verified from each app's canonical tab enumeration — NOT invented):
//   • nornir  — 28 tabs · `nornir/src/viz/app.rs`            `Tab::ALL` / `Tab::id()`
//   • dwarves — 18 tabs · `dwarves/dwarves-gui/src/main.rs`  `TabbedView::new(...)`
//   • holger  — 11 tabs · `holger/server/ui/src/app.rs`       `TAB_ORDER` / `slot_id()`
//   • korp    — 16 tabs · `korp/src/main.rs`                  `Tab::ALL` / `Tab::key()`
//   • ordning — 12 views· `ordning/crates/ordning-gui/src/lib.rs` facet re-exports
//
// HONESTY (no false green): today ONLY dwarves mounts a `CmdJsonServer`
// (dwarves-gui `--robot-remote <socket>`), so a real run installs a
// `MountRouter` that drives dwarves for real and answers `Ok(None)` (probe-only)
// for the other four — their `@fat` nodes then read as not-yet-driven and the gate
// reds, naming each. The four apps carry a per-app mount TODO below; each flips to
// really-driven the moment it grows its own `--robot-remote`-style mount.
//
// Teardown is NOT a step: it is RampGuard-style RAII — the FleetHarness drops and
// every spun member is killed through jera's ledger (`draupnir down`), so a
// crashed run never leaks a container.
Pipeline(
    name: "nordisk-fleet-of-five",
    triggers: [ Manual ],
    stages: [
        // 1. BUILD — materialise each app's server/demo Container variant (build-thing).
        //    A real run lowers these to draupnir `build_image`; the mock declares the
        //    image ref directly on each SpinFleetMember below.
        Stage(
            name: "build",
            steps: [
                Build(variant: "nornir-server-container"),
                Build(variant: "dwarves-gui-container"),
                Build(variant: "holger-server-container"),
                Build(variant: "korp-container"),
                Build(variant: "ordning-demo-container"),
            ],
        ),

        // 2. PROVISION — spin each member as an OCI container through jera → draupnir
        //    run_to_completion, holding on the healthcheck barrier until Ready. Each
        //    member's `surface` is its declared viz-tab set the gate requires proven
        //    (each tab must be driven BOTH fat and thin).
        Stage(
            name: "provision",
            needs: ["build"],
            steps: [
                // ── nornir — the warehouse/viz app (28 tabs). ─────────────────────
                SpinFleetMember(
                    name: "nornir",
                    image: "localhost/nornir-server:fleet",
                    ports: [50051, 8080],
                    env: [("NORNIR_ZONE", "test")],
                    surface: [
                        "Nornir", "Timeline", "DepGraph", "CallGraph", "Architecture",
                        "Metro", "Helix", "Bloodstream", "Funnel", "TimeTravel",
                        "LiveRun", "Release", "Knowledge", "Warehouse", "WarehouseDeck",
                        "Mcp", "Search", "Gates", "Bench", "Test", "Leaderboard",
                        "Chat", "Security", "Dwarves", "Holger", "Manual", "Claude",
                        "Models",
                    ],
                ),
                // ── dwarves — the CI/build forge demo (18 tabs). THE MOUNTED APP:
                //    dwarves-gui `--robot-remote <socket>` serves a CmdJsonServer, so
                //    the fat leg drives it for real over a MountRouter/UnixEguiWire. ──
                SpinFleetMember(
                    name: "dwarves",
                    image: "localhost/dwarves-gui:fleet",
                    ports: [50052, 8081],
                    env: [("DWARVES_ZONE", "test")],
                    surface: [
                        "Warehouse", "Forge", "Pipeline", "CI/CD", "Builds", "Funnel",
                        "Claude", "Jera", "Tools", "Status", "Release", "Benches",
                        "Fleet", "Binary", "Coderoom", "Saga", "Settings", "Profile",
                    ],
                ),
                // ── holger — the artifact-registry operator UI (11 tabs). ─────────
                //    TODO(mount): holger-server must mount a CmdJsonServer over its
                //    egui UI (mirror dwarves-gui `--robot-remote <socket>`); until
                //    then MountRouter answers Ok(None) → probe-only (@fat uncovered).
                SpinFleetMember(
                    name: "holger",
                    image: "localhost/holger-server:fleet",
                    ports: [50053, 8082],
                    env: [("HOLGER_ZONE", "test")],
                    surface: [
                        "status", "repos", "browse", "archive", "artifact", "graph",
                        "helix", "lineage", "tools", "upload", "signin",
                    ],
                ),
                // ── korp — the graph/geo analysis app (16 tabs). ──────────────────
                //    TODO(mount): korp must mount a CmdJsonServer over its egui UI
                //    (mirror dwarves-gui `--robot-remote <socket>`); until then
                //    MountRouter answers Ok(None) → probe-only (@fat uncovered).
                SpinFleetMember(
                    name: "korp",
                    image: "localhost/korp:fleet",
                    ports: [50054, 8083],
                    env: [("KORP_ZONE", "test")],
                    surface: [
                        "demo", "pipelines", "spark", "graph", "falkordb", "data",
                        "tables", "ingest", "analysis", "map", "geo", "timeline",
                        "jobs", "lineage", "investigations", "settings",
                    ],
                ),
                // ── ordning — the planning UI (12 facet views). Serverless (embedded
                //    event-store, no network port) → declared with the demo image and
                //    an empty published-port set. ────────────────────────────────────
                //    TODO(mount): ordning-demo must mount a CmdJsonServer over its egui
                //    UI (mirror dwarves-gui `--robot-remote <socket>`); until then
                //    MountRouter answers Ok(None) → probe-only (@fat uncovered).
                SpinFleetMember(
                    name: "ordning",
                    image: "localhost/ordning-demo:fleet",
                    ports: [],
                    env: [("ORDNING_ZONE", "test")],
                    surface: [
                        "calendar", "cfd", "file", "gantt", "kanban", "markdown",
                        "plan", "search", "sprint", "timeline", "toast", "workload",
                    ],
                ),
            ],
        ),

        // 3. DRIVE — run each member's egui (fat, wire-remote over its CmdJsonServer
        //    mount) + web (`nornir robot` WebDriver, thin) scenarios against the spun
        //    container, streaming a TestResultRow per scenario. Driving BOTH legs of
        //    every tab is what makes the gate green. dwarves is driven for real; the
        //    other four are probe-only until they mount (their @fat legs stay
        //    uncovered — the fleet is HONEST about which apps are really driven).
        Stage(
            name: "drive",
            needs: ["provision"],
            steps: [
                RobotDriveMember(
                    member: "nornir",
                    egui_scenarios: [
                        "Nornir", "Timeline", "DepGraph", "CallGraph", "Architecture",
                        "Metro", "Helix", "Bloodstream", "Funnel", "TimeTravel",
                        "LiveRun", "Release", "Knowledge", "Warehouse", "WarehouseDeck",
                        "Mcp", "Search", "Gates", "Bench", "Test", "Leaderboard",
                        "Chat", "Security", "Dwarves", "Holger", "Manual", "Claude",
                        "Models",
                    ],
                    web_scenarios: [
                        "Nornir", "Timeline", "DepGraph", "CallGraph", "Architecture",
                        "Metro", "Helix", "Bloodstream", "Funnel", "TimeTravel",
                        "LiveRun", "Release", "Knowledge", "Warehouse", "WarehouseDeck",
                        "Mcp", "Search", "Gates", "Bench", "Test", "Leaderboard",
                        "Chat", "Security", "Dwarves", "Holger", "Manual", "Claude",
                        "Models",
                    ],
                ),
                RobotDriveMember(
                    member: "dwarves",
                    egui_scenarios: [
                        "Warehouse", "Forge", "Pipeline", "CI/CD", "Builds", "Funnel",
                        "Claude", "Jera", "Tools", "Status", "Release", "Benches",
                        "Fleet", "Binary", "Coderoom", "Saga", "Settings", "Profile",
                    ],
                    web_scenarios: [
                        "Warehouse", "Forge", "Pipeline", "CI/CD", "Builds", "Funnel",
                        "Claude", "Jera", "Tools", "Status", "Release", "Benches",
                        "Fleet", "Binary", "Coderoom", "Saga", "Settings", "Profile",
                    ],
                ),
                RobotDriveMember(
                    member: "holger",
                    egui_scenarios: [
                        "status", "repos", "browse", "archive", "artifact", "graph",
                        "helix", "lineage", "tools", "upload", "signin",
                    ],
                    web_scenarios: [
                        "status", "repos", "browse", "archive", "artifact", "graph",
                        "helix", "lineage", "tools", "upload", "signin",
                    ],
                ),
                RobotDriveMember(
                    member: "korp",
                    egui_scenarios: [
                        "demo", "pipelines", "spark", "graph", "falkordb", "data",
                        "tables", "ingest", "analysis", "map", "geo", "timeline",
                        "jobs", "lineage", "investigations", "settings",
                    ],
                    web_scenarios: [
                        "demo", "pipelines", "spark", "graph", "falkordb", "data",
                        "tables", "ingest", "analysis", "map", "geo", "timeline",
                        "jobs", "lineage", "investigations", "settings",
                    ],
                ),
                RobotDriveMember(
                    member: "ordning",
                    egui_scenarios: [
                        "calendar", "cfd", "file", "gantt", "kanban", "markdown",
                        "plan", "search", "sprint", "timeline", "toast", "workload",
                    ],
                    web_scenarios: [
                        "calendar", "cfd", "file", "gantt", "kanban", "markdown",
                        "plan", "search", "sprint", "timeline", "toast", "workload",
                    ],
                ),
            ],
        ),

        // 4. GATE — nornir-testmatrix Surface − Covered − Allow = Gap; ASSERT Gap==∅
        //    across ALL FIVE members (one constellation-wide verdict). RED — naming
        //    the exact `app:tab@leg` — if any declared surface node went undriven.
        //    Today this is RED by construction: only dwarves mounts, so every other
        //    app's `@fat` nodes are probe-only (uncovered) until they mount.
        Stage(
            name: "gate",
            needs: ["drive"],
            steps: [
                CoverageGate(workspace: "nordisk", allow: []),
            ],
        ),
    ],
)