Files
deepgrain-foundry/AGENTS.md
T
44r0n7andClaude Opus 5.5 cab9343b26 Run browser tests locally: Playwright 1.64.1 in .venv, scripts switch to it on their own
The browser tests and screenshot tools now work with plain python3 on a machine whose
system Python refuses pip installs: tools/use_venv.py re-runs them with the repo's .venv.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 16:50:15 -04:00

10 KiB
Raw Blame History

Deepgrain Foundry: instructions for coding agents

Deepgrain Foundry is a 2D falling-sand factory game in the style of Sandustry, set in a Terraria-like side-view world. Every grain of sand, ore, water and fire is simulated. The player flies a small bot, digs with a resonance drill, vacuums up loose material, builds furnaces, belts, tubes and machines from blocks, casts metal in molds and sells goods at a trade dock to pay off a land claim. It runs in the browser as one self-contained HTML page, built from small JavaScript files with no dependencies.

The owner (the user) directs the work and is the real playtester. You build, test and report.

Start of every session

  1. Read NOTES.md. It is the user's to-do inbox (see "Working from NOTES.md" below).
  2. Read docs/PROJECT_MAP.md before searching the code. It says where everything lives. Its index lists every top-level function and variable by file.
  3. Before changing how the game plays, read the relevant part of docs/DESIGN.md. Many choices were made deliberately and are recorded there with the reason.
  4. Look at the top of docs/agent-log.md for what was done recently.

Honesty and accuracy

These rules matter more than speed.

  • Do not guess or assume. If you have not checked something, say so. Keep "I tested this and it works" separate from "this should work".
  • Prove it with the game. Run the tests. For anything visible, take a screenshot and look at it before saying it looks right (see "Testing"). A passing test only covers what it checks.
  • Never invent names. Look a function or variable up in the project map's index, or search for it (rg name src), before you use it or talk about it.
  • When you are not confident, check more than one source. Use the web for anything outside this repository: browser APIs, Playwright, WebGL, real-world materials and temperatures, physics, how other games do something. Confirm important facts with at least two independent sources, and give the links.
  • Report problems plainly. Say when a test fails, when something is only partly done, or when you could not reproduce a problem. Never weaken or delete a test just to make it pass. If a test's expectation is outdated because the design changed on purpose, update it and say why.
  • Ask when it matters. If a request is unclear and getting it wrong would waste a lot of work, ask first. If the choice is small or easy to undo, take the sensible reading, say which one you took, and carry on.

Working from NOTES.md

NOTES.md belongs to the user. They add notes at any time and ask you to work on them ("do the notes", "do the belt note"). Work on notes only when asked.

  • Kinds of note.
    • Tasks ("add...", "make...", "fix...") are built.
    • Discussion notes ("let's think about...", "brainstorm...", "what can we do about...", "let's talk about...") get options with trade-offs and a recommendation. Do not build them until the user decides.
    • Questions get answers.
  • Keep the user's wording. Never rewrite or tidy a note. If you need to add context, add a sub-bullet that starts with (agent).
  • When a task is finished, move the note, word for word, into the "Done, please check" section at the bottom. Under it, add an (agent) sub-bullet saying what changed and how to check it in the game. Do not delete it: the user deletes it after checking. If the user says it is not fixed, move it back up and add their feedback as a sub-bullet.
  • Log your work. Add an entry at the top of docs/agent-log.md with the date, the note, what changed, the files touched, the tests run, and anything left open.
  • Record decisions. When the user decides something about the design, add it to the decisions list in docs/DESIGN.md with the date and the reason they gave.

Commands

python3 build.py                 # build dist/index.html from src/ (takes a second)
python3 -m http.server -d dist   # play at http://localhost:8000 (or open dist/index.html)
python3 test/run_all.py          # all headless test suites, in parallel (about a minute)
python3 test/run_all.py pipes    # only suites whose name contains "pipes"
python3 test/run_all.py --browser   # also the browser tests (rebuilds first; about 3 more minutes)
node test/t_pipes.js             # one suite
python3 tools/shot.py shots/x.png --setup tools/scenes/clear-sky.js --js "..." --ticks 60
python3 tools/where.py 7614      # which source line is line 7614 of dist/index.html
python3 tools/map.py             # regenerate the index in docs/PROJECT_MAP.md

You need Node (tested with 22) and Python 3. Playwright for Python and its Chromium live in .venv/ in the repo (not committed); set up as in README.md → Develop. The browser scripts and tools/shot.py switch to .venv by themselves (tools/use_venv.py), so the commands above work with plain python3.

