Files
tvctl/AGENT.md
T
44r0n7 584da2d825 chore: scaffold tvctl foundation
Set up the Rust crate, baseline module layout, and project docs so the
repository matches the design bundle and builds cleanly as a starting point.
2026-04-14 09:02:32 -04:00

6.9 KiB

AGENT.md

Instructions for AI Agents Working on tvctl

Read this file completely before writing any code.


Step 1: Orient Yourself

Before doing anything else, read these files in this order:

  1. PROJECT_MAP.md — architecture, decisions, data shapes, command tree, API surface
  2. ROADMAP.md — current milestone, what's done, what's next
  3. README.md — user-facing documentation (understand what you're building toward)

Do not skip any of these. Do not begin coding until you have read all three.


Step 2: Understand What You Must Not Change

The following decisions are final and were made intentionally during design. Do not re-open, re-litigate, or silently deviate from them:

  • Resource-verb CLI patterntvctl device list, not tvctl list-devices
  • Unix socket for CLI↔daemon — not TCP, not pipes
  • HTTP API for tool builders — versioned at /v1/, loopback-only default
  • Adapter trait — the exact interface defined in PROJECT_MAP.md
  • TOML config — not YAML, not JSON
  • kebab-case key namesvolume-up, not VolumeUp, not volume_up
  • JSON response envelope{ "ok": true, "data": {...} } always
  • Organic app cache — no pre-populated database, grows from live TV data
  • User-level systemd service — not system-level, not root

If you believe one of these decisions is wrong, document your concern in a comment or note and ask for clarification. Do not silently work around them.


Step 3: Understand the Codebase Before Touching It

Before modifying any file:

  1. Read the file you are about to change completely
  2. Read any files it imports or depends on
  3. Understand the data flow through the component you are changing

Do not make changes based on filenames or directory structure alone.


Code Standards

Rust conventions

  • Use thiserror for error types
  • Use tokio for all async runtime
  • Use axum for HTTP server
  • Use clap (derive API) for CLI
  • Use serde + serde_json for all serialization
  • Use uuid crate for UUIDs
  • Use chrono for timestamps
  • Prefer anyhow for application-level error propagation
  • All public types must have doc comments

Error handling

  • Never use .unwrap() in non-test code unless you can prove it cannot fail
  • Every error returned to the user (CLI or API) must include a hint field
  • CLI errors must suggest the next action — not just report what went wrong
  • API error code values are stable contracts — do not change existing codes

CLI output

  • Human-readable output by default
  • --json flag must work on every command
  • Errors go to stderr, data goes to stdout
  • Do not mix human text and JSON in the same output stream

API

  • All endpoints return the standard envelope — no exceptions
  • error.code values are snake_case strings
  • error.hint is optional but strongly encouraged
  • Never return platform-specific field names at the API surface level

Help text

Every command must have:

  1. A one-line description
  2. A short paragraph of context
  3. A usage line
  4. All subcommands/args listed with descriptions
  5. At least two concrete examples
  6. A notes section for technical details (if applicable)

File Responsibilities

File/Directory Responsibility Notes
src/main.rs Binary entry point, daemon vs CLI dispatch Keep thin
src/cli/ All clap definitions and CLI handlers No business logic here
src/daemon/ Daemon lifecycle, routing, services Core of the application
src/daemon/registry.rs Device registry, persistence Owns devices.json
src/daemon/discovery.rs SSDP discovery, polling Platform-agnostic
src/daemon/cache.rs App cache, persistence Per-platform json files
src/daemon/state.rs In-memory state cache Never persisted
src/api/ axum HTTP server, route definitions Thin layer over core
src/adapters/mod.rs TvAdapter trait, TvKey, shared types The contract
src/adapters/roku/ Roku ECP implementation Only place Roku logic lives

The CLI and API layers must contain no business logic. They translate user input into core calls and translate core results into output. All logic lives in the daemon and adapter layers.


App Resolution Flow

When a user runs tvctl app launch netflix or POST /v1/devices/{id}/apps/launch:

1. Check per-device installed app list in memory
2. If found → launch directly
3. If not found → check platform cache (roku.apps.json)
4. If found in cache → attempt launch (app might be installed)
5. If launch fails → report "not installed", suggest `tvctl app list`
6. If not in cache at all → fetch live app list from TV
7. Populate platform cache with all returned apps
8. Persist to cache file
9. Retry launch

Name matching is case-insensitive. Users can also pass raw platform IDs with --id.


Discovery Flow

1. tvctld starts
2. If discovery.auto_discover = true → run SSDP scan
3. SSDP returns device IP addresses
4. For each address → instantiate appropriate adapter → call adapter.discover()
5. Adapter returns DeviceInfo
6. Check if device UUID already exists in registry
7. If new → add to registry, use device-reported name as default friendly name
8. If existing → update last_seen timestamp
9. Persist registry to devices.json

Manual add bypasses SSDP — takes IP and platform directly.


Updating PROJECT_MAP.md

You MUST update PROJECT_MAP.md when you:

  • Add a new source file
  • Change the directory structure
  • Implement a new CLI command or API endpoint
  • Change a data shape
  • Make a significant architectural decision
  • Complete a roadmap milestone

Update the "Last updated" date at the top of PROJECT_MAP.md with every change.

Do not let PROJECT_MAP.md drift from the actual codebase.


Updating ROADMAP.md

When you complete a task:

  1. Move it from "In Progress" or its milestone section to "Completed"
  2. Add the completion date
  3. Update the "Current Focus" section if the milestone changed

When you start a task:

  1. Move it to "In Progress"
  2. Note what you're working on

What To Do If You Are Unsure

If you are unsure about:

  • A design decision → check PROJECT_MAP.md first. If not covered, ask.
  • What to build next → check ROADMAP.md current milestone.
  • How a component should behave → check README.md for user-facing behavior.
  • Whether to add a feature → default to no. Prefer smaller scope.

When in doubt, do less. A smaller correct implementation is better than a larger incorrect one. This project has a clear identity — do not add things that don't serve it.


Definition of Done

A feature is done when:

  • It works correctly
  • --json output works if it's a CLI command
  • Errors include helpful hint text
  • Help text is complete (description, usage, examples, notes)
  • No .unwrap() in non-test paths
  • PROJECT_MAP.md is updated if structure changed
  • ROADMAP.md is updated to reflect completion