From a974357acd15b71b9172d565f0cc26dfaea5805f Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 17 Jul 2026 05:26:00 +0000 Subject: [PATCH] =?UTF-8?q?Groove=20SPEC=20=C2=A78.1=20discovery=20panel?= =?UTF-8?q?=20(AffineScript)=20+=20frozen-backend=20dialect=20keep-alive?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Panel: src/ui/tea/groove_discovery.affine — the discovery UI the groove spec asks PanLL to provide (discovered systems, connections with lease modes per SPEC v0.3 §4.6, mesh, hash-linked attestations), authored in the sibling .affine TEA idiom with linear Model/Msg. AUTHORED-CANONICAL, AWAITING TOOLCHAIN like the other 17 .affine modules: the ReScript chain deliberately gains no new panel wiring (estate ruling 2026-07-09: new panels are AffineScript); the runnable form today is the browser harness (groove/harness/groove-panel-template) against a live provider. Frozen-legacy keep-alive (src-gossamer/STATUS.adoc permits security/ build-keep-alive; owner PMI decision 2026-07-09 adds wire-dialect keep-alive, now recorded in STATUS.adoc so this never needs re-litigating): - groove.rs manifest: add "mode":"active" (SPEC §2.1.1); feedback capability retyped to the registered "dogfood-feedback"; health-mesh stays and its type is now REGISTERED in groove-registry.json. - MESH_PROBE_PORTS: [6473, 8080, 8081, 8091-8093] probed a moved service (verisimdb → 6475) and three unmapped ports — the mesh view was wrong. Now the registry's groove-discovery surfaces (two-plane rule: app-plane URLs like VeriSimDB's :8080 API are deliberately unchanged). - Stale Tauri comment corrected to Gossamer. Docs: HISTORICAL banners on GROOVE_PANLL_RESEARCH_SUMMARY.md and TRANSMUTABLE_PANEL_ARCHITECTURE.md (2024 Mistral-Vibe era; superseded by the joinery canon — pointers included; kept for provenance, never deleted). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01ECjSL2nqzn3qJEzA28V4m5 --- GROOVE_PANLL_RESEARCH_SUMMARY.md | 12 ++ TRANSMUTABLE_PANEL_ARCHITECTURE.md | 12 ++ src-gossamer/STATUS.adoc | 6 +- src-gossamer/src/groove.rs | 15 +- src/ui/tea/groove_discovery.affine | 211 +++++++++++++++++++++++++++++ 5 files changed, 249 insertions(+), 7 deletions(-) create mode 100644 src/ui/tea/groove_discovery.affine diff --git a/GROOVE_PANLL_RESEARCH_SUMMARY.md b/GROOVE_PANLL_RESEARCH_SUMMARY.md index 4b0f1380..1fac42ee 100644 --- a/GROOVE_PANLL_RESEARCH_SUMMARY.md +++ b/GROOVE_PANLL_RESEARCH_SUMMARY.md @@ -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 diff --git a/TRANSMUTABLE_PANEL_ARCHITECTURE.md b/TRANSMUTABLE_PANEL_ARCHITECTURE.md index 5ff94433..4a341f6b 100644 --- a/TRANSMUTABLE_PANEL_ARCHITECTURE.md +++ b/TRANSMUTABLE_PANEL_ARCHITECTURE.md @@ -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 diff --git a/src-gossamer/STATUS.adoc b/src-gossamer/STATUS.adoc index 2556c1ea..1c59681a 100644 --- a/src-gossamer/STATUS.adoc +++ b/src-gossamer/STATUS.adoc @@ -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. diff --git a/src-gossamer/src/groove.rs b/src-gossamer/src/groove.rs index 95d5c65f..1c4c184d 100644 --- a/src-gossamer/src/groove.rs +++ b/src-gossamer/src/groove.rs @@ -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>> = Lazy::new(|| Mutex::new(Vec::new())); @@ -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", @@ -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", @@ -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. diff --git a/src/ui/tea/groove_discovery.affine b/src/ui/tea/groove_discovery.affine new file mode 100644 index 00000000..f6d73591 --- /dev/null +++ b/src/ui/tea/groove_discovery.affine @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) +// +// 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, + connections: List, + // Newest-last attestation event names, as served by /attestations. + attestation_events: List, + probing: Bool +} + +// ── Msg ────────────────────────────────────────────────────────────────────── + +enum Msg { + SelectTab(Tab), + // groove_discover completed with the peers found. + Discovered(List), + // groove_status refresh for the live connections. + ConnectionsRefreshed(List), + // Attestation chain fetched (event names, newest-last). + AttestationsFetched(List), + // 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, id: String, health: PeerHealth) -> List { + 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)" + } +}