Skip to content

feat(web): Observatory skeleton + read-only API (Day 15–16, M5 PR 1 of 3)#34

Merged
furkankoykiran merged 4 commits intomainfrom
day-15-16-observatory-skeleton
Apr 24, 2026
Merged

feat(web): Observatory skeleton + read-only API (Day 15–16, M5 PR 1 of 3)#34
furkankoykiran merged 4 commits intomainfrom
day-15-16-observatory-skeleton

Conversation

@furkankoykiran
Copy link
Copy Markdown
Owner

Context

Day 15–16 of 33. First PR of M5 (Observatory + MCP + pitch), inserted after a strategic replan at end of Day 14 that moved the old Day 15–16 scope (fee skim + payout graph hook) to M6 (Days 21–22). Rationale: competitor SwarmHaul has a live devnet observatory + public MCP endpoint + pitch page live on the web; Quorum has been CLI + jsonl only, so judges can't see any of the technical depth unless they git clone && uv sync. This block closes that visibility gap.

One PR for the two-day block (pivot exception to the one-PR-per-day rule — backend + frontend ship nothing demo-able if split).

What landed

  • apps/api/ — read-only FastAPI adapter over the runner's jsonl artefacts. No writes, no mutation endpoints, no on-chain code. Endpoints:
    • GET /healthz, /stats, /runner/metrics
    • GET /debates/recent, /debates/{debate_id}
    • GET /shapley/leaderboard, /shapley/history
    • GET /payout/latest (null until the Day 22 graph hook)
    • WS /live/debates — 2s poll + 10s heartbeat
  • apps/orchestrator/cli.py — new cli api subcommand that lazy-imports uvicorn, so cli run (the systemd runner) stays free of FastAPI imports.
  • apps/web/ — Vite + React 19 + Tailwind 3.4 + TanStack Query SPA. Standalone pnpm package (own lockfile), so the existing typecheck solana-agent CI lane is untouched. Four routes:
    • / Observatory — hero stats + live agent-reasoning stdout + recent table
    • /debates, /debates/:id
    • /shapley — leaderboard + hand-rolled SVG trend (no chart lib)
    • /payout — placeholder for M6
    • Plus a live WebSocket hook with exponential-backoff reconnect and a ProtocolTelemetry footer (23 tests · devnet/fork · RFB-04/05/02 · git sha).
  • 3 new offline tests (20 → 23): readers skip malformed jsonl lines gracefully; shapley history respects k; /stats endpoint shape round-trips through StatsResponse.
  • CI: third job web typecheck + build, caching apps/web/pnpm-lock.yaml.

Deferred to M6 (Days 21–22)

  • Fee skim on Jupiter cleanup ix + fee-vault index (old Day 15).
  • _make_payout_node graph hook + payout_dry_run state field (old Day 16).
  • Fork-live payout broadcast demo (new M6 scope).

/payout/latest deliberately returns {payout: null, message: "payout node lands Day 22"} until the graph hook lands.

Verification

  • uv run pytest tests/test_offline.py -q23 passed
  • uvx ruff check . && uvx ruff format --check . → clean
  • cd packages/solana-agent && pnpm typecheck → clean
  • cd apps/web && pnpm typecheck && pnpm build → clean (84 KB gzipped JS)
  • Manual: uv run python -m apps.orchestrator.cli api --port 8081curl /stats returns real runner numbers (total=2, debates_count=58, shapley_rows=27 at time of smoke)
  • Manual: cd apps/web && pnpm dev → 5173 proxies /api and /live to the API cleanly; all 4 routes render without console errors
  • Manual: WS client receives the current latest debate on connect + a {type: "ping"} within 10s

Next PR

Days 17–18: apps/mcp-server/ — public MCP endpoint (TS, @modelcontextprotocol/sdk, 8 tools) so Claude Desktop and other agents can query Quorum debates + submit symbols.

Scope guardrails respected

No on-chain code · No new specialists · No writes to jsonl · No deploys · QUORUM_LIVE and QUORUM_PAYOUT_LIVE untouched · Ports 8000 (Divimero) and 8080 (Freqtrade) not touched.

Introduces apps/api/ — a read-only adapter over the runner's jsonl
artefacts (debate_log.jsonl, shapley_history.jsonl, runner_metrics.json).
Exposes:

  GET  /healthz
  GET  /stats
  GET  /runner/metrics
  GET  /debates/recent
  GET  /debates/{debate_id}
  GET  /shapley/leaderboard
  GET  /shapley/history
  GET  /payout/latest      (null until the Day 22 graph hook)
  WS   /live/debates       (2s poll + 10s heartbeat)

The app is a separate entrypoint: adds a `cli api` subcommand that
imports uvicorn lazily, so `cli run` (the systemd runner) stays free
of FastAPI imports. No writes, no mutation endpoints, no on-chain code.

Adds fastapi, uvicorn[standard], websockets to dependencies and httpx
to the dev group for the FastAPI TestClient.
Three new offline tests under tests/test_offline.py, taking the suite
from 20 to 23:

  test_readers_debate_log_limit          — malformed line skipped, limit respected
  test_readers_shapley_history_respects_k — k + keys contract
  test_stats_endpoint_shape              — FastAPI TestClient round-trip

All fixtures monkeypatch apps.api.paths.DATA_DIR onto tmp_path, so the
suite stays fully offline and network-free.
First cut of apps/web, a read-only SPA over the Observatory API:

  /           Observatory   hero stats + live agent-reasoning stdout + recent table
  /debates    debate list + filtering (link-through to detail)
  /debates/:id transcript + pyth + jupiter + dry-run + shapley
  /shapley    leaderboard + hand-rolled SVG trend (no chart lib)
  /payout     placeholder until the Day 22 graph hook lands

Built with Vite + React 19 + Tailwind 3.4 + TanStack Query; terminal
monospace aesthetic, single accent colour, feedback button stub.
Live feed via a WebSocket hook with exponential-backoff reconnect.

apps/web is a standalone pnpm package (its own pnpm-lock.yaml) so the
existing `typecheck solana-agent` CI job stays untouched. Vite dev
server on 5173 proxies /api → 127.0.0.1:8081 and /live → ws://…:8081
to match the FastAPI backend in the same repo.
Adds a third CI job that runs `pnpm typecheck` and `pnpm build` in
apps/web on every push / PR. Uses apps/web/pnpm-lock.yaml for the
Node action cache so it stays independent of the solana-agent lane.

Branch protection addition (require "web typecheck + build") happens
after the job has a green run on main — user action, not part of this
PR.
@furkankoykiran furkankoykiran merged commit 47eff6f into main Apr 24, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant