flybrain/CONTRIBUTING.md
acamilo 660c3cf00d
Some checks failed
ci / node 22 (test + typecheck) (push) Has been cancelled
ci / rust stable (cargo test --workspace --release) (push) Has been cancelled
ci / infra/tests/lint.sh (push) Has been cancelled
ci / playwright apps/stage (allowed to fail) (push) Has been cancelled
flybrain v0.4.0: public tree (history retained privately)
2026-09-21 15:09:46 +00:00

9.5 KiB

Contributing

A simulated fly brain that plays Game Boy games on a 24/7 stream. Start with docs/architecture-tour.md: it walks every layer once, says why each is built the way it is, and points at the code. Then docs/stream-mvp-plan.md for what has been decided and what state it is in.

There is no game in this repository. ROM-POLICY.md explains what that means and how to run everything without one — which is almost all of it.

Branches and worktrees

Work happens on a branch, in its own git worktree, never on main directly.

git worktree add -b feat/my-change .claude/worktrees/feat-my-change main
cd .claude/worktrees/feat-my-change

.claude/worktrees/ is gitignored, so the checkouts never enter history. One branch per piece of work, named for what it does (feat/, fix/, docs/, chore/, publish/). Several can be in flight at once because each has its own tree; that is the point of using worktrees rather than switching branches in place.

Merge into main with --no-ff, so a branch stays visible as a unit in the history:

git merge --no-ff feat/my-change
git worktree remove .claude/worktrees/feat-my-change

Never rewrite published history on main.

The suites, before every merge

All four, green, on the branch. Not a subset.

npm ci
npm test                                    # TypeScript: brain, feed, stage units, bridge
npm run typecheck                           # tsc across every workspace
cd services/flysim && cargo test --workspace   # Rust: core, gb, service; ROM-gated tests skip
bash infra/tests/lint.sh                    # shellcheck + systemd-analyze over infra/

Plus the stage's browser suite when anything visual changed:

cd apps/stage && npm run test:e2e           # Playwright: screenshot baselines per fixture and tab,
                                            # text-size lint, phone-downscale legibility, structure
npm run mockups                             # regenerate the review PNGs

