Commit Graph
9 Commits
Author SHA1 Message Date
Omar SobhandClaude Opus 4.8 1a39457940 feat(web): two-pane "cockpit" redesign with a live board rail
Restructure the workshop flow into the designer's cockpit: a persistent shell
(header + 5-step stepper + sticky instrument rail) wrapping the phase routes via
a React-Router layout route, so the rail stays mounted across navigation.

- Design system: IBM Plex Mono + Newsreader; the full cockpit token set (light
  + dark) in index.css; a working light/dark theme toggle (store `theme` +
  useApplyTheme); a `switch` ui primitive.
- Shell: CockpitLayout, Stepper (forward-gated), PanelChrome helpers. Every
  phase page restyled to the editorial panels + the WORKSHOP-FLOW fixes
  (channels-after-bind, domain framing + L1 prefill, L2/L3 prefill at 3/3,
  in-place submission finale). Store gains `tried` + `channels.saidHi` (v4).
- Live rail (CockpitRail): real node heartbeat + agent activity log + ADD
  progress; sim telemetry (useTelemetry) for the waveform/accel/I2C behind a
  seam, marked SIM.
- LED-matrix PIXEL MIRROR (real): the rail shows exactly what the physical
  matrix displays — API GET /nodes/:team/matrix reads the board's framebuffer
  off the :9999 relay (readMatrixFrame + the `matrixget` relay command);
  useMatrixMirror polls it and unpacks the 104 bits.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-23 07:38:14 -07:00
Omar SobhandClaude Opus 4.8 c12f40837b fix(state): persist to localStorage + explicit Disconnect (survive reload)
The Zustand store persisted to sessionStorage, which is tab-volatile — a
hard reload / new tab / closing the tab dropped the whole session,
including the board binding (device.connected, nodeUrl, teamId). The
workshop runs on the team's own laptop, so switch the store (and the
offline outbox) to localStorage: the binding + progress now survive a
refresh, hard reload, navigation, and tab close.

Clearing is now explicit only: a new `disconnect()` action drops the board
binding (keeping team/domain/phases/ADD so they can re-bind and resume),
surfaced as a "Disconnect" button on the claimed-board card. `reset()`
still wipes everything.

