Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions GROOVE_PANLL_RESEARCH_SUMMARY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Groove Protocol & PanLL Research Summary

> **HISTORICAL (2024, Mistral-Vibe era) — SUPERSEDED.** Kept for provenance;
> do not implement from this document. The groove dialect described here
> (port 9000, `groove_version` probing, capabilities-as-shown) predates the
> canonical protocol, and the five-mode "transmutation spectrum" was the
> precursor intuition of what is now the cleave dial. Current canon:
> the joinery naming ADR (groove `docs/decisions/0009`), groove
> `spec/SPEC.adoc` (v0.3: leases §4.6, signed manifests §2.1.5),
> `cleave/docs/KERNEL.adoc` + `RANKED-OWNERSHIP-CLEAVE.adoc` v0.3 (the
> dial, soft/hard as lease modes, posture TS-1..7), and
> `cleave/docs/architecture/THE-JOINERY.adoc` (orientation).


**Date:** 2024-04-14
**Researcher:** Mistral Vibe
**Purpose:** Understand Groove Protocol and PanLL panel/plugin development for eNSAID integration
Expand Down
12 changes: 12 additions & 0 deletions TRANSMUTABLE_PANEL_ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Transmutable Panel Architecture for Discipline Analyzers

> **HISTORICAL (2024, Mistral-Vibe era) — SUPERSEDED.** Kept for provenance;
> do not implement from this document. The groove dialect described here
> (port 9000, `groove_version` probing, capabilities-as-shown) predates the
> canonical protocol, and the five-mode "transmutation spectrum" was the
> precursor intuition of what is now the cleave dial. Current canon:
> the joinery naming ADR (groove `docs/decisions/0009`), groove
> `spec/SPEC.adoc` (v0.3: leases §4.6, signed manifests §2.1.5),
> `cleave/docs/KERNEL.adoc` + `RANKED-OWNERSHIP-CLEAVE.adoc` v0.3 (the
> dial, soft/hard as lease modes, posture TS-1..7), and
> `cleave/docs/architecture/THE-JOINERY.adoc` (orientation).


**Date:** 2024-04-14
**Version:** 1.0
**Status:** Design Phase
Expand Down
6 changes: 5 additions & 1 deletion src-gossamer/STATUS.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ source of truth going forward is `panel-clades/` (Idris2 ABI + Zig FFI +
a2ml clades) — see `docs/decisions/ADR-0001-coprocessor-and-panel-clades-pivot.adoc`.