Notes that save time:

  • The ROM-gated Rust tests skip with a printed line when FLY_ROM is unset. That is a normal green run, not a hole. See ROM-POLICY.md.
  • The dataset tests read data/fafb-v783, which is committed; they need no download.
  • The Rust golden tests compare against services/flysim/golden/*.flygold at 0 ulp. If one fails after a change to the Rust side, the Rust side is wrong — see the oracle rule below. Goldens are regenerated from the TypeScript with packages/brain/tools/golden.ts, never edited by hand.
  • Screens are reviewed as PNGs under apps/stage/mockups/, not as prose descriptions. On-screen copy is terse.

Commit messages

Plain and factual. A short imperative subject with a scope prefix, a body when the change needs one, and nothing else.

feat(decoder): add blocked-direction cooldown

Raises the habituation of a channel the sim loop has watched produce no
movement for a whole hold. Does not name a position or an alternative.

No attribution trailers of any kind. No Co-Authored-By, no generated-with lines, no tool or model credits, no session links. This applies to every commit, including ones written by an agent.

No secrets in a commit, ever — no stream keys, tokens, passwords or credentials. Nothing from a cartridge. Check the diff before committing, not after.

Licences and file headers

The repository is not under a single licence. LICENSES.md at the root says which paths fall under which terms; the short version is Apache-2.0 for the code, CC BY 4.0 for the documentation, on-screen copy and stage assets, and CC BY-NC 4.0 — unchanged and not ours to relicense — for the FlyWire connectome artifacts in data/fafb-v783/.

  • No per-file licence headers are required. Do not add them to new files, and do not add them to existing ones. LICENSE, LICENSES.md and NOTICE at the root carry the terms for the whole tree; a header on every file is noise that then has to be kept accurate.
  • NOTICE carries the attributions. If you add third-party material — a vendored source, a font, a dataset, anything you did not write — put its attribution in NOTICE and its row in LICENSES.md in the same branch as the material, with its own licence text alongside it in the tree. Material whose upstream licence you cannot establish does not go in; if it does go in, NOTICE says so in plain words rather than guessing.
  • Contributions are accepted under Apache-2.0. By submitting a change you license it under the Apache License 2.0, per section 5 of that licence. There is no separate CLA and none is planned.
  • Anything derived from data/fafb-v783/ is CC BY-NC 4.0, like the data. services/flysim/golden/real.flygold is the one such artifact in the tree today. If a change adds another — a recorded run, a prerendered image, a checked-in trace — say so in the commit and add it to section 3 of LICENSES.md. The four .flyfeed fixtures are generated by the fake simulator and are deliberately not in that category; keep it that way.

The two binding contracts

  • docs/feed-protocol.md — the WebSocket snapshot format the service publishes and the page reads.
  • docs/control-api.md — the loopback HTTP API the bridge calls.

These are binding. Where any other document, design note or comment disagrees with them, the contracts win and the other document is the thing that is wrong. Changing a contract is its own change, made deliberately, with both sides of it updated in the same branch and the reason written down. Both have a Rust implementation and a TypeScript implementation and a shared fixture that each must pass; a change that only satisfies one side is not done.

One property of the control API is load-bearing: there is no button endpoint. Nothing outside the simulation can press a button, and a test asserts the route table contains no such route. Do not add one.

The oracle rule

packages/brain is the reference implementation. Its semantics define what the neural core does: the LIF kernel, plasticity, the decoder, the agent loop, the checkpoint envelope.

When the Rust port, the service, the page or anything else disagrees with it, fix the other side. Never adjust packages/brain to make another implementation's output match — that converts a bug into the specification. The verbatim prototype modules under packages/brain/tests/legacy/ and the bit-exact oracle tests against them exist to make this rule enforceable, and so do the Rust goldens.

The default-config version strings lif-1ms-f64-v2 and fly-kc-mbon-rstdp-v2 stay as they are. Checkpoints are keyed on them; changing one invalidates saved state on the release box.

If the reference itself is genuinely wrong, that is a deliberate change to the reference with a new version string, regenerated goldens, and the reasoning recorded — not a quiet edit.

The honesty rule

Nothing on the stream is scripted. This is not a style preference; it is the point of the project, and it constrains contributions.

  • The buttons are always the fly's. Nothing outside the simulation chooses, biases, defaults or times a button press. There is no fallback press, no scripted objective, no nudge on a timeout. A scene the fly ignores waits.
  • Rewards are read out of memory after the fact. A reward rule may observe what the game state became and stimulate the modulatory pathway. It may not tell the fly where to go or what to press. Every value is positive by design; there are no penalties.
  • The readout is fixed, not learned, and it knows nothing about maps, doors or goals.
  • Failure is shown. Stalls, rollbacks, unsupported cartridges and lag appear on screen with the reason. They are not hidden and not smoothed over.
  • What is real and what is scaffolding is documented. docs/limitations.md is the honest list — the reward modulator is synthetic, the retina is not fly optics, learning has not been shown to improve play. docs/rewards-learning.md has its own "Honesty" section stating exactly what a reward can and cannot move. If your change alters where that line falls, update those pages in the same branch and say so plainly. If a claim has not been measured, write that it has not been measured.

A change that makes the demo look better by making it less true will be rejected, however small.

Where the documents are

  • docs/architecture-tour.md — read first; the whole system, layer by layer, with the reasons.
  • docs/feed-protocol.md, docs/control-api.md — the binding contracts.
  • docs/stream-mvp-plan.md — decisions and current status.
  • docs/model.md, docs/plasticity.md, docs/readout.md, docs/rewards-learning.md, docs/dataset-format.md, docs/verification.md — the neural core, what it learns, how it decides, what it is paid for, the data layout, and how all of it is checked.
  • docs/limitations.md — what this is not.
  • docs/design/ — per-feature design notes (flysim.md, stage-bridge.md, fly-avatar.md, animation.md, ladder.md, macros.md, platformer.md, room-escape.md, gpu.md, infra.md, and others). Designs, not contracts: where one differs from a contract, the contract wins.
  • infra/docs/ — the runbook, provisioning records, measurements and spike write-ups for the machines that actually run it.
  • docs/publish/ — notes prepared for publication, including how the licence decision was reached.
  • LICENSE, LICENSES.md, NOTICE — the terms, the path-by-path split, and the attributions.
  • ROM-POLICY.md — no game here, and how to run everything anyway.