JuanCS-Dev commited on
Commit
0000d94
·
verified ·
1 Parent(s): 594d12f

feat: O2b risk queue live binding + SW v2

Browse files
bot/tests/scientific/test_phase33_ops_stream_scientific.py CHANGED
@@ -49,6 +49,19 @@ class TestOpsSnapshotReusesStudioAndAddsCoStreamer:
49
  assert routes.build_ops_snapshot(None)["channel"] == "default"
50
 
51
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  class TestOpsStreamRouteWired:
53
  """Prove the SSE endpoint is registered and contractually declared."""
54
 
 
49
  assert routes.build_ops_snapshot(None)["channel"] == "default"
50
 
51
 
52
+ class TestOpsSnapshotBindingContract:
53
+ """O2b: the snapshot sections must match what the panel renderers consume,
54
+ so the SSE can feed the panels (kills load-once) without a shape mismatch."""
55
+
56
+ def test_queue_section_matches_action_queue_endpoint_shape(self):
57
+ # renderActionQueuePayload reads payload.items / payload.summary; the
58
+ # snapshot queue must carry them (it is the /api/action-queue payload).
59
+ queue = routes.build_ops_snapshot("default")["queue"]
60
+ assert "items" in queue, "queue.items missing — binding would render nothing"
61
+ assert "summary" in queue, "queue.summary missing"
62
+ assert queue["ok"] is True
63
+
64
+
65
  class TestOpsStreamRouteWired:
66
  """Prove the SSE endpoint is registered and contractually declared."""
67
 
dashboard/features/action-queue/controller.js CHANGED
@@ -199,6 +199,13 @@ export function createActionQueueController({
199
  bindActionQueueEvents,
200
  refreshActionQueue,
201
  scheduleActionQueuePolling,
 
 
 
 
 
 
 
202
  setSelectedChannel(channelId) {
203
  selectedChannel =
204
  String(channelId || "")
 
199
  bindActionQueueEvents,
200
  refreshActionQueue,
201
  scheduleActionQueuePolling,
202
+ // O2b: render the queue from the live ops snapshot (/api/ops/stream) instead
203
+ // of a fetch — snap.queue is byte-for-byte the /api/action-queue shape.
204
+ renderQueueFromSnapshot(queueSection) {
205
+ if (aqEls && queueSection) {
206
+ renderActionQueuePayload(queueSection, aqEls, decideQueueItem);
207
+ }
208
+ },
209
  setSelectedChannel(channelId) {
210
  selectedChannel =
211
  String(channelId || "")
dashboard/main.js CHANGED
@@ -44,6 +44,9 @@ function initFocusMode() {
44
  let opsMood = { count: 0 };
45
  let opsMascotApi = null;
46
  let opsPresence = null;
 
 
 
47
 
48
  // action type -> transient spark kind on the clicked card.
49
  const SPARK_FOR = {
@@ -98,6 +101,7 @@ function connectOpsStream() {
98
  opsMood = deriveOpsMood(snap);
99
  if (opsMascotApi) opsMascotApi.render();
100
  if (opsPresence) opsPresence.render(snap);
 
101
  document.getElementById("bytePresence")?.classList.add("has-data");
102
  } catch (_e) {
103
  /* ignore a malformed frame; the next one will arrive */
@@ -216,6 +220,19 @@ async function bootstrapDashboard() {
216
  actionQueueController.scheduleActionQueuePolling();
217
  clipsController.startPolling();
218
  hudController.startPolling();
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  }
220
 
221
  bootstrapDashboard().catch((err) =>
 
44
  let opsMood = { count: 0 };
45
  let opsMascotApi = null;
46
  let opsPresence = null;
47
+ // O2b: set once the controllers exist; the SSE feeds the panels through it,
48
+ // killing the load-once stale (the O4 debt). Wired in bootstrapDashboard.
49
+ let opsPanelSync = null;
50
 
51
  // action type -> transient spark kind on the clicked card.
52
  const SPARK_FOR = {
 
101
  opsMood = deriveOpsMood(snap);
102
  if (opsMascotApi) opsMascotApi.render();
103
  if (opsPresence) opsPresence.render(snap);
104
+ if (opsPanelSync) opsPanelSync(snap);
105
  document.getElementById("bytePresence")?.classList.add("has-data");
106
  } catch (_e) {
107
  /* ignore a malformed frame; the next one will arrive */
 
220
  actionQueueController.scheduleActionQueuePolling();
221
  clipsController.startPolling();
222
  hudController.startPolling();
223
+
224
+ // O2b: feed the live ops snapshot into the panels (kills the load-once stale).
225
+ // Incremental + verified: the risk queue first (snap.queue === the
226
+ // /api/action-queue shape, confirmed). More panels follow the same pattern.
227
+ opsPanelSync = (snap) => {
228
+ try {
229
+ if (snap && snap.queue) {
230
+ actionQueueController.renderQueueFromSnapshot(snap.queue);
231
+ }
232
+ } catch (_e) {
233
+ /* a malformed section must never break the others */
234
+ }
235
+ };
236
  }
237
 
238
  bootstrapDashboard().catch((err) =>
dashboard/sw.js CHANGED
@@ -1,7 +1,7 @@
1
  // dashboard/sw.js — Byte Studio service worker (PWA shell + offline).
2
  // Scope "/" (served from /sw.js). Network for the API/SSE; cache-first for the
3
  // static shell so the dashboard opens instantly and works offline.
4
- const CACHE = "byte-studio-v1";
5
  const SHELL = [
6
  "/studio",
7
  "/dashboard/styles/studio.css",
 
1
  // dashboard/sw.js — Byte Studio service worker (PWA shell + offline).
2
  // Scope "/" (served from /sw.js). Network for the API/SSE; cache-first for the
3
  // static shell so the dashboard opens instantly and works offline.
4
+ const CACHE = "byte-studio-v2";
5
  const SHELL = [
6
  "/studio",
7
  "/dashboard/styles/studio.css",
docs/plan/ops-dashboard-redesign.md CHANGED
@@ -229,7 +229,8 @@
229
  - **Implementação:** reusar herói de saúde + sparklines do studio (`health.js`/`sparkline.js`); bento assimétrico; micro-animações nativas transitórias.
230
  - **Critério:**
231
  - **O2a (entregue):** [x] **focus mode** (botão + hotkey `F`, ignora digitação; CSS colapsa pra presença + fila; `focus.js` puro, 11 asserts); [x] **empty-state com propósito** (presence bar fica quieta até o 1º snapshot — `has-data`); [x] 60fps/reduced-motion; [x] suíte verde (node 149/150).
232
- - **O2b:** [x] **View Transitions** na troca de aba (progressive enhancement + reduced-motion); [x] **bento grid 1–2–3** na Operation (CSS `grid-template-areas` escopado: intro full-width · health=hero + autonomy à esquerda · risk queue=ação coluna alta à direita · events=rodapé; responsivo 1-col ≤1024px; **densidade preservada** — render-validado); [ ] **ligar os painéis existentes ao snapshot** (mata o load-once — dívida da O4); [ ] dado faltando/label longo/extremo nos painéis. **Validado ao vivo:** O0→O2a deployados + confirmados na Space real (mascote/foco/pele/`/api/ops/stream` 403-gated).
 
233
  - **Validação ao vivo:** [x] render da ops com botão FOCO + presence bar + layout intacto (zero regressão); [ ] colapso do focus mode + bento (render com estado) — pendente.
234
  - **Meta-reflexão (O2a):**
235
  1. *Claim tentador:* "O2 (wow/bento) pronto". **Verdade:** entregue **focus mode** + polish de empty-state; o **bento** (a manchete da O2) e o **binding dos painéis ao snapshot** **não** — são O2b.
 
229
  - **Implementação:** reusar herói de saúde + sparklines do studio (`health.js`/`sparkline.js`); bento assimétrico; micro-animações nativas transitórias.
230
  - **Critério:**
231
  - **O2a (entregue):** [x] **focus mode** (botão + hotkey `F`, ignora digitação; CSS colapsa pra presença + fila; `focus.js` puro, 11 asserts); [x] **empty-state com propósito** (presence bar fica quieta até o 1º snapshot — `has-data`); [x] 60fps/reduced-motion; [x] suíte verde (node 149/150).
232
+ - **O2b:** [x] **View Transitions** na troca de aba (progressive enhancement + reduced-motion); [x] **bento grid 1–2–3** na Operation (densidade preservada, render-validado, **deployado ao vivo**); [~] **ligar painéis ao snapshot** (mata o load-once — dívida da O4): **risk queue ligada** (`snap.queue → renderQueueFromSnapshot`; `snap.queue` é byte-a-byte o shape do `/api/action-queue` — verificado + **teste de contrato** guarda contra drift); demais painéis seguem o **mesmo padrão incremental verificado**; [ ] dado faltando/label longo/extremo nos painéis. **Validado ao vivo:** O0→bento deployados + confirmados na Space real; mascote **68px ops / 60px studio** (bug do `className` que comia a classe de tamanho — corrigido + teste de regressão).
233
+ - **Meta-reflexão (O2b binding):** *Claim tentador:* "painéis em tempo real". **Verdade:** só a **risk queue** foi ligada (1 de ~5); as demais seguem o padrão mas **não** estão ligadas ainda. *Método:* cada painel exige **verificar shape `snap.<seção>` == response do endpoint** antes de ligar (senão quebra silencioso) — por isso incremental. *Honesto:* sem token, o SSE dá 403 → o binding só rende ao vivo com o admin token (que o operador tem); a lógica é shape-verificada + contrato. *Dono restante:* demais painéis (observability/autonomy/control-plane) — mesmo padrão.
234
  - **Validação ao vivo:** [x] render da ops com botão FOCO + presence bar + layout intacto (zero regressão); [ ] colapso do focus mode + bento (render com estado) — pendente.
235
  - **Meta-reflexão (O2a):**
236
  1. *Claim tentador:* "O2 (wow/bento) pronto". **Verdade:** entregue **focus mode** + polish de empty-state; o **bento** (a manchete da O2) e o **binding dos painéis ao snapshot** **não** — são O2b.