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

173 lines
11 KiB
Markdown

# Claude Instructions for LedGrab
## Code Search
**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.
```bash
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):
```bash
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](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](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](https://chaquo.com/pypi-13.1/)), 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](contexts/frontend.md) | HTML, CSS, JS/TS, i18n, modals, icons, bundling |
| [contexts/graph-editor.md](contexts/graph-editor.md) | Visual graph editor changes |
| [contexts/server-operations.md](contexts/server-operations.md) | Server restart, startup modes, demo mode |
| [contexts/chrome-tools.md](contexts/chrome-tools.md) | Chrome MCP tool usage for testing |
| [contexts/ci-cd.md](contexts/ci-cd.md) | CI/CD pipelines, release workflow, build scripts |
| [Gitea Python CI/CD Guide](https://git.dolgolyov-family.by/alexei.dolgolyov/claude-code-facts/src/branch/main/gitea-python-ci-cd.md) | Reusable CI/CD patterns: Gitea Actions, cross-build, NSIS, Docker |
| [server/CLAUDE.md](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_targets``output_targets`), entity ID prefixes (e.g. `pt_``ot_`), and any field renames in dataclass models.
**Incident context:** A past rename of `picture_targets.json``output_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
<!-- code-review-graph MCP tools -->
## 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.