Files
deepgrain-foundry/docs/agent-log.md
T

19 KiB
Raw Blame History

Agent log

What agents have done, newest first. Add an entry for every piece of work: the date, which note or request it was for, what changed, the files touched, the tests run, and anything left open or not verified. Keep each entry short; the git history has the details.

2026-10-23: world sounds are heard around the bot, not the view (Claude Code)

Note: "the sound effect is still heard when the player's zoom level is farther out… sounds shouldnt be heard based on the viewport, but rather in a radius from the player."

  • src/ui/audio.js sfxPlace: gain from the distance to the bot's centre (full within 24 grains, silent at 96), pan from the horizontal offset; zoom no longer matters.
  • test/play.py: the audio checks use points relative to the bot; a new check zooms out to 2 and plays a visible sizzle 200 grains away: silent (it played before). play.py and all 11 headless suites pass. docs/DESIGN.md.
  • Open: nothing.

2026-10-23: F on an ingot mold does nothing and says nothing (Claude Code)

Note: "now that ingot molds empty themselves, F still makes a message…"

  • src/sim/actions.js interactAt passes ingot molds by, so F reaches anything else nearby; src/ui/actions.js shows no "Nothing to use there." when the cursor is on an ingot mold.
  • Tests: t_molds (F on an ingot mold in every state returns nothing, changes nothing, no sound) and play.py (F over an ingot mold leaves no status and no toast). All 11 headless suites and play.py pass.
  • Open: nothing.

2026-10-23: pixel-art silo for the pixel look (Claude Code, with Codex)

Note: "the silo art looks the same for both pixelated graphiocs and smooth."

  • src/render/machines.js: drawSilo is the smooth (illustrated) version as before; in the pixel look drawSiloPixel draws it grain by grain: a drum shaded by column with rib rows and ladder rungs, a cone to the slide gate, legs, the roof hatch with its lid, and the sight glass filling in the contents colour. Codex's first version read poorly (dark side columns made the drum look narrow); Claude redrew the grain layout.
  • tools/scenes/silo.js (run after clear-sky.js): three silos, empty, half sand, nearly full copper ore with the gate open. Shots shots/silo-*.png at zoom 6 and 12; smooth shots match the before shots pixel for pixel. docs/DESIGN.md, docs/PROJECT_MAP.md.
  • Tests: all 11 headless suites and play.py (129) pass. t_smooth.py's half-resolution check failed twice on the slow fallback at load ~5 (known flaky; it passed in the previous task).
  • Open: nothing.

2026-10-23: world sounds are local (Claude Code, with Codex)

Note: "there is a sound effect that seems to happen randomly but constantly… maybe the sound effects are not local?" The owner then confirmed a new world is quiet.

  • Measured headlessly over 5 game minutes: a fresh world emits no sounds; a lava and water contact 720+ grains from the bot emitted 2,889 sizzles (about 580 a minute), and events.js played every one at full volume. The sizzle (heat.js boiling and quenching) is the only sim sound with a position.
  • src/ui/audio.js: sfxPlace(x, y) gives gain (1 in view, 0 one view width beyond it) and pan; playSfx(name, arg, x, y) skips silent ones before the rate limit and plays the rest through a gain and stereo panner (AU.bus). src/ui/events.js passes positions. docs/DESIGN.md, docs/PROJECT_MAP.md.
  • test/play.py: far off-screen is silent, on screen plays, just off-screen is quieter and panned. All 11 headless suites, play.py and t_smooth.py pass.
  • Checked afterwards with the owner's exported world (seed 622525): over one minute, 48 sizzles and no other sounds, all from water boiling on naturally hot deep rock (a pool on 580°C basalt and drips in a shaft and a cave, about 500 grains below the bot). Nothing built there. With the fix every one of them is silent from the surface.

2026-10-23: fewer, smaller messages (Claude Code, with Codex)

Note: "lets change the engot mold and other status messages. they are way too large and just too many messages…". The owner chose molds, machine warnings and shorter status lines.

  • src/render/overlays.js: every mold always shows a small percentage (moldLabel), gold 100% · F for a set bit or tank, red when spoiled or when an ingot mold is blocked (mo.blocked, set in ejectIngot, not saved). Details are in the hover panel (info.js), which also carries the furnace-loading advice that F messages used to append.
  • src/ui/events.js: no cards or toasts for bars dropping, blocked molds or set castings (the sounds stay). Fan heat is a red ! marker on the fan (mm.hot, hover shows °C); melted tubes and machines destroyed by heat get a short red floating label (warn event, coloured spawnFloater) instead of a toast.
  • Status lines shortened to one sentence across src/sim (molds, actions, blocks, blueprints, drill, items, machines, tools, undo, vacuum) and src/ui (actions, selection).
  • Tests: play.py records every status line and checks none is over 90 characters (longest now 55); mold label and no-toast checks. t_molds (message lengths, mo.blocked), t_build (hot fan: mm.hot, no toast), t_world. All 11 headless suites and play.py pass.
  • Shots: shots/messages-pixel.png, shots/messages-smooth.png (filling, blocked, set bit, hot fan).
  • Open: the first-ever bar drop no longer shows an explaining card; the notebook still explains.

