# Nebula Nodes — Session Handoff

**Date:** 2026-04-17
**For:** next Claude Code session starting fresh
**Goal:** get you oriented fast so you can pick up productive work immediately

---

## TL;DR

Open-source node-based AI media generation app. Python FastAPI backend on `:8000`, React frontend on `:5173`. Graph canvas via React Flow. Chat panel in the frontend wraps the user's local `claude -p` CLI as a persistent session — the user talks to Claude Code through the app and Claude builds node graphs on the canvas via a `nebula` CLI + skills.

Most recent work: built skills for Gemini, Meshy, and FAL (160 models). Ran an API-reference gap audit for FAL-backed nodes. Long-term plan is a proprietary SwiftUI Mac + iPad app on top of this OSS engine.

---

## Current state — what works

**Backend (Python FastAPI at `:8000`):**
- Execution engine for graphs (sync + streaming)
- Handlers for: Anthropic, OpenAI (chat, image, audio), ElevenLabs, Meshy, Runway, Google Gemini, Veo, Grok, Higgsfield, MiniMax, Replicate (universal), FAL (universal passthrough)
- CLI `nebula` (`backend/cli/`) with `create`, `connect`, `run-all`, `graph`, `import`, quick, etc. Used by Claude when building graphs through the chat panel.
- WebSocket: `/ws` for execution events, `/ws/chat` for chat panel claude-session bridge
- Chat WebSocket spawns `claude -p --dangerously-skip-permissions --output-format stream-json --verbose --model <x> --append-system-prompt <primer> [--resume <sid>]` per message. Subprocess stdout buffer bumped to 64MB (was 64KB) to survive large tool results.

**Frontend (React + Vite at `:5173`):**
- React Flow canvas with drag/drop, multi-select, undo/redo, clipboard, execution state, streaming previews
- Chat panel — draggable (header), resizable (left/bottom/corner handles), persists width/height/position via uiStore, switches top-right anchored ↔ top-left anchored on first drag
- Node library — 93 node types, collapsed by default, search auto-expands matches
- Dropping an image on canvas uploads via `/api/upload` then POSTs to `/api/graph/node` — lands in cli_graph so Claude's `nebula graph` can see it
- Graph save/load — `.nebula` format v2 now persists outputs + state so generated images reopen intact
- Drag a node's id chip into the chat textarea to insert `@n1` reference

**Skills installed at `.claude/skills/`:**
- `gemini/` — Gemini text, Imagen, Nano Banana, Veo (full multi-file)
- `meshy/` — Meshy 3D API (full multi-file with 586+ animation library)
- `fal/` — FAL.ai bundle: 160 per-model skills + 16 category files + SKILL.md router with node→endpoint map

Auto-memory at `~/.claude/projects/-Users-justinperea-Documents-Projects-nebula-nodes/memory/MEMORY.md`.

---

## Important working rules

### Verify Before Reporting (critical — added today to `~/.claude/CLAUDE.md`)

For factual claims about libraries, APIs, products, versions, pricing, or behavior: **fetch the canonical primary source**. WebSearch is for discovering URLs; WebFetch is for extracting facts. Never synthesize confident answers from search snippets, dev.to posts, or SEO aggregators — they mix stale and hallucinated content.

The user caught me skipping this step once today on Anthropic's Agent SDK / Managed Agents research — I reported details from search snippets that turned out wrong. The rule was codified into `~/.claude/CLAUDE.md` as a new section with a dated reference to that miss.

### Research Protocol (`~/.claude/rules/research-protocol.md`)

Before researching any topic, read `~/Documents/Obsidian Vault/Research/_hot.md` → `_index.md` → topic note. After research, write back to all four files. Freshness: fast-moving = always refresh, moderate = 30 days, stable = 90 days.

### Activity Logging

After meaningful tasks, append to `~/.claude/activity/YYYY-MM-DD.log` using `bash ~/.claude/scripts/activity-log.sh write '...'`. Never use raw heredocs — they trigger permission prompts. Format in `~/.claude/rules/activity-logging.md`.

### Dual-Param Architecture (project-specific)

Nodes with both FAL and direct routes use `sharedParams` + `falParams` + `directParams` in `node_definitions.json`. **Never merge these** — they're consumed separately by the two code paths. Memory file at `feedback_dual_params.md`.

### The nebula chat panel workflow

The chat backend spawns `claude -p` with `cwd=PROJECT_ROOT`, env `NEBULA_DISABLE_QUICK=1`, and `--append-system-prompt` set to `NEBULA_SYSTEM_PRIMER` in `backend/services/chat_session.py`. Key primer rules:
- Always run `nebula graph` FIRST before creating nodes
- Use graph mode (`nebula create` / `connect` / `run-all`) — **never** `nebula quick` (hard-disabled by env var)
- Default to branching, not replacement — only delete when user explicitly says "replace/remove/clear/start over"
- Reuse existing input nodes rather than duplicating

---

## Recent work (newest first) — so you know why things are the way they are