Tests: add an in-memory localStorage to setupTests (Node 22's disabled
experimental localStorage shadows jsdom's) + clear it per test.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-22 10:34:55 -07:00
Omar SobhandClaude Opus 4.8 063116507b feat(workshop): local-demo landing + consolidated Phase 1 + live Telegram setup
Reframes the app for the local single-team demo and reworks onboarding.

Landing (local-demo chrome):
- Drop Lecture / staff sign-in / programme / stat tiles / bottom CTA.
- Hero → "an APESS agent on the edge"; CTA → "Start the workshop".
- Presenter keeps redclaw.dev + [email protected] contact.

Phase strip:
- Colour by position off the active phase (earlier=done, current=active,
  later=pending) so a stale/persisted flag can't light up a future phase
  green (fixes "phase 2 green while on phase 1").

Phase 1 (Team registration) — consolidated onboarding:
- After the board connects, reveal Say hi (moved from Phase 2), Extra
  channels (Telegram wizard), and a Voice enable/disable toggle.
- Phase 2 "Meet your node" thins to Open-your-node + Pick-your-domain;
  removed the Lock-it-down card. New session `channels {telegram, voice}`
  (persist v3, non-destructive migration).

Live Telegram apply (browser → APESS API → board ZeroClaw gateway):
- Wizard Finish pushes the bot token to the running node and restarts it:
  PUT /api/config/prop channels.telegram.main.bot_token (gateway
  auto-creates the alias + enc2-encrypts the secret) then POST
  /admin/reload (in-place subsystem reload → the Telegram channel starts).
- api: bridge configureTelegram + POST /nodes/:teamId/telegram; client
  configureTelegram(); wizard shows applying/error and only saves on
  success. Remote /admin/reload needs the board's gateway.allow_remote_admin
  (+ pairing); on a fully-open board the reload step 403s.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-22 00:24:05 -07:00
Omar SobhandClaude Opus 4.8 6d94833bb9 feat(web): reshape workshop from 'tune a harness' to 'design a domain node'
The board is now a Claude-powered ZeroClaw agent with expert skills that teams
talk to directly (its own dashboard / Telegram / voice). The frontend was built
on the older sensor+harness model; this repoints it.

- State/DTO: AddLayers remapped to the 5 design layers (Domain/Skills/Policies/
  Harness/Loops); added session.domain (+ mirrored through TeamSnapshot, sync,
  and the SQLite store with a guarded migration); dropped Harness/Provider/RunMode;
  persist v2 migrate resets stale state.
- Removed the harness/sim/failover machinery (HarnessProviderSelect, HarnessTuner,
  HarnessTomlPreview, ResiliencePanel, TriggerButtons, harness.ts, useSerial,
  LiveFeed/serial classifier) and every sim-vs-live branch.
- Onboarding: EnvSetup rebuilt into 'Meet your node' — Open-your-node hero (new
  reusable OpenYourNode CTA), say-hi-to-your-agent, a free-text DomainPicker
  (domain drives L1-L5), Telegram/voice pointers, and the open->locked lockdown
  policy step. Domain is required to proceed.
- Modules repointed: M1 Domain & events (L1), M2 Skills & Policies (L2/L3, skills
  reference + actuation-gate framing), M3 Harness (reasoning+tiering) & Loops
  (cadence) (L4/L5). BuildFlash reframed as a guided 'ask your node to build X'
  that hands off to the node dashboard.
- Judge rubric -> Domain fit/Skills/Policies/Harness/Loops; Lecture + Landing
  re-storied to the Claude-node + talk-to-your-node narrative.

Verified: web tsc + 181 tests, api tsc + 58 tests, dead-ref sweep clean.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-16 15:33:38 -07:00
Omar SobhandClaude Opus 4.8 af9cae0abc feat(onboarding): surface board LAN URL so teams open their node's ZeroClaw web UI
The board self-registers its gateway URL (http://<lan-ip>:8080) but the API
withheld it from the browser — teams saw only an online boolean. The bearer
token is the only real secret; the URL is safe to expose. Return it in the
/claim response and the node status poll, thread it into device.nodeUrl, and
render an 'Open your node →' deep-link in EnvSetup's connected-device card so a
team can reach its own node's embedded web chat directly on the workshop LAN.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-15 16:26:15 -07:00
Omar SobhandClaude Opus 4.8 2c18e67c1b feat(onboarding): board re-attach — auto-heal reboots, resume lost browsers, instructor release
Fixes a latent bug and hardens claim recovery. Refactors the unclaimed pool
into a BoardRegistry that tracks claimedBy, so a board's binding survives its
own reboot and a team can get back onto its board after an error.

- Auto-heal: a claimed board that reboots re-announces with a fresh IP/token;
  self-register now REFRESHES its node binding instead of dumping it back into
  the unclaimed pool (previously the team's board went stale/offline + the kit
  wrongly reappeared as unclaimed).
- Resume: re-claiming an already-claimed kit with the right code returns the
  board's CANONICAL teamId + team snapshot (not a fresh identity). The client
  adopts it via a new resumeTeam() action, restoring name/members/phases/stats
  so a lost-browser re-claim doesn't clobber synced progress.
- Release: POST /claim/release (admin) frees a kit back to the pool + unbinds
  its node, for mis-claims / reassignment. Wired to a small control in /admin.

API 56 tests green; web 220 green; tsc + eslint clean.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-08 17:16:14 -07:00
Omar SobhandClaude Opus 4.8 f21bbdf31e feat(apess): wire real ZeroClaw nodes + live/sim mode + cloud→local fallback
Turn the workshop shell into a real on-device agent platform: APESS now
proxies each team's Arduino Uno Q (running ZeroClaw) and drives
generate → compile → flash from the UI, with a simulation fallback.

api — node backbone
- nodes.ts: team→node registry, a pure ZeroClaw /api/events → WsEvent
  mapper, per-node live SSE subscription fanned out to the collective hub
  AND per-team participant listeners, and a prompt proxy. Bearer tokens
  stay server-side; list()/broadcasts never expose them.
- routes: POST/GET/DELETE /nodes (register is admin-only), public
  POST /nodes/:teamId/prompt, and a participant-scoped SSE
  GET /nodes/:teamId/events.
- WsEvent gains node:status / node:activity.

front-end
- session store: mode 'live' | 'sim' (default sim); a 'local' provider
  and a harness.fallbackLocal toggle ("cloud first, on-board Qwen if it
  fails") reflected in the harness.toml preview.
- EnvSetup: Simulation / Live board toggle — sim runs a virtual board
  (self-test + Proceed with no hardware), live requires a registered node.
- BuildFlash panel (in Module 2): prompt the board, watch activity stream
  live (SSE) or a simulated generate→flash sequence.
- lib/api: sendPrompt + openTeamActivity (EventSource).

housekeeping
- better-sqlite3 → 12.11 (Node 26 support; the 11.x native build fails on
  Node 26's V8).
- vitest excludes the vendored Uno-QClaw/ tree.

Tests: front-end 172, api 29, typecheck clean, prod build passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-03 05:52:51 -07:00
Omar SobhandClaude Opus 4.8 ca023cdaa5 feat: stable teamId in store + shared collective DTOs (B-prep)
- session store gains a persisted teamId (crypto.randomUUID, preserved
  across reset) used as the team's stable identity for the collective
- src/types.ts: TeamSnapshot/SubmissionDTO/ScoreDTO/LeaderboardRow/WsEvent,
  the contract shared with the api backend

2 new tests; suite 88/88 green.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-16 19:14:09 -07:00
Omar Sobh 3d433b9b1d Initial scaffold: React 19 + Vite + Tailwind + shadcn + Vitest
Scaffolds APESS 2026 workshop platform per PRD §4.1 with strict-TDD
infrastructure and the 1300-LOC ceiling enforced via ESLint.

- React 19.2 + Vite 8 + TypeScript 6 baseline
- Tailwind v3 + shadcn/ui (new-york style) configured (components.json)
- Path alias @/* -> src/*
- Zustand session store mirroring the prototype's shared.js shape
  (team, device, phases, harness, stats, add, submission) with
  sessionStorage persistence
- PhaseStrip component covering the five PRD-mandated phases, wired
  to react-router-dom v7 with active/done/pending data-state surfaces
  for snapshot-friendly testing
- Vitest + React Testing Library + jsdom green: 4/4 tests pass
- ESLint max-lines: 1300 enforced; largest file currently 106 LOC

Next: shadcn primitive install, landing page from prototype index.html,
workshop screens 1-5, /admin instructor dashboard, /judge review.
2026-06-09 18:30:34 -05:00