2026-10-23: ingot molds need no F; removing a mold returns its metal (Claude Code, with Codex)

Note: "now that the ingot molds drop the ingots, what is the purpose of F…". The owner chose no F on ingot molds.

  • src/sim/molds.js ejectIngot: when nothing over or in the mold is molten, junk and mixed metal (as scrap) and plugs over the trough or frozen in the grate are written as loose grains into the 4 rows under the mold; what doesn't fit waits. Slag and ash are thrown away. After a drop or a clean-out the grate above is raked. Bars and the short stub work as before.
  • collectMold on an ingot mold only says "Ingot molds empty themselves." Bit and tank molds keep F. removeMold (every kind) gives the set metal to the bag: whole bars as bars, the rest as scrap; molten metal is left alone.
  • Text: troughState flaws, info.js, overlays.js (no "· F" on ingots), pieces.js, progress.js, notebook.js, docs/systems/stone-and-liquids.md, docs/DESIGN.md.
  • Tests: t_molds 60 checks (F does nothing on ingots; plate, slag and rake cases now automatic; junk out, mixed metal out as scrap, junk waits when blocked and while molten; removal returns bars and scrap, leaves molten metal). furnace_play.js adapted. All 11 headless suites and play.py (122) pass.
  • Open: undoing a mold removal brings the mold back empty (its metal stays in the bag).

2026-10-23: compact build card top-left (Claude Code, with Codex)

Note: "build tool has no info box like the other tools." The owner chose a compact card.

  • src/ui/selection.js: the build tool has a card again: Q, piece name, cost (red if short), E; variant chips, Replace · G and ghosts waiting; one how-to line; the status line. No long description; that stays in the hover popup.
  • src/ui/build-menu.js: the .bb-status row is gone (its contents are on the card); the variant chips are shared through buildVariantChips(P). src/styles/style.css, docs/DESIGN.md, docs/PROJECT_MAP.md.
  • test/play.py: the card is visible while building with name and cost and without the description; status and Replace · G are read from the card; no .bb-status (122 checks pass). All 11 headless suites pass; t_smooth.py's half-resolution check is still flaky under load.
  • Shots: shots/build-card-* (belt and wall, both looks, 1280×800 and 900×700) look right.
  • Open: at 900px wide the bottom-left hover panel still sits partly under the build bar.

2026-10-23: Replace is held G, not Ctrl (Claude Code, with Codex)

Note: "i see the issue was ctrl+w closing the tab… it happens for the bookmark hotkey too. maybe change the replace key?" The owner chose holding G.

  • src/ui/input.js: GAME.ctrl became GAME.replace, set while G is held. Ctrl no longer does anything on its own, so WASD works while replacing. Ctrl+S, Ctrl+Z, Ctrl+wheel and the leave-page prompt are unchanged.
  • Flag users and text: previews.js, actions.js ("Hold G to replace it."), build-menu.js (Replace · G chip, key hints), loop.js, state.js, notebook.js controls, docs/DESIGN.md.
  • test/play.py: the replace checks hold G; a new check flies right with D while G is held (121 checks pass). All 11 headless suites pass. t_smooth.py's half-resolution check failed on the slow fallback again (known, flaky under load).
  • Open: nothing.

2026-10-23: Ctrl+W no longer closes the game without asking (Claude Code)

Note: "crash: hoplding crtl and pressing W crashes the whole browser."

  • Cause: Ctrl+W is the browser's close-tab shortcut (the whole window if it is the last tab); holding Ctrl for replace while pressing W to fly hits it. Chrome and Brave reserve it, so the page never sees the key and cannot cancel it.
  • src/ui/input.js: a beforeunload handler makes the browser ask before leaving while GAME.running. The visibilitychange autosave no longer depends on the audio context existing (it used to return early before any sound had played).
  • test/play.py: 2 checks (leaving asks while a world is open, not on the title screen); 120 checks pass. All 11 headless suites pass; t_smooth.py passed on its rerun (the known flaky half-resolution check failed once at load ~5).
  • Open: the real Ctrl+W prompt can only be seen in a headed browser; the owner checks it.

