Files
guide/CLAUDE.md
2025-11-22 01:14:11 +00:00

8.5 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

A FastAPI-based guided demo platform that automates browser interactions with Raindrop using Playwright. The app executes data-driven actions (stored in ActionRegistry) on behalf of personas that target configured browser hosts (CDP or headless). All configuration is externalized via YAML files and environment overrides.

Entry Point: python -m guide (runs src/guide/main.pyguide.app.main:app) Python Version: 3.12+ Key Dependencies: FastAPI, Playwright, Pydantic v2, PyYAML, httpx

Essential Commands

# Install dependencies
uv sync

# Type checking (required before commits)
basedpyright src

# Compile sanity check
python -m compileall src/guide

# Run development server (default: localhost:8000)
python -m guide
# or with custom host/port:
HOST=127.0.0.1 PORT=9000 python -m guide

# View API docs
# Navigate to http://localhost:8000/docs

# Key endpoints:
# GET  /healthz                           # liveness check
# GET  /actions                           # list action metadata
# POST /actions/{id}/execute              # execute action; returns ActionEnvelope with correlation_id
# GET  /config/browser-hosts              # view current default + host map

Code Structure

Root module: src/guide/app/

  • actions/ — FastAPI-triggered demo actions; thin, declarative, action-scoped side effects. Registry wiring happens in actions/registry.py.
  • auth/ — Pluggable MFA/auth helpers (currently DummyMfaCodeProvider; needs real provider for production).
  • browser/BrowserClient + Playwright wrappers; centralizes navigation, timeouts, error handling, tracing. Handles both CDP attach and headless launch.
  • core/ — App bootstrap: AppSettings (Pydantic v2 w/ env prefix RAINDROP_DEMO_), logging, dependency wiring, venv detection.
  • errors/GuideError hierarchy; routes normalize error responses to HTTP status + payload.
  • raindrop/ — GraphQL client + operations. Queries/mutations defined in raindrop/operations, schemas/types colocated in strings/.
  • strings/ — Centralized selectors, labels, copy, GraphQL strings (no inline literals in actions). Service enforces strict key lookup to catch UI mismatches early.
  • models/ — Domain/persona models. PersonaStore loads from config; use Pydantic v2 with explicit types.
  • utils/ — Shared helpers. Keep <300 LoC per file; avoid circular imports.
  • api/ — FastAPI routers; map requests → ActionRegistryBrowserClient → responses.

Config files (git-tracked):

  • config/hosts.yaml — Browser host targets (id, kind: cdp|headless, host, port, browser type).
  • config/personas.yaml — Personas (id, role, email, login_method, browser_host_id).

Config overrides (runtime only, never commit):

  • RAINDROP_DEMO_BROWSER_HOSTS_JSON — JSON array overrides hosts.yaml.
  • RAINDROP_DEMO_PERSONAS_JSON — JSON array overrides personas.yaml.
  • RAINDROP_DEMO_RAINDROP_BASE_URL — Override default https://app.raindrop.com.

Architecture Patterns

App Initialization (main.py → create_app)

  1. Load AppSettings (env + YAML).
  2. Build PersonaStore from config.
  3. Build ActionRegistry with default actions (dependency-injected with persona store + Raindrop URL).
  4. Create BrowserClient (manages Playwright contexts/browsers, handles CDP + headless).
  5. Stash instances on app.state for dependency injection in routers.
  6. Register error handlers (GuideError → HTTP, unhandled → 500 + logging).

Action Execution Pipeline

  • Request: POST /actions/{action_id}/execute with ActionRequest (persona_id, host_id, etc.).
  • Router resolves persona + host from config → validates persona exists.
  • BrowserClient.open_page() — resolves host by ID → CDP attach or headless launch → reuse existing Raindrop page.
  • Action.run(context) — executes logic; may call ensure_persona() (login flow) before starting.
  • Response: ActionEnvelope with correlation_id (from ActionContext) + status + result.

