|
| 1 | +# AGENTS.md — violetear |
| 2 | + |
| 3 | +This file is the door for any AI coding agent working in this repo. Read it |
| 4 | +first. Other tools (Codex, OpenCode, Cursor, Aider) auto-load it; for Claude |
| 5 | +Code it's loaded via the workspace convention in `~/Workspace/CLAUDE.md`. |
| 6 | + |
| 7 | +There is also an `AGENT.md` (singular) — Alex's personal protocol describing |
| 8 | +how to *think* before coding (Understand → Plan → Develop → Document). That |
| 9 | +file is voice/process; **this file (AGENTS.md, plural) is structural**. |
| 10 | + |
| 11 | +## What violetear is |
| 12 | + |
| 13 | +A full-stack isomorphic Python web framework. FastAPI + uvicorn on the |
| 14 | +server; Pyodide bundle on the client. Ships a server-side HTML builder |
| 15 | +(`markup.Element`, `HTML.div(...)`), a reactive-state layer (`@app.local` + |
| 16 | +`ReactiveProxy`) that emits `data-bind-*` annotations during SSR and |
| 17 | +hydrates them on the client, a `DOMElement` wrapper for imperative |
| 18 | +browser-side DOM work, a WebSocket RPC bus (`@app.server.rpc` / |
| 19 | +`@app.client.realtime`), a CSS DSL (`Style`, `StyleSheet`), and PWA |
| 20 | +scaffolding. No JS framework, no templates, no htmx. |
| 21 | + |
| 22 | +Vault node: `vault/Efforts/Repos/violetear.md` (in Alex's workspace). |
| 23 | + |
| 24 | +## Layout |
| 25 | + |
| 26 | +``` |
| 27 | +violetear/ package source |
| 28 | + app.py App, ClientRegistry, ServerRegistry, SocketManager, bundle gen |
| 29 | + markup.py Element, Document, HTML builder, Component |
| 30 | + state.py @local decorator, ReactiveProxy, LeafProxy |
| 31 | + client.py Pyodide-side: ReactiveRegistry, hydrate(), socket listener |
| 32 | + dom.py Pyodide-side: DOMElement, DOM static factory, Event protocol |
| 33 | + storage.py Pyodide-side: store/session wrappers around localStorage |
| 34 | + style.py fluent style builder |
| 35 | + stylesheet.py stylesheet builder, selectors |
| 36 | + color.py Color class + Colors named-color registry |
| 37 | + pwa.py Manifest, ServiceWorker generators |
| 38 | + presets.py FlexGrid, SemanticDesign, UtilitySystem, Atomic |
| 39 | +tests/ pytest suite |
| 40 | +docs/ quarto docs site + example .py files (consumed by test_examples.py) |
| 41 | +examples/ hand-written demos (hello_world, reactivity, full_pwa, …) |
| 42 | +issues/ design docs for unimplemented features |
| 43 | +.github/workflows/ CI (ruff format-check + pytest on push/PR) |
| 44 | +roadmap.md v1.0 roadmap (Phases 1-4) |
| 45 | +AGENT.md Alex's code-agent thinking protocol (orthogonal to this file) |
| 46 | +``` |
| 47 | + |
| 48 | +## Conventions |
| 49 | + |
| 50 | +- **Python 3.12+** required. Use modern features: `match`, `list[str]` (not |
| 51 | + `typing.List[str]`), generic `[T]` syntax on functions/classes. |
| 52 | +- **Type hints** on public APIs; loose typing inside private methods OK. |
| 53 | +- **Async** for any function that crosses the server/client boundary |
| 54 | + (`@app.server.rpc`, `@app.server.realtime`, `@app.client.callback`, |
| 55 | + `@app.client.realtime`, lifecycle handlers). Sync handlers are rejected |
| 56 | + at decoration time with `ValueError`. |
| 57 | +- **Decorator order** matters for `@app.local`: `@app.local` goes |
| 58 | + *outside* `@dataclass`. The standalone `local()` in `state.py` is the |
| 59 | + bare-decorator version; `App.local` wraps it to also register the class |
| 60 | + for bundle transpilation. |
| 61 | +- **Module-level defs** for any class/function passed to `@app.local` or |
| 62 | + `@app.client.*`. The bundle generator dedents source so nested defs |
| 63 | + work in tests, but real apps should define these at module scope for |
| 64 | + predictability and IDE support. |
| 65 | +- **Formatting** via `ruff format`. CI enforces it. Run `make format` |
| 66 | + before pushing or let `make` (default = `test-unit`) fail loudly. |
| 67 | +- **No JS framework**. No htmx. If you find yourself reaching for one, |
| 68 | + build the missing primitive in Python instead. |
| 69 | + |
| 70 | +## Common workflows |
| 71 | + |
| 72 | +### Run the test suite |
| 73 | + |
| 74 | +```bash |
| 75 | +make # = make test-unit (ruff format-check + pytest --cov) |
| 76 | +make test-all # also collects tests outside tests/ (rarely needed) |
| 77 | +``` |
| 78 | + |
| 79 | +CI runs the same gate on every push/PR (`.github/workflows/tests.yml`). |
| 80 | + |
| 81 | +### Add a feature |
| 82 | + |
| 83 | +1. Read the relevant `issues/<n>-...md` design doc if one exists; otherwise |
| 84 | + draft one before coding non-trivial surface. |
| 85 | +2. Update `roadmap.md` checkboxes when shipping a phase item. |
| 86 | +3. Pin behavior with a test in the matching file |
| 87 | + (`tests/test_<surface>.py`). Coverage gates aren't enforced but |
| 88 | + leaving Pyodide-only paths at 0% is the established norm — that's |
| 89 | + what the deferred "Pyodide simulator" slice is for. |
| 90 | + |
| 91 | +### Fix a bug found in production usage |
| 92 | + |
| 93 | +If the bug shows up in an example (e.g. `examples/*.py`), the example is |
| 94 | +documentation; fix it too. If the bug is in the framework, add a |
| 95 | +regression test pinning the new behavior before flipping the production |
| 96 | +code — characterization first prevents accidental re-regression. |
| 97 | + |
| 98 | +### Release |
| 99 | + |
| 100 | +```bash |
| 101 | +NEW_VERSION=1.3.0 make release |
| 102 | +``` |
| 103 | + |
| 104 | +This: |
| 105 | +1. Verifies formatting + runs the full suite. |
| 106 | +2. Bumps `pyproject.toml` and `violetear/__init__.py` (they must stay in sync). |
| 107 | +3. Commits, tags `v1.3.0`, pushes commit + tag, creates a GitHub release. |
| 108 | + |
| 109 | +Never bump versions manually — the makefile is the single source of truth |
| 110 | +so the two version files don't drift. |
| 111 | + |
| 112 | +## Surfaces with limited test coverage |
| 113 | + |
| 114 | +Be extra careful when touching these — regressions are easy because the |
| 115 | +test suite won't catch them: |
| 116 | + |
| 117 | +- **`client.py`** (hydration, ReactiveRegistry, socket listener) — pure |
| 118 | + Pyodide code, no server-side tests. Bundle-generation tests pin *what |
| 119 | + gets emitted* but not the runtime behavior. |
| 120 | +- **`dom.py` `DOMElement`** — same, pure Pyodide. |
| 121 | +- **`storage.py`** — has a server-side memory fallback for testing but |
| 122 | + it's untested today. |
| 123 | +- **Style/StyleSheet fluent methods** (`.padding`, `.font`, `.border`, |
| 124 | + `.flex`, …) — black-box tested against fixtures in `tests/test_examples.py` |
| 125 | + via expected `.css` outputs in `tests/expected_outputs/`, no unit tests. |
| 126 | +- **`Component` subclasses + `ElementSet.spawn`** — pattern exists in |
| 127 | + `markup.py` but no examples or tests exercise it. |
| 128 | + |
| 129 | +## Things to NOT do |
| 130 | + |
| 131 | +- Don't broadcast from `@app.server.on("startup")`. At startup there are |
| 132 | + zero active websocket connections; clients reconnect after startup. Use |
| 133 | + `@app.server.on("connect")` + `.invoke(client_id, ...)` to greet new |
| 134 | + clients, or `.broadcast(...)` from inside a request handler when there |
| 135 | + are already-connected clients. |
| 136 | +- Don't put PII or secrets in `examples/`. They're shipped in PyPI sdists. |
| 137 | +- Don't add a new third-party runtime dependency without first checking |
| 138 | + whether the workspace already has a sibling lib (in `repos/`) that |
| 139 | + provides the capability. Cross-repo reuse: `vault/Atlas/Architecture/` |
| 140 | + may have a relevant design doc. |
| 141 | + |
| 142 | +## Know-how index |
| 143 | + |
| 144 | +*(Empty for now. Add procedure docs to `know-how/<topic>.md` as recurring |
| 145 | +tasks emerge — testing a new client-side feature, debugging a Pyodide |
| 146 | +bundle failure, profiling render performance, etc. Each entry here gets a |
| 147 | +short "when to reach for it" line so future agents can match by intent.)* |
0 commit comments