2026-10-23: build menu hover popup, top-left card hidden while building (Claude Code, with Codex)

Notes: "mouse hover reviels a new info popup…" and "dont need the short description in the build menu…". The owner chose to hide the top-left card entirely while building.

  • src/ui/build-menu.js: #bb-pop above the hovered tile (icon, name, cost, size, power, full description, how to place, filter help, build keys), clamped to the window; on a gamepad it shows for the selected tile. The .bb-tip row became .bb-status: variant (material, direction, filter) chips, Replace · Ctrl, ghosts waiting, and status messages. The bar now updates its parts separately so hovering doesn't rebuild the tiles.
  • src/ui/selection.js: #selcard is hidden for the build tool unless copying, cutting or pasting; the build branch moved to the bar. src/styles/style.css: popup and status styles; on narrow screens the bottom-right panel moves up while building.
  • src/ui/notebook.js, docs/DESIGN.md, docs/PROJECT_MAP.md.
  • Tests: play.py 118 checks (popup on hover, switching tiles, hidden on leave, gamepad, no .bb-tip, card hidden while building and back for tools 1 and 2, status in the bar; the Ctrl replace check now reads the bar). All 11 headless suites pass. t_smooth.py's half-resolution check is still flaky under load (~4.8): 1 pass in 3 runs, as before.
  • Screenshots in shots/ (build-popup-*) at 1280×800 and 900×700 in both looks.
  • Open: at 900px wide the bottom-left hover-info panel sits partly under the build bar.

2026-10-23: a backed-up heap on a belt no longer shears (Claude Code, with Codex)

Note: "belts with a big pile on top only move the grains on the top of the stack…". The owner's setup: a belt ending in a screw tube going up; material mixed into the bottom layers never moved.

  • Cause: carryColumn (src/sim/conveyors.js) skipped a blocked grain and carried on up the column, so with the front backed up the top slid over a stuck bottom. Reproduced headlessly first: over 4,000 ticks, 8,815 shear steps at the tube (1,415 against a wall) and only 57 of 192 bottom sand grains left the belt.
  • Fix: on a belt, the walk stops at the first blocked powder grain, so the load above stands still. On a filter the walk goes on only so chosen materials can still drop through; nothing above a blocked grain is carried. After: no shear, 119 of 192 sand grains out through the tube.
  • test/t_conveyors.js: 3 new checks (no shear into an up tube, none against a wall, bottom layer leaves through the tube). tools/scenes/belt-heap.js sets up the owner's case for tools/shot.py; shots at 30 and 900 ticks look right. docs/DESIGN.md, docs/PROJECT_MAP.md.
  • Tests: all 11 headless suites and play.py (109) pass. t_smooth.py failed its half-resolution check once at load 6.5 and passed on the rerun.
  • Open: t_smooth.py's half-resolution check is flaky on a busy machine (the game drops to pixel when frames are slow). It is worth making that test immune to the slow fallback.

2026-10-23: ingots drop out of the bottom of the mold (Claude Code, with Codex)

Note: "copper bars go into the furnace. also they shouldnt require the player to press F…". The owner confirmed the first part was a bug: the knock-out threw bars up into the furnace.

  • src/sim/molds.js: ejectIngot runs from checkMolds (every 15 ticks). Bars spawn directly under the mold, only where entFreeAt says there is room; the rest wait in the trough. Metal is taken from the top so the short stub stays on the floor. ingot_drop and ingot_blocked events (blocked is said once per spell via mo.blockedSaid, not saved). F on a ready ingot never launches bars upward; F on an empty, short or spoiled mold still cleans and rakes.
  • src/ui/events.js: first drop gets a card, later drops a toast; blocked gets a toast. Ingots no longer emit cast_ready.
  • Text: pieces.js, progress.js, notebook.js, info.js, docs/DESIGN.md (with the decision), docs/systems/stone-and-liquids.md, docs/PROJECT_MAP.md.
  • Tests: t_molds (38 checks: downward drops, stub kept, top-up, blocked and warned once, partial drops, full entity pool, slag), t_basics, t_stone, furnace_play.js and play.py section 6 changed to the new behaviour. All 11 headless suites, play.py (109) and t_smooth.py (8) pass. A shot of a mold with a belt one block below shows the bars riding off.
  • Open: a belt touching the mold's underside counts as blocked; bars need one open block. The owner may want a belt directly under the mold to work.

2026-10-23: Shift+Q/E build tabs, G pick key removed (Claude Code, with Codex)