### 2026-04-17
- **FAL skill integration.** Copied user's vault bundle (`~/Documents/Obsidian Vault/raw/api-docs/fal/`) into `.claude/skills/fal/` — 160 skills, 16 categories. Wrote `SKILL.md` entry point with node→endpoint routing.
- **FAL node gap report** at `.planning/fal-node-gap-report.md`. Diffed every FAL-backed nebula node against its skill. Two bugs surfaced (veo-3 aspectRatio casing, wan-2-6-i2v/r2v spurious `fal-ai/` prefix). Recommended triage order. **Nothing applied yet — awaiting user direction on which batch to start.**
- **Meshy skill.** Built at `.claude/skills/meshy/` — SKILL.md + 3d-generation.md + post-processing.md + 2d-generation.md + reference/ (endpoints, credits, rate-limits, animation-library 586+ actions, handler-gaps). Every param verified against docs.meshy.ai.
- **Verify-Before-Reporting rule.** Added to `~/.claude/CLAUDE.md` after I reported Anthropic SDK/Managed Agents details from search snippets that turned out partially wrong.
- **Chat panel resize bug fix.** In dragged (top-left anchored) mode, left-edge resize now moves both width AND position so the handle stays under the cursor. Was previously reversed-feeling.
- **Chat panel drag.** Added draggable-by-header behavior. `uiStore.panels.chat` gained optional `left`/`top` coords. Clamps keep ≥40px in viewport.
- **Chat freeze fix.** Asyncio subprocess stdout buffer bumped 64KB → 64MB + `LimitOverrunError` drain fallback. User had hit "Separator is found, but chunk is longer than limit" on large Read/Bash tool results. UI now marks dangling tools as failed on stream end/error.
- **Chat resize.** Added bottom-edge + corner drag handles for height. Store clamps 240–2000px height, 260–720px width.

### 2026-04-16
- **Gemini skill** at `.claude/skills/gemini/` — SKILL.md + imagen.md + nano-banana.md + veo.md + gemini-text.md + reference/model-ids.md + reference/official-docs.md. All sourced from ai.google.dev.
- **Chat panel iteration 2** — Sonnet 4.6 default (was Opus 4.7), auto-fit on graphSync, no-delete-by-default primer rule, node-id chip draggable into chat textarea.
- **Image drop fix** — image drops now go through `/api/graph/node` (cli_graph) instead of creating frontend-only UUID nodes. `export_graph_for_frontend` derives `_previewUrl` from `filePath` under OUTPUT_ROOT.

### 2026-04-15 (initial chat panel work)
- Chat panel component + `/ws/chat` WebSocket
- Backend `chat_session.py` that spawns `claude -p` per message
- Universal primer with nebula-aware rules
- `NEBULA_DISABLE_QUICK=1` env gate on the bundled `nebula quick` CLI so Claude can't accidentally use it

---

## Pending items (ordered by ROI)

### Tier 1 — two small bugs from FAL audit (minutes)
1. Drop `aspectRatio` (camelCase) from `veo-3` node definition. Keep `aspect_ratio`.
2. In `backend/execution/sync_runner.py`: remove spurious `fal-ai/` prefix from `wan-2-6-i2v` and `wan-2-6-r2v` endpoint routes. Should match `wan-2-6-t2v` which correctly uses `wan/v2.6/text-to-video` (no prefix).

### Tier 2 — high-value FAL node param additions
Full list in `.planning/fal-node-gap-report.md`. Highlights:
- Meshy FAL nodes: expose `enable_rigging`, `enable_animation`, `animation_action_id`, `rigging_height_meters`, `texture_image_url`
- Sora-2: expose `model` so user can pick sora-2-pro
- Flux Schnell: expose `guidance_scale`, `num_inference_steps`, `image_size`, `output_format`
- Kling-v3: expose `multi_prompt`, `shot_type`

### Tier 3 — next provider skill
Runway is the largest remaining provider surface. 7 nodes (Gen-4, Act-Two, Aleph, image, TTS, STS, Dubbing). Same template as Gemini/Meshy/FAL skills.

### Tier 4 — backlog items parked in `.planning/backlog/chat-panel-followups.md`
- Helper library (color palettes, prompt templates) — design spike needed
- Drag-to-chat v2 (UUID resolution + vision attachments)
- Auto-layout research (dagre vs smarter heuristic)

### Tier 5 — longer horizon
- **Visual zones/frames** on canvas — user wants semi-transparent colored regions for grouping nodes into workflows. Open questions locked in `.planning/backlog/chat-panel-followups.md`. Needs new `zone` node type, NodeResizer, cli_graph zone primitives, primer awareness.
- **Mac + iPad app** — spec at `.planning/private/mac-app-spec.md` (gitignored). Decision: SwiftUI-native, proprietary, commercial. Uses Agent SDK (not claude CLI subprocess) so users bring their own Anthropic API key. iPad connects to Mac over Bonjour/Tailscale. v1 is WKWebView hosting the React canvas; v3 is native Metal+SwiftUI canvas (3–6 months). Open decisions: repo layout (separate private repo recommended), pricing model, bundled-CLI naming to avoid conflict with OSS install. Don't start this until OSS hits a shippable v1.

