# ───────────────────────────────────────────────────────────────────────────── # Lantern Systems — a Rust browser-agent harness, connected over your LAN. # Product: `harnessd` — a daemon any node on the network (Tailscale or plain # LAN) can talk to, that drives a browser with an LLM agent loop and streams # everything: events, screenshots, traces, nothing cloud-hosted, ever. # The build gate is REAL: [verify] = cargo build + cargo test on the repo. # ───────────────────────────────────────────────────────────────────────────── [company] name = "Lantern Systems" mission = "Ship the local-first browser agent harness: a Rust daemon on your own network that any machine can reach over Tailscale or plain LAN — LLM-driven browsing with server-side sessions, token auth, live event streams, screenshots and traces. If it doesn't compile on main, it doesn't exist — and if it talks to the cloud, it's fired. HOUSE RULE: `cargo check` runs the moment any commit lands; read it fix it in-session before the full gate sees you." product = "lantern-agent: `harnessd` binary + HTTP/WS API (sessions, tasks, tool-call event stream), a Browser abstraction (real Chromium + a scripted mock for CI), an LLM agent loop (OpenAI-compatible models, e.g. the team llama.cpp box), token auth, screenshots, trace logs." stack = "Rust (axum, tokio, serde, ureq). Tests run against the scripted mock browser - no real browser in CI. Chromium/CDP lands later behind the same trait." [verify] # The build gate the whole company lives and dies by: compile + contract # tests + a /healthz smoke. judge.sh prints FAIL lines naming owner files. command = "cd work/project && sh judge.sh" timeout_secs = 600 # Per-commit reflex: cargo check — the team's minimum bar before claiming # anything is done. Errors land in work/project/check_report.md + mail. [verify_fast] command = "cd work/project && cargo check --quiet 2>&1 | head -30" timeout_secs = 120 # Ten years of civilization, five people. [[people]] name = "Nia Odum" role = "Team Lead / API Design" personality = "API-contract zealot. Writes the route table before anyone writes rust, reviews every diff against it, and says 'contract is code' in meetings. Ships compile-green small changes. Protects the daemon's public surface: renaming a route = incident. Allocates browser-work to Rufus only when the mock is passing. Keeps the tailscale-ready setup as a stated product promise, not docs fluff." is_manager = true [[people]] name = "Kazimir Novak" role = "Systems & Networking Engineer" personality = "Lantern's TLS tokens, bind addresses, proxy config are his. Allergic to localhost-only defaults ('it must bind 0.0.0.0 or it is a toy'). Speaks in exact curl commands. Wrote our token-auth scheme and defends it like a castle. Distrusts async code that cannot be reasoned about in sequence; draws sequence diagrams for every endpoint." is_manager = false [[people]] name = "Rufus Whitley" role = "Agent Loop Engineer" personality = "Lives inside the tool-call protocol: strict JSON schemas, retries with backoff, and 'the agent must never be surprised by its own output'. Obsessed with the action vocabulary; will argue for an hour about whether `click` should be `click_at(x, y)` or `click(selector)` and has a documented opinion. Keeps the agent's failure modes as explicit enum variants, never with plain strings." is_manager = false [[people]] name = "Imelda Cruz" role = "QA / Test Architect" personality = "Treats the scripted mock browser as the test contract: scripts pages as fixtures, hunts flakes on sight, treats 'it works locally' as an insult. Her mock fixture DSL is ruthless and she writes a new adversarial case for every bug we ship. Refuses to pass any review whose source of truth is only a passing demo; a test file must move." is_manager = false [[people]] name = "Bea Lindqvist" role = "Docs & Developer-Care" personality = "Writes the README, the curl examples, and the actual getting-started path a stranger could follow on a Tailscale join in under five minutes. Bounces any PR that adds a flag without an example. Keeps the 'bells and whistles' checklist: if it's shipped, it's documented, with a curl that proves it." is_manager = false # Backlog — every acceptance gate is files-on-disk criteria; the [verify] # command above compiles + tests whatever exists, every commit, every 17:00. [[backlog]] id = "T1" title = "harnessd skeleton: axum server + /healthz + routes table" description = "Open the repo. work/project must be a cargo project: src/main.rs serves /healthz and a stubbed route table (POST /sessions, POST /sessions/{id}/task, GET /events/{id}, GET /metrics). Route names are the contract - stolen names are a review-bounce. Keep main() thin; run blocking work via the pool." area = "systems" effort_days = 2 [[backlog.acceptance]] file = "src/main.rs" min_lines = 40 [[backlog]] id = "T2" title = "Browser trait + scripted mock browser" description = "Define the trait apps drive through: browse, click, type, screenshot, b. Implement MockBrowser in src/mock.rs with scripted fixture pages so Imelda's tests can drive scenarios without a real browser. Trait must be object-safe (Box)." area = "browser" effort_days = 3 [[backlog.acceptance]] file = "browser.rs" min_lines = 30 [[backlog]] id = "T3" title = "Agent loop: LLM driver + tool-call JSON schema" description = "src/agent.rs: run a task against an OpenAI-compatible endpoint (default the team llama.cpp at http://localhost:8085/v1/chat/completions), parse tool-call JSON strictly, dispatch to Browser, loop until 'done' or budget exhausted. Failures are enum AgentError variants with a per-step retry, never strings." area = "agent" effort_days = 4 [[backlog.acceptance]] file = "src/agent.rs" min_lines = 60 [[backlog]] id = "T4" title = "Event stream: SSE + audit log JSONL" description = "src/events.rs: every session emits structured events (ToolCall, PageEvent, Step, Result, Error) on GET /events/{id} as SSE and to a JSONL audit log sidecar file. Reconnecting clients get replay-from-byte framing or an explicit cursor." area = "systems" effort_days = 2 [[backlog.acceptance]] file = "src/events.rs" min_lines = 30 [[backlog]] id = "T5" title = "LAN-ready: token auth + tailscale story + bind flags" description = "Kaz's castle: API token from env/config; 401 on mismatch; document the Tailscale/ plain-LAN join in docs/deployment.md (bind 0.0.0.0 default, curl examples from another tailnet node, optionally the `tailscale serve` variant). /metrics with basic counters for the ops page." area = "systems" effort_days = 3 [[backlog.acceptance]] file = "src/auth.rs" min_lines = 40 [[backlog]] id = "T6" title = "Bells: screenshots, session recordings, trace JSONL" description = "Implement MockBrowser screenshot rendering (PNG/PPM sketch on the fixture), session.replay.jsonl, and a trace JSONL per step (LLM prompt excerpt, tool call, result). This is what makes the harness auditable after the fact." area = "browser" effort_days = 3 [[backlog.acceptance]] file = "src/trace.rs" min_lines = 40 [[backlog]] id = "T7" title = "The gauntlet: contract test suite" description = "tests/contract.rs drives the mock end-to-end: session lifecycle, agent loop tool dispatch, full event stream shape, auth behavior. Bea's rule: every route behaves per its stub; a test case for every event type. Judge (judge.sh) runs this every commit." area = "qa" [[backlog.acceptance]] file = "tests/contract.rs" min_lines = 40 [[backlog]] id = "T8" title = "Getting-started: docs a stranger can follow" description = "Bea's README + docs/deployment.md + docs/quickstart.md with a 5-minute path: cargo run harnessd, curl /healthz from a second machine over Tailscale, POST one task, watch the SSE. Every flag documented with an example. No example = not shipped." area = "docs" effort_days = 2 [[backlog.acceptance]] file = "docs/quickstart.md" min_lines = 20