Permitted changes: security fixes and build-keep-alive only (interim
legacy-stabilisation, e.g. PR #37). All new panel/coprocessor work
legacy-stabilisation, e.g. PR #37). *Wire-dialect keep-alive is included*
(owner decision 2026-07-09, recorded after a PMI review): constants and
manifest-string corrections needed to stay conformant with the current
groove SPEC/registry — never features — may land here, so this question
does not need re-litigating at every dialect bump. All new panel/coprocessor work
happens in `panel-clades/`, modelled on Axiom.jl's standard
backend-organisation approach. The ~38 panel dirs here are
port-reference until clade parity, then removed.
15 changes: 9 additions & 6 deletions src-gossamer/src/groove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,12 @@ use std::time::{SystemTime, UNIX_EPOCH};

use once_cell::sync::Lazy;

/// Known groove peers for health mesh probing.
/// Known groove peers for health mesh probing — groove-DISCOVERY surfaces
/// per groove-registry.json (ADR 0006; two-plane rule: app ports differ).
/// groove-ref 6465, gossamer 6470, burble 6473, verisimdb 6475, vext 6480,
/// echidna 9000, hypatia 9090.
/// PanLL probes these ports to discover neighbouring services.
const MESH_PROBE_PORTS: &[u16] = &[6473, 8080, 8081, 8091, 8092, 8093];
const MESH_PROBE_PORTS: &[u16] = &[6465, 6470, 6473, 6475, 6480, 9000, 9090];

/// Cached mesh health state: peer service_id -> status.
static MESH_STATE: Lazy<Mutex<Vec<PeerHealth>>> = Lazy::new(|| Mutex::new(Vec::new()));
Expand All @@ -66,6 +69,7 @@ const MANIFEST: &str = r#"{
"groove_version": "1",
"service_id": "panll",
"service_version": "0.1.0",
"mode": "active",
"capabilities": {
"panel_ui": {
"type": "panel-ui",
Expand All @@ -76,7 +80,7 @@ const MANIFEST: &str = r#"{
"panel_compatible": true
},
"feedback": {
"type": "feedback",
"type": "dogfood-feedback",
"description": "Groove-routed feedback collector — accepts and routes feedback to any mesh service",
"protocol": "http",
"endpoint": "/.well-known/groove/feedback",
Expand Down Expand Up @@ -107,9 +111,8 @@ const MAX_REQUEST_SIZE: usize = 16 * 1024;
/// Spawn the groove discovery HTTP server on port 8000 in a background thread.
///
/// This is a minimal blocking HTTP server that handles only the groove protocol
/// endpoints. It runs alongside the main Tauri application, spawned from
/// the `.setup()` hook on a dedicated OS thread (Tauri manages its own
/// async runtime, so we use std::net to avoid tokio dependency conflicts).
/// endpoints. It runs alongside the main Gossamer application on a dedicated OS
/// thread (std::net, no async-runtime dependency).
///
/// The Tauri app is the real PanLL — this server exists solely for groove
/// discovery by other services in the mesh.
Expand Down
211 changes: 211 additions & 0 deletions src/ui/tea/groove_discovery.affine
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
//
// Groove Discovery Panel — AffineScript TEA Interface (groove SPEC §8.1)
//
// Purpose: the discovery UI the groove spec asks PanLL to provide —
// currently discovered groove systems, active connections with their lease
// modes, mesh topology, and heartbeat health — driven through the Gossamer
// runtime bridge (`groove_discover` / `groove_status` / `groove_attest`
// invoke commands; the same surface groove/harness/groove-harness.js
// exercises in the browser).
//
// Nature: AffineScript TEA where Model and Msg are consumed linearly, in
// the idiom of panll_gui.affine. AUTHORED-CANONICAL, AWAITING TOOLCHAIN:
// like the other .affine modules in this tree, this module activates when
// PanLL's AffineScript build path lands (or panel-clades, whichever first)
// — the ReScript TEA chain deliberately gains no new panel wiring (estate
// ruling 2026-07-09: new panels are AffineScript, not ReScript). The
// runnable form of this panel today is groove/harness/
// groove-panel-template/panel.html against a live provider.

// ── Domain ──────────────────────────────────────────────────────────────────

/**
* Lease mode on a connection (groove SPEC §4.6): the wire-observable
* shadow of cleave RC-6 — Soft expires to a zero-residue wipe, Hard
* refreshes on heartbeat. NoLease = legacy §4.3 connection.
*/
enum LeaseMode {
Soft,
Hard,
NoLease
}

/**
* Heartbeat health of a peer, as the mesh sees it.
*/
enum PeerHealth {
Up,
Degraded,
Down
}

/**
* One discovered groove system (a row of the discovery table).
*/
struct Peer {
service_id: String,
port: Int,
health: PeerHealth,
capability_count: Int
}

/**
* One active connection (a row of the connections table).
*/
struct Connection {
handle: String,
peer: String,
lease: LeaseMode,
// Milliseconds until the lease is due absent refresh; 0 for NoLease.
ttl_remaining_ms: Int
}

// ── Model ───────────────────────────────────────────────────────────────────

enum Tab {
Discovered,
Connections,
Mesh,
Attestations
}

/**
* Groove Discovery Model. Lists are owned linearly by the model; every
* refresh consumes the old lists (no stale rows can survive a transition).
*/
struct Model {
tab: Tab,
peers: List<Peer>,
connections: List<Connection>,
// Newest-last attestation event names, as served by /attestations.
attestation_events: List<String>,
probing: Bool
}

// ── Msg ──────────────────────────────────────────────────────────────────────

enum Msg {
SelectTab(Tab),
// groove_discover completed with the peers found.
Discovered(List<Peer>),
// groove_status refresh for the live connections.
ConnectionsRefreshed(List<Connection>),
// Attestation chain fetched (event names, newest-last).
AttestationsFetched(List<String>),
// A heartbeat cycle marked a peer degraded/up/down.
PeerHealthChanged(String, PeerHealth),
StartProbe,
ProbeFinished
}

// ── TEA Logic ────────────────────────────────────────────────────────────────

/**
* Initial state: nothing discovered yet, Discovered tab focused.
*/
fn groove_discovery_init() -> Model {
#{
tab: Tab::Discovered,
peers: [],
connections: [],
attestation_events: [],
probing: false
}
}

/**
* Affine TEA update. Every arm consumes (msg, model) exactly once and
* yields the successor model — refreshes REPLACE the owned lists, so a
* dropped peer cannot linger in the view (the UI-level echo of the
* zero-residue discipline).
*/
fn groove_discovery_update(@linear msg: Msg, @linear model: Model) -> Model {
match model {
{ tab: t, peers: ps, connections: cs, attestation_events: evs, probing: pr } => {
match msg {
SelectTab(new_tab) => {
#{ tab: new_tab, peers: ps, connections: cs, attestation_events: evs, probing: pr }
}

Discovered(found) => {
// Old peer list is consumed here; only the fresh probe survives.
#{ tab: t, peers: found, connections: cs, attestation_events: evs, probing: false }
}

ConnectionsRefreshed(fresh) => {
#{ tab: t, peers: ps, connections: fresh, attestation_events: evs, probing: pr }
}

AttestationsFetched(chain) => {
#{ tab: t, peers: ps, connections: cs, attestation_events: chain, probing: pr }
}

PeerHealthChanged(id, health) => {
#{
tab: t,
peers: update_peer_health(ps, id, health),
connections: cs,
attestation_events: evs,
probing: pr
}
}

StartProbe => {
#{ tab: t, peers: ps, connections: cs, attestation_events: evs, probing: true }
}

ProbeFinished => {
#{ tab: t, peers: ps, connections: cs, attestation_events: evs, probing: false }
}
}
}
}
}

/**
* Replace one peer's health, consuming the list.
*/
fn update_peer_health(@linear peers: List<Peer>, id: String, health: PeerHealth) -> List<Peer> {
match peers {
[] => [],
[p, ...rest] => {
match p {
{ service_id: sid, port: pt, health: old_health, capability_count: cc } => {
if sid == id {
[#{ service_id: sid, port: pt, health: health, capability_count: cc },
...update_peer_health(rest, id, health)]
} else {
[#{ service_id: sid, port: pt, health: old_health, capability_count: cc },
...update_peer_health(rest, id, health)]
}
}
}
}
}
}

/**
* View: header line per tab (the panel body renders the owned lists).
*/
fn groove_discovery_view(model: Model) -> String {
match model.tab {
Discovered => "GROOVE: DISCOVERED SYSTEMS (probe band + registry surfaces)",
Connections => "GROOVE: ACTIVE CONNECTIONS (soft expires / hard refreshes — SPEC §4.6)",
Mesh => "GROOVE: MESH TOPOLOGY (who feeds whom)",
Attestations => "GROOVE: ATTESTATION CHAIN (hash-linked, newest-last)"
}
}

/**
* Subscriptions: the heartbeat/probe interval drives Discovered /
* PeerHealthChanged messages via the runtime bridge.
*/
fn groove_discovery_subs(model: Model) -> String {
if model.probing {
"interval: groove_discover (500ms probe window)"
} else {
"interval: groove_status heartbeat (5000ms, SPEC §4.3)"
}
}
Loading