Browser Host Resolution

  • kind: cdp — connect to running Raindrop instance (requires host + port in config). Errors surface as BrowserConnectionError.
  • kind: headless — launch Playwright browser (chromium/firefox/webkit); set browser field in config.
  • Always use async with BrowserClient.open_page() to ensure proper cleanup.

GraphQL & Data Layer

  • raindrop/graphql.py — HTTP client (httpx, 10s timeout).
  • raindrop/operations/ — query/mutation definitions + response models.
  • Validate all responses with Pydantic models; schema mismatches → GuideError.
  • Never embed tokens/URLs; pull from AppSettings (env-driven).
  • Transport errors → GraphQLTransportError; operation errors → GraphQLOperationError (includes details from server).

Selector & String Management (strings/)

  • Keep all selectors, labels, copy, GraphQL queries in strings/ submodules.
  • Use strings.service (enforces domain-keyed lookups); missing keys raise immediately.
  • Selectors should be reusable and labeled; avoid brittle text selectors—prefer data-testid or aria labels.

Development Workflow

  1. Edit code (actions, browser logic, GraphQL ops, etc.).
  2. Run type check: basedpyright src (catches generic types, missing annotations).
  3. Sanity compile: python -m compileall src/guide (syntax check).
  4. Smoke test: python -m guide then hit /docs or manual test via curl.
  5. Review error handling: ensure GuideError subclasses are raised, not generic exceptions.
  6. Commit with scoped, descriptive message (e.g., feat: add auth login action, chore: tighten typing).

Type & Linting Standards

  • Python 3.12+: Use PEP 604 unions (str | None), built-in generics (list[str], dict[str, JSONValue]).
  • Ban Any and # type: ignore: Use type guards or Protocol instead.
  • Pydantic v2: Explicit types, model_validate for parsing, model_copy for immutable updates.
  • Type checker: Pyright (via basedpyright).
  • Docstrings: Imperative style, document public APIs, include usage examples.

Error Handling & Logging

  • Always raise GuideError subclasses (not generic Exception); routers translate to HTTP responses.
  • Log via core/logging (structured, levelled). Include persona/action IDs and host targets for traceability.
  • For browser flows, use Playwright traces (enabled by default in BrowserClient); disable only intentionally.
  • Validate external inputs early; surface schema/connection issues as GuideError.

Testing & Quality Gates

  • Minimum gate: basedpyright src + python -m compileall src/guide before merge.
  • Add unit tests under tests/ alongside code (not yet in structure, but expected).
  • Mock Playwright/GraphQL in tests; avoid real network/CDP calls.
  • Require deterministic fixtures; document any env vars needed in test module docstring.

MFA & Auth

  • Default DummyMfaCodeProvider raises NotImplementedError.
  • For real runs, implement a provider and wire it in core/config.py or auth/ modules.
  • ensure_persona() in actions calls the provider; stub or override for demo/test execution.

Performance & Footprint

  • Keep browser sessions short-lived; close contexts to avoid handle leaks.
  • Cache expensive GraphQL lookups (per-request OK, global only if safe).
  • Don't widen dependencies without justification; stick to project pins in pyproject.toml.
  • Promptly close Playwright contexts/browser handles (wrapped in contextmanager; keep action code lean).

Git & PR Hygiene

  • Scoped, descriptive commits (e.g., feat: add sourcing action, fix: handle missing persona host).
  • PRs should state changes, commands run, new config entries (hosts/personas).
  • Link related issues; include screenshots/logs for UI or API behavior changes.
  • Never commit credentials, MFA tokens, or sensitive config; use env overrides.

Quick Checklist (New Feature)

  • Add/verify action in actions/ with thin logic; use strings/ for selectors/copy.
  • Ensure persona/host exist in config/hosts.yaml + config/personas.yaml (or use env overrides).
  • Run basedpyright src + python -m compileall src/guide.
  • Test via python -m guide + /docs or manual curl.
  • Add GraphQL queries to raindrop/operations/ if needed; validate responses with Pydantic.
  • If auth flow required, implement/mock MFA provider.
  • Review error handling; raise GuideError subclasses.
  • Commit with descriptive message.