# AGENTS.md · dara/icarus

[View on GitCafe](https://git.cafe/dara/icarus/blob/agents-md-server-has-users/AGENTS.md)

Repository: [dara/icarus](https://git.cafe/dara/icarus)

Visibility: public

Requested revision: refs/heads/agents-md-server-has-users

Commit: 73e07f3a874690314807ad5e6efbfdc81efbf908

Blob: 174b36700f341f5f69d5e3cdea57b8f078b91491

Size: 6984 bytes

[Immutable source](https://git.cafe/dara/icarus/blob/73e07f3a874690314807ad5e6efbfdc81efbf908/AGENTS.md?format=markdown)

```
i(Dara,me) want to write this to you(agent). Icarus is mine, and we take care of it together. This branch is where Icarus learns to live online.

Icarus is a Valorant strategy and lineup tool: a desktop Flutter app where players, igl's, and coaches draw plans on maps, place agents and abilities, and build a library of their tactical work. The shipped version keeps everything on the user's machine. This branch adds the cloud: accounts, a Convex backend, sync, and shared strategies. The web beta is public and real users keep their libraries on the production deployment. We are still finding the right shape for these features, but the shape now has people living in it.

Quick glossary of relevant parties in this document:

you - the agent reading this document and working on Icarus directly.
me/we/us - the humans contributing to Icarus. This is the party talking to you as we build.
users - Valorant players and coaches. They are not developers. They will never read an error log, they will only feel whether the app worked.

And the words we use when we work:

simple - how cleanly the logic breaks down. each step follows from the last, no step doing two jobs.
obvious - the next reader never asks "why is this here?". measured by the reader. not always simple; sometimes obvious has more parts.
the library - a user's saved strategies, folders, and lineups. in local mode it lives only on their machine; in cloud mode a copy lives on the server. both copies are the user's work.
round-trip - export then import with nothing lost.
local mode - the shipped behavior: signed out, library on disk, exactly what main's users have today.
cloud mode - signed in, library synced to Convex.
op - one queued change to cloud data. an op lands when the server accepts it.

The domain vocabulary (strategy, page, lineup, .ica file, and friends) lives in CONTEXT.md, use those words exactly. DESIGN.md holds the rules for how we build UI (the values themselves live in `lib/const/settings.dart`), read it before touching UI. PRODUCT.md holds who this is for and how it must feel.

Here's the philosophy we work by:

After audits, investigations, or substantial testing runs, read [answers.md](answers.md) before reporting the outcome.

Before changing view cones, map blockers, or elevation handling, read [docs/vision-model.md](docs/vision-model.md). It defines which data controls wall placement and height.

## The library is sacred
Corrupted or dropped library data is unrecoverable. In local mode nothing here changes: a change to the Hive models means source models, generated adapters, and a migration (`lib/migrations/`) so that data written by any past version loads in this one. In cloud mode the op queue holds work the user believes is saved. Every op either lands or the user is told, on screen, before they walk away. When a write path is uncertain, fail loudly without saving rather than save something wrong.

## Sync status is a promise
The chip that says synced is the app promising the work is on the server. A conflict resolved in silence makes the app lie. So does an op dropped after retries, and so do offline edits with no badge. A user who catches the app lying once stops trusting it with their library. Every state the user's work can be in has a face on screen, and when the true state is uncertain, show uncertainty.

## The server has users now
The clay hardened: production Convex holds real users' libraries, and a cloud library is as sacred as a local one. Never wipe a deployment that users write to. A schema change ships as a migration: widen the schema so old and new rows both validate, backfill in pages that reschedule themselves and record when they finish (`backfillAssetReferences` in `convex/maintenance.ts` is the pattern), and only narrow the schema once the backfill is done everywhere. Merging `convex/` changes deploys to production, so every server change must keep working with the web build that is live and every desktop build we shipped; gate new server strictness behind something new clients opt into. The dev deployment still holds only our data and can be reshaped freely.

## Everything exported must come home
Every .ica file and library backup from every version we ever shipped must round-trip. When you change what a strategy contains, export and import change with it in the same commit.

## The server is reading it now
On main we build the data layer as if a server were already reading it. Here one is. `convex/` reads every byte the client writes and has never seen our widgets, and that stays true: payloads carry a payloadVersion, rows carry a revision, JSON is canonical, and nothing under `lib/collab/` or `convex/` imports UI.

## The user is mid-thought
People use Icarus while their tactical idea is still hot, the interface must never make them wait or wonder. The network is never in that loop. Edits hit the canvas immediately and the queue catches up behind them. When a feature works but feels wrong, it is not done.

## Fight for the obvious solution
Measure twice, cut once: understand the problem fully before building, because cleverness is what gets written when you haven't. The biggest simplicity win is refusing to solve problems we don't have. Good code is the most simple thing that delivers full functionality, nothing traded away, nothing bolted on. Push back when you see a more obvious way.

## Some general rules
These steer us in the right direction. They are not hard-set, but default to following them; if you think one should be ignored, be very loud about it and get approval from us first.

- Never edit generated files (`*.g.dart`, `convex/_generated/`). Edit the source models or schema, then regenerate (`dart run build_runner build --delete-conflicting-outputs`; Convex regenerates via `npx convex dev`).
- Convex behavior is defined by the current schema, source, generated types, and tests in this repository. If an API detail is uncertain, check the current official Convex documentation before editing, then prove the change with `npx tsc --noEmit` and `npm run test:convex`.
- Each thread owns one running instance of the app, and never touches another's. Start yours once, keyed by your worktree or branch name: `flutter run -d macos --pid-file /tmp/icarus-<key>.pid --dart-entrypoint-args "--hive-store-dir=<dir>"`, with `<dir>` = `~/Library/Containers/xyz.icarus-strats/Data/Library/Application Support/icarus-<key>` so its library is yours alone, launched from an unsandboxed shell or it draws but takes no clicks. Instances share a window frame, so move yours aside before you click. After every edit, hot reload it with `kill -USR1 $(cat /tmp/icarus-<key>.pid)`; the tool's stdin is not a terminal, so typing `r` does nothing. Keep it running for the thread, and kill it when the thread ends. The signed-in session is shared across instances: sign out from yours and you sign everyone out.
- Do not add generated agent guidance or bundled skills back to the repository.

```