---

## Open decisions awaiting user input

1. **FAL gap report triage** — which tier to execute first. User has the report, hasn't said "apply."
2. **Mac app v1 start signal** — not yet; gated on OSS v1 maturity and 10–20 real sessions of dogfooding.
3. **Visual zones** — four open questions in the spec (overlap, drag-contents, colors, backend-or-frontend-only).
4. **Repo split for Mac app** — single repo with gitignored folder vs separate private repo with OSS submodule.
5. **Runway next provider skill** — user hasn't picked yet.

---

## Known issues / quirks

- **Backend needs manual restart for primer/handler changes.** The user's `uvicorn main:app --port 8000` runs without `--reload`. Every change to `chat_session.py`, `main.py`, or handlers requires `lsof -ti:8000 | xargs kill; python -m uvicorn main:app --port 8000`. Frontend HMR handles React/CSS changes automatically.
- **Stale output 404s are harmless.** Old `.nebula` files reference `/api/outputs/...` URLs that got cleaned up. Just ghost 404s on load, no functional impact.
- **4× `/api/settings` fetch on page load** — StrictMode double-invoke + multiple components. Minor, not urgent.
- **User's claude CLI auth is claude.ai OAuth, not API key.** Claude Agent SDK requires API key — our chat panel spawns the CLI which respects the OAuth session, so subscription flows work there. SDK-based Mac app will require API key (Anthropic prohibits third-party apps from using user's claude.ai subscription).

---

## Key file locations

### Backend
- `backend/main.py` — FastAPI app, WebSockets, REST
- `backend/services/chat_session.py` — the `claude -p` subprocess wrapper + stream parser + NEBULA_SYSTEM_PRIMER
- `backend/services/cli_graph.py` — the cli_graph state Claude builds against
- `backend/execution/sync_runner.py` — handler registry + FAL endpoint routing
- `backend/handlers/` — one handler per provider
- `backend/cli/` — nebula CLI
- `backend/data/node_definitions.json` — all node schemas (93 nodes)

### Frontend
- `frontend/src/components/panels/ChatPanel.tsx` — chat UI, draggable, resizable
- `frontend/src/components/panels/NodeLibrary.tsx` — node palette, collapsed-by-default
- `frontend/src/components/Canvas.tsx` — React Flow canvas + keyboard shortcuts + auto-fit
- `frontend/src/store/uiStore.ts` — panel state (positions, sizes, collapsed categories)
- `frontend/src/store/graphStore.ts` — nodes/edges + graphSync merge logic
- `frontend/src/lib/graphFile.ts` — `.nebula` save/load v2

### Skills (project)
- `.claude/skills/gemini/` — Gemini family
- `.claude/skills/meshy/` — Meshy direct API
- `.claude/skills/fal/` — FAL 160-model bundle + SKILL.md router

### Planning
- `.planning/fal-node-gap-report.md` — FAL node diff (untouched changes)
- `.planning/backlog/chat-panel-followups.md` — parked ideas
- `.planning/private/mac-app-spec.md` — proprietary Mac app spec (gitignored)

### Vault references
- `~/Documents/Obsidian Vault/raw/api-docs/fal/` — source FAL bundle (160 skills + sync script)
- `~/Documents/Obsidian Vault/Research/topics/` — researched topics with freshness dates
- `~/Documents/Obsidian Vault/Research/_hot.md` — last-7-days cache
- `~/.claude/skills/nebula/SKILL.md` — global nebula skill (project-agnostic)

### Memory
- `~/.claude/projects/-Users-justinperea-Documents-Projects-nebula-nodes/memory/MEMORY.md` — session-persistent memory index
- `~/.claude/CLAUDE.md` — global rules (Activity Logging, Research Protocol, Verify Before Reporting, Keep Planning Docs in Sync, etc.)

---

## Recommended first action

Start by **reading this handoff + the latest two activity log entries** (`~/.claude/activity/2026-04-17.log`) so you see what I just shipped. Then either:

- **Execute Tier 1 of the FAL gap report** (2 bug fixes, minutes of work) to build trust and demonstrate the loop works.
- **OR ask the user** which tier to take next. Don't bulk-apply the whole report without confirmation — the user explicitly wants to review batches.

---

## Tone / working style with this user

- They're a solo developer, fast-moving, comfortable with terminal + Mac + React + Python. Don't over-explain basic concepts.
- They prefer **concise end-of-turn summaries**. One or two sentences on what changed and what's next. Don't recap the whole session.
- They want you to **verify before reporting**. Fetch canonical docs. Test live when possible. Say "I don't know" rather than synthesizing.
- They use **activity logs as a living record**. Log meaningfully after substantive work.
- They **review diffs before applying bulk changes** — when the surface is more than a couple files, offer a report and wait for green light.
- They **appreciate honest pushback** when something you'd say is wrong. Don't hedge.
- They write in lowercase a lot in short messages. Don't read that as sloppy — it's just their casual register.
