Imported from potato-os/core (
AGENTS.md). Install upstream withnpx skills add potato-os/core. Copyright stays with the author.
Repository Guidelines
Project Structure & Module Organization
Core application code lives in core/:
core/main.py: FastAPI entrypoint, API routes, orchestrator loop, lifespan.core/model_state.py: Model registry, settings persistence, projector helpers.core/runtime_state.py: Runtime config, dual-runtime slot discovery, system metrics, power calibration.inferno(external package,potato-os/inferno): Inference layer — backend proxy, model family classification, LiteRT adapter, launch config builder, model registry, runtime management, orchestration. Installed viarequirements.txt.core/assets/: Frontend —index.html,shell.css,shell.js(platform shell), vendor libs.core/rig_envelope.py: RIG step envelope validation (MS/TS contract checks).
Process-isolated apps live in apps/:
apps/<id>/app.json: App manifest (identity, process config, lifecycle).apps/<id>/rig.md: RIG workflow contract (steps, flow graph, schemas).apps/<id>/main.py: App entry point (runs as subprocess).apps/chat/: Chat interface — the built-in Potato app (stays in core).- Non-core apps (Permitato, Skeleton template) live in
potato-os/apps. Deploy viaPOTATO_APPS_REPO.
Operational scripts are in bin/:
run.sh: Main entrypoint (systemd calls this).start_llama.sh: Thin wrapper — sets LD_LIBRARY_PATH, execs Python-computed llama-server args.install_dev.sh: Deploys to Pi (idempotent). UsesPOTATO_LLAMA_RUNTIME_FAMILYfor slot selection.build_llama_runtime.sh: Builds ik_llama or upstream llama.cpp on Pi from source.prepare_imager_bundle.sh: Packages SD card image payload.ensure_model.sh: Bootstrap model download helper.publish_runtime.sh: Packages, tags, and publishes a runtime slot to GitHub Releases.publish_ota_release.sh: Packages core/ and bin/ into an OTA tarball and publishes to GitHub Releases. Seedocs/ota-releases.md.lib/runtime_release.sh: Shared helpers for downloading runtimes from GitHub Releases.
Service definitions in systemd/. Nginx config in nginx/. Image build in image/.
Tests are organized under tests/:
tests/api/: HTTP/API behavior tests (pytest).tests/unit/: Python and shell-script logic tests (pytest).tests/ui/: Playwright E2E tests — split into 9 feature-scoped spec files:settings.spec.js,chat.spec.js,image.spec.js,download.spec.jsruntime.spec.js,model-switcher.spec.js,sessions.spec.js,bootstrap.spec.jsupdate.spec.js
tests/ui/helpers.js: Shared test utilities (waitUntilReady,makeStatusPayload, etc.).tests/e2e/: Pi smoke/uninstall/OTA flows over SSH.ota_update_pi.shtests the full OTA cycle (happy path +--test-failurefor rollback).
Build, Test, and Development Commands
Local development
uv sync: Create/sync local dev environment.POTATO_ENABLE_ORCHESTRATOR=0 uv run uvicorn core.main:app --host 0.0.0.0 --port 1983: Run API locally.
Running tests (required before every push)
- Python (unit + API):
uv run python -m pytest tests/unit tests/api -q -n auto - Playwright (UI):
npx playwright test --reporter=dot --timeout=15000 --workers=75% - Both together:
uv run python -m pytest tests/unit tests/api -q -n auto && npx playwright test --reporter=dot --timeout=15000 --workers=75% - First-time Playwright setup:
npx playwright install chromium
Pi deployment
Credentials: export SSHPASS=raspberry. All commands below assume SSHPASS is set.
/opt/potato/ is owned by potato:potato. The pi user needs sudo for rsync. Use SUDO_ASKPASS (not --rsync-path="sudo rsync", which fails because sudo requires a password).
These commands are written for a macOS dev environment. COPYFILE_DISABLE=1 prevents macOS tar/rsync from embedding ._ resource fork files (harmless on Linux). The separate chown step compensates for macOS rsync lacking --chown (Linux rsync 3.1+ supports it natively).
- Fast core deploy:
COPYFILE_DISABLE=1 sshpass -e rsync -az --delete \ --rsync-path="SUDO_ASKPASS=/opt/potato/bin/askpass.sh sudo -A rsync" \ -e "ssh -o StrictHostKeyChecking=accept-new" \ core/ pi@potato.local:/opt/potato/core/ sshpass -e ssh pi@potato.local \ "echo raspberry | sudo -S /opt/potato/venv/bin/pip install -r /opt/potato/core/requirements.txt" - Fast apps deploy:
COPYFILE_DISABLE=1 sshpass -e rsync -az \ --rsync-path="SUDO_ASKPASS=/opt/potato/bin/askpass.sh sudo -A rsync" \ -e "ssh -o StrictHostKeyChecking=accept-new" \ apps/ pi@potato.local:/opt/potato/apps/ - Fix ownership after deploy (macOS rsync lacks
--chown):sshpass -e ssh pi@potato.local "echo raspberry | sudo -S chown -R potato:potato /opt/potato/core /opt/potato/apps" - Full install:
sshpass -e ssh pi@potato.local "cd /tmp/potato-os && sudo ./bin/install_dev.sh" - Restart service:
sshpass -e ssh pi@potato.local "echo raspberry | sudo -S systemctl restart potato"
Image building
./bin/build_local_image.sh --setup-docker: Build Potato OS image from macOS (recommended entry point)../bin/prepare_imager_bundle.sh: Package SD card image payload (patch existing card).- See
docs/building-images.mdfor the full build guide.
Runtime Architecture
Dual-runtime system
Potato OS supports two curated llama runtimes:
- ik_llama (default): IQK-optimized fork, faster on Pi 5.
- llama_cpp: Upstream llama.cpp, standard build.
Runtime slots live at /opt/potato/runtimes/{ik_llama,llama_cpp}/ with runtime.json metadata. The active runtime is rsynced to /opt/potato/llama/. Switch via POST /internal/llama-runtime/switch with {"family": "ik_llama"}.
Bootstrap auto-download
On fresh installs with no model, a 5-minute countdown triggers automatic download of Qwen3.5-2B-Q4_K_M.gguf (~1.8GB) + its F16 vision projector from Unsloth/HuggingFace. One-off: never runs again after completion or cancellation.
Multi-chat sessions
Chat history persisted in browser IndexedDB (potato_sessions database). Sessions survive page reload. Image data URLs stripped before persistence; sanitized on restore so backend never receives [stripped] references.
Real Pi QA
Use Chrome MCP for browser-based QA on http://potato.local. Test on real hardware before merging Pi-impacting changes.
Health check: curl -s http://potato.local/status | python3 -c "import json,sys; d=json.load(sys.stdin); print(d['state'])"
Coding Style & Naming Conventions
- Python 3.11+, 4-space indentation, type hints for new/changed code.
- Small testable functions, explicit error paths.
snake_casefor functions/files,PascalCasefor classes.- Env vars prefixed with
POTATO_(e.g.POTATO_LLAMA_RUNTIME_FAMILY). - Shell scripts:
set -euo pipefail, POSIX/Bash-safe, explicit side effects. - Frontend: Vanilla JS (no frameworks, no build step). Single
chat.jsloaded via<script>tag.
Testing Guidelines
- TDD-first for all feature tickets.
- Playwright tests split by feature (8 spec files in
tests/ui/). - Python tests:
pytestwithtests/unit/andtests/api/. - Name test files
test_*.py(Python) or*.spec.js(Playwright). - For Qwen3.5 models: only F16 projectors. Vision detection matches
*qwen*3.5*.
Commit & Pull Request Guidelines
Concise imperative commits:
feat(api): add backend mode fallbackfix(chat): preserve multi-turn historychore(runtime): standardize bundle layouttest(ui): add model switcher specs
PRs must include:
- What changed and why.
Closes #<id>orRefs #<id>for issue linkage.- Test evidence (pytest + Playwright output).
- Pi QA results for Pi-impacting changes.
- No AI attribution lines.
- Never merge PRs in any repo (core, inferno, or other org repos) without explicit user approval.
Branching & Workflow
mainis merge-only. No direct commits for feature/bug/chore work.- Branch from
main:feat/issue-<id>-<slug>,fix/issue-<id>-<slug>,chore/issue-<id>-<slug>. - Use the org-owned
Potato OSboard athttps://github.com/orgs/potato-os/projects/1for all active work. - Board flow: Todo → In Progress → In Review → QA → Done.
- Squash merge preferred. Delete branch after merge.
- See
WORKFLOW.mdfor full lifecycle details.
Security & Configuration
Never commit secrets, SSH keys, or device-specific credentials. Keep credentials in environment variables. Confirm .gitignore excludes local-only artifacts (references/, output/, .venv/, node_modules/).