Notes: "add shift + q/e to move tabs" and "having the "g pick" there is confusing".

  • src/ui/build-menu.js: selectBuildCat(c) (shared with tab clicks) and cycleBuildCat(d); the menu hint now reads Q/E cycle · Shift+Q/E tab.
  • src/ui/input.js: Shift+Q/E switch tabs with the build tool; G is unbound.
  • Text: build card (selection.js), notebook controls (notebook.js), actions.js comment, docs/DESIGN.md, docs/PROJECT_MAP.md (regenerated, --check passes).
  • test/play.py: checks for Shift+Q/E, wrapping, the remembered piece per tab, plain Q/E, and that G no longer calls pick while middle mouse still does (109 checks).
  • Tests: all 11 headless suites and play.py pass; a tools/shot.py shot of the Moving tab shows the new hint. t_smooth.py passes, but its "half-resolution smooth still draws" check failed on several runs with the 'slow' pixel fallback while the machine was busy (load ~14). The previous commit fails the same way under the same load, so this change didn't cause it.
  • Open: that check is timing-sensitive on a loaded machine; rerun it when the machine is idle.

2026-10-23: browser tests run locally (Claude Code, with Codex)

Request: get the browser tests, Playwright and Chromium working on the owner's Arch machine (Python 3.14 refuses a system-wide pip install).

  • Playwright 1.64.1 and its Chromium 156 (revision 1248) are in an in-repo .venv/ made with uv.

  • Added tools/use_venv.py and called it before Playwright imports in tools/shot.py, tools/perf.py, tools/playthrough.py, test/play.py and test/t_smooth.py. The sys.prefix guard prevents an exec loop if the venv lacks Playwright.

  • Updated requirements.txt, .gitignore, README.md, AGENTS.md, docs/TESTING.md and docs/PROJECT_MAP.md for Playwright 1.64.1 and venv setup.

  • Checks: regenerated the project map and ran tools/map.py --check.

  • Tests run with plain python3: test/run_all.py --browser all passed (11 headless suites, play.py 101 checks, t_smooth.py 8 checks) on the newer Chromium; tools/shot.py shots of the stone-uses scene look right in both looks. With .venv moved away, play.py exits with the setup message instead of a traceback.

  • Open: nothing.

2026-10-23: export from claude.ai to a local repository (Claude, in claude.ai)

Request: split the source into small files, deduplicate and tidy a little, test fully, and write the notes, docs and agent instructions for local development.

  • Source split.
    • core.js (3,688 lines), main.js (2,012) and render.js (938) became 52 files in src/sim/, src/render/ and src/ui/, each with a header line. The build order lives in src/manifest.json.
    • Checked that the joined code was identical to before, apart from comments and blank lines.
    • Every headless suite gave the same output, line for line, before and after.
    • Screenshots of two scenes, in both looks, were pixel-identical before and after (tools/shot.py --freeze).
  • Tidying, behaviour unchanged.
    • One scan loop in simMove instead of two mirrored copies.
    • A drawHazardRims helper replaced 3 copies.
    • The belt and filter shader code shares beltLineSD, bandRidge and bandColor.
    • Removed unused refundCost, wakeArea, brushFor, and the PARTS / PART_BY_ID aliases.
    • The suites share suite() (check / done) from the harness, and failures now set the exit code.
  • Tests.
    • Six broken old scripts were deleted; they used functions removed long ago.
    • Experiments and reports moved to tools/experiments/.
    • t_v2 / t_v4 / t_v9 were renamed t_basics / t_world / t_conveyors.
    • Added test/run_all.py.
  • Tools.
    • tools/shot.py: repeatable screenshots of a scene set up in JavaScript.
    • tools/where.py: maps built-page line numbers back to source files.
    • tools/map.py: the project map's index.
    • tools/scenes/: example scenes.
    • The game skips cursor previews when GAME.hidePreviews is set, for clean screenshots.
  • World files outside claude.ai.
    • Pause → Export world file only worked inside claude.ai, through its downloads hook.
    • It now falls back to an ordinary browser download (exportWorld in src/ui/saves.js).
    • test/play.py checks that an exported file downloads and imports back on the title screen as the same world.
  • Build. build.py writes dist/index.html (a full page), dist/deepgrain-foundry.html (the same without the document wrapper, as the claude.ai artifact used) and dist/bundle-lines.json.
  • Docs. AGENTS.md, CLAUDE.md, README.md, NOTES.md, docs/PROJECT_MAP.md, docs/DESIGN.md, docs/TESTING.md, docs/systems/*, docs/history/requests.md.
  • Tests run. All 11 headless suites pass, as do test/play.py (101 checks) and test/t_smooth.py (8 checks).
  • Open.
    • The owner reports that big piles on belts still only move at the top (NOTES.md); not yet reproduced.
    • The game's last claude.ai artifact was version 24.