Files
alexei.dolgolyov 6745e25b20 feat: roadmap batch (2026-06-19) — solar/linear-light/dither/nanoleaf + integrations
Eight roadmap features from the 2026-06-19 review, each a full vertical
(backend + tests + frontend + i18n en/ru/zh); ~67 new unit tests:

- automations: SolarRule sunrise/sunset trigger (new utils/solar.py, shared
  with the daylight cycle; window logic mirrors TimeOfDayRule)
- ci: best-effort arm64 multi-arch Docker manifest via QEMU + docker manifest
  (release.yml; amd64 path untouched, continue-on-error)
- game-integration: wire the orphaned LoLPoller via a LoLPollManager + a shared
  runtime_state module (poll lifecycle on enable/CRUD/startup/shutdown)
- ui: color-harmony gradient generator (complementary/analogous/triadic/...)
- effects: audio-reactive palette modulation (new audio_energy_tap; brightness/
  saturation modulation across all 12 procedural effects)
- capture: linear-light blending + spatio-temporal dithering, opt-in per
  calibration (new utils/linear_light.py, utils/dither.py)
- devices: Nanoleaf extControl v2 per-panel UDP streaming (per_panel mode)

Also bundles the pending 2026-06-18 production-review fixes and other
in-progress work already in the working tree (manual-trigger rule, etc.),
since they share files and could not be cleanly separated.

Gate: ruff + tsc clean; pytest 2654 passed / 2 skipped. The single failing
test (automation manual_trigger handler coverage) is a separate in-progress
item owned elsewhere, intentionally left as-is.
2026-06-22 23:21:24 +03:00

11 KiB

Claude Instructions for LedGrab

Priority order: vex (PRIMARY) → ast-index (fallback) → Grep/Glob (last resort). This repo has a fully-featured .vex.toml index. Use vex first for any symbol/definition/usage/call-graph lookup. Fall back to ast-index only when vex legitimately can't help, and to Grep/Glob only for regex patterns, string literals, comments, config files, or unparsed languages.

IMPORTANT — use ALL vex indexing features. The index is built with every capability enabled, and queries must take advantage of them. Keep them ON and exploit them:

Capability Status Powers
Semantic embeddings (jina-code, 768-dim) ON vex search (semantic channel), similar, find_similar, duplicates
Call graph ON vex callers, callees, paths, reachable, bundle --mode pr-impact
BM25 ON hybrid RRF text channel in vex search
Pattern index ON vex pattern AST-shape matching
C++ includes ON include-graph resolution
Body tokens (incremental HNSW) ON fast incremental reindex
History ON vex history, vex diff <rev> blame/evolution queries

In-session, use the mcp__vex__* MCP tools (search, show, usages, callers, callees, bundle, outline, implementations, similar, grep, status, etc.) — MCP output is far cheaper in tokens than Bash("vex …"). Drop to Bash vex only for CLI-only features (pattern, diff, paths, reachable, bundle, history, --strict/--why flags), for subagent prompts, or for shell composition.

vex search "query" --semantic             # Hybrid semantic + BM25 search
vex show <Symbol>                          # Definition body (prefer over Read)
vex usages <Symbol> --strict               # Reference sites (AST-precise on T1 langs)
vex callers <Function>                     # Call sites (function-scoped)
vex callees <Function>                     # Outgoing calls
vex paths --from <A> --to <B>              # Multi-hop call-graph path
vex bundle --mode pr-impact --base master  # Changed symbols + callers + reachable tests
vex pattern '$X async fn returning Response'  # AST-shape (metavariables)
vex diff master                            # Symbol-level branch diff
vex history <Symbol>                        # Commit evolution of a symbol

Maintenance: the index has auto_update = true, so it refreshes on stale queries. After a vex self-update, rerun vex index --history --semantic --embedder jina-code --device cuda so newly-added extractors populate and all features stay enabled. Verify with vex status — every capability line should read yes.

IMPORTANT for subagents: Subagents don't inherit MCP. When spawning Agent subagents (Plan, Explore, general-purpose, etc.), instruct them to use vex via Bash for code search (e.g. include "Use vex search, vex show, vex usages, vex callers via Bash for code search; ast-index is the fallback"). Don't tell them to default to grep/Glob.

Fallback — ast-index (use only when vex is unavailable):

ast-index search "Query"                  # Universal search
ast-index class "ClassName"               # Find class/struct/interface definitions
ast-index usages "SymbolName"             # Find all usage sites
ast-index symbol "FunctionName"           # Find any symbol
ast-index callers "FunctionName"          # Find all call sites
ast-index outline "path/to/File.py"       # Show all symbols in a file
ast-index changed --base master           # Show symbols changed in current branch

Git Commit and Push Policy

NEVER commit or push without explicit user approval. Wait for the user to review changes and explicitly say "commit" or "push". Completing a task, "looks good", or "thanks" do NOT count as approval. See the system-level instructions for the full commit workflow.

Auto-Restart and Rebuild Policy

  • Python code changes (server/src/ excluding static/): Auto-restart the server. See contexts/server-operations.md for the restart procedure.
  • Frontend changes (static/js/, static/css/): Run cd server && npm run build to rebuild the bundle. No server restart needed.