How the code is put together

  • build.py joins the files listed in src/manifest.json, in order, into one classic script inside src/shell.html. The output is dist/index.html. There are no modules and no import/export: every file shares one global scope.
  • Order matters only for code that runs when the page loads. A function can call a function from any file. But a top-level const/let must come before top-level code that uses it. New files go into the manifest in the right place: simulation first, then render, then UI.
  • 'use strict' at the top of src/sim/base.js applies to the whole bundle.
  • src/sim/ must never touch the DOM (no document, window, canvas or localStorage). The headless tests load only these files, and keeping the simulation pure keeps a later port to Rust or WebAssembly possible.
  • Keep files small and on one topic. Aim for under about 400 lines. Split a file that grows past that along a clear seam, add the new file to the manifest, and rebuild. Then run python3 tools/map.py and update the hand-written part of the project map.
  • Errors point at real files. In Node tests, stack traces name the source file and line. In the browser they point into dist/index.html; use tools/where.py to find the source line (tools/shot.py does this for you).

Rules for the simulation

  • Performance. The simulation runs 60 ticks a second over millions of cells.
    • Use the existing typed arrays.
    • Do not allocate objects or arrays inside per-cell loops.
    • Only chunks that are awake (32×32 grains) are simulated. After changing cells directly, wake them; setCell already does.
    • Measure before and after anything in a hot path (tools/perf.py, tools/experiments/factory_cost.js).
  • Determinism. Randomness in src/sim/ comes only from rnd() (seeded) or mulberry32. Never use Math.random there. The headless tests rely on this: the same code gives the same output, line for line.
  • Save compatibility. The user has saved worlds they care about. Material ids, block codes (BP_*), part ids (P_*) and machine type names are stored in saves.
    • Never renumber or reuse them. Add new ones at the end (raise NMAT for materials).
    • New saved fields need a default when an older save lacks them. Migrations go in deserializeWorld (src/sim/save.js).
    • Only raise SAVE_VERSION if old saves truly cannot be read any more, and tell the user first.
  • Undo. Every building action the player takes must be undoable. Wrap it in undoRecord(label, fn). The functions that place and remove things record their own steps with undoNote. The bot building ghosts is never an undo step of its own.

Checklists

A new material. It needs:

  • an id constant in src/sim/materials.js (at the end, before NMAT, and raise NMAT);
  • a def(...) entry with a plain description;
  • its price in PRICE (src/sim/economy.js) if it can be sold;
  • its place in BAG_ORDER (src/ui/state.js) if it can go in the bag;
  • its place in MARKET_GROUPS (src/ui/notebook.js) if it is sold;
  • any special tables it belongs to: CASTMETAL, SOLIDMETAL, METAL_TIER, BAR_OF, WALL_OF/WALL_LIST, REFINE, HOTLIQ, DROSS.

Check its colour in both looks.

A new buildable piece. It needs:

  • an entry in PIECES (src/sim/pieces.js) with cost and description;
  • a block code or machine footprint;
  • placing and removing that support undo and Ctrl-replace;
  • a tile in BUILD_CATS and an icon in PIECE_ICONS (src/ui/build-menu.js);
  • hover info (hoverInfo in src/ui/info.js);
  • drawing in src/render/ (pixel look, and a smooth-look class if it needs one);
  • saving and loading of any state it keeps;
  • a line in the controls help (controlsHtml in src/ui/notebook.js) if it adds controls;
  • a headless test.

Text the player reads (descriptions, toasts, hover info, help):

  • plain words and short sentences;
  • what it does and how to use it;
  • °C for temperatures, "grains" for cells, "blocks" for the 4×4 build grid;
  • no programmer words.

Match the existing descriptions in PIECES and the material defs.

Testing

docs/TESTING.md has the details: the harness, how to write a suite, the browser tests, screenshots and the pitfalls. The essentials:

  • Every change to the simulation gets a headless check in a test/t_*.js suite. Write the check first when fixing a bug, and see it fail.
  • Test in the pixel look (the default in the tools). Use the smooth look only when checking graphics, and then check both looks.
  • Look at your screenshots. Open the PNG and check it shows what you meant, especially after changing anything drawn.
  • Before you say you are finished, run python3 test/run_all.py. After changing anything in src/ui/ or src/render/, run python3 test/run_all.py --browser too.
  • For a refactor that should not change behaviour, save the suites' output before and after and diff them. The headless suites are deterministic, so any difference is a real change (lines with timings in ms excepted).

Git

  • Small commits, one change each, with a message that says what changed for the player or developer. Run the tests first.
  • dist/ and shots/ are build output and are not committed.
  • Never commit secrets: tokens, passwords, or keys.
  • Work happens one agent at a time. If running several agents side by side is set up later, this section will say how they coordinate.