Project Structure

  • /server — Python FastAPI backend (see server/CLAUDE.md)
  • /android — Android TV app (Kotlin shell + embedded Python via Chaquopy)
  • /contexts — Context files for Claude (frontend conventions, graph editor, Chrome tools, server ops, demo mode)

Android Dependency Sync (CRITICAL)

The Android app (android/app/build.gradle.kts) installs the server package with --no-deps and lists Android-compatible dependencies explicitly in the Chaquopy pip {} block. This is because server/pyproject.toml includes desktop-only packages (mss, psutil, sounddevice, etc.) that have no Android wheels.

When adding a new dependency to server/pyproject.toml:

  1. If the package is pure Python or has Chaquopy wheels (check Chaquopy PyPI), also add it to android/app/build.gradle.kts in the pip { install(...) } block
  2. If the package is desktop-only (native C/Rust extension without Android support), do NOT add it to build.gradle.kts — and guard its import with try/except ImportError in Python code
  3. If unsure, check Chaquopy's package index first

Incident context: Chaquopy's pip runs on the build machine (Windows), not on Android. Platform markers like sys_platform != 'linux' evaluate against the BUILD host, not the target device. pip install --exclude does not exist. The only reliable way to exclude packages is to not list them.

Context Files

File When to read
contexts/frontend.md HTML, CSS, JS/TS, i18n, modals, icons, bundling
contexts/graph-editor.md Visual graph editor changes
contexts/server-operations.md Server restart, startup modes, demo mode
contexts/chrome-tools.md Chrome MCP tool usage for testing
contexts/ci-cd.md CI/CD pipelines, release workflow, build scripts
Gitea Python CI/CD Guide Reusable CI/CD patterns: Gitea Actions, cross-build, NSIS, Docker
server/CLAUDE.md Backend architecture, API patterns, common tasks

Documentation Lookup

Use context7 MCP tools for library/framework documentation lookups (FastAPI, OpenCV, Pydantic, yt-dlp, etc.) instead of relying on potentially outdated training data.

Data Migration Policy (CRITICAL)

NEVER rename a storage file path, store key, entity ID prefix, or JSON field name without writing a migration. User data lives in JSON files under data/. If the code starts reading from a new filename while the old file still has user data, THAT DATA IS SILENTLY LOST.

When renaming any storage-related identifier:

  1. Add migration logic in BaseJsonStore.__init__ (or the specific store) that detects the old file/key and migrates data to the new name automatically on startup
  2. Log a clear warning when migration happens so the user knows
  3. Keep the old file as a backup after migration (rename to .migrated or similar)
  4. Test the migration with both old-format and new-format data files
  5. Document the migration in the commit message

This applies to: file paths in StorageConfig, JSON root keys (e.g. picture_targetsoutput_targets), entity ID prefixes (e.g. pt_ot_), and any field renames in dataclass models.

Incident context: A past rename of picture_targets.jsonoutput_targets.json was done without migration. The app created a new empty output_targets.json while the user's 7 targets sat unread in the old file. Data was silently lost.

UI Component Rules (CRITICAL)

NEVER use plain HTML <select> elements. The project uses custom selector components:

  • IconSelect (icon grid) — for predefined items (effect types, palettes, easing modes, animation types)
  • EntitySelect (entity picker) — for entity references (sources, templates, devices)

Plain HTML selects break the visual consistency of the UI.

Pre-Commit Checks (MANDATORY)

Before every commit, run the relevant checks and fix any issues:

  • Python changes: cd server && ruff check src/ tests/ --fix
  • TypeScript changes: cd server && npx tsc --noEmit && npm run build
  • Both: Run both checks
  • Always run tests: cd server && py -3.13 -m pytest tests/ --no-cov -q — all tests MUST pass before committing. Do NOT commit code that fails tests.

Do NOT commit code that fails linting or tests. Fix the issues first.

General Guidelines

  • Always test changes before marking as complete
  • Follow existing code style and patterns
  • Update documentation when changing behavior
  • Never make commits or pushes without explicit user approval

MCP Tools: code-review-graph

IMPORTANT: This project has a knowledge graph. ALWAYS use the code-review-graph MCP tools BEFORE using Grep/Glob/Read to explore the codebase. The graph is faster, cheaper (fewer tokens), and gives you structural context (callers, dependents, test coverage) that file scanning cannot.

When to use graph tools FIRST

  • Exploring code: semantic_search_nodes or query_graph instead of Grep
  • Understanding impact: get_impact_radius instead of manually tracing imports
  • Code review: detect_changes + get_review_context instead of reading entire files
  • Finding relationships: query_graph with callers_of/callees_of/imports_of/tests_for
  • Architecture questions: get_architecture_overview + list_communities

Fall back to Grep/Glob/Read only when the graph doesn't cover what you need.

Key Tools

Tool Use when
detect_changes Reviewing code changes — gives risk-scored analysis
get_review_context Need source snippets for review — token-efficient
get_impact_radius Understanding blast radius of a change
get_affected_flows Finding which execution paths are impacted
query_graph Tracing callers, callees, imports, tests, dependencies
semantic_search_nodes Finding functions/classes by name or keyword
get_architecture_overview Understanding high-level codebase structure
refactor_tool Planning renames, finding dead code

Workflow

  1. The graph auto-updates on file changes (via hooks).
  2. Use detect_changes for code review.
  3. Use get_affected_flows to understand impact.
  4. Use query_graph pattern="tests_for" to check coverage.