[Release] 2026-09-25: v2.7.0 — goal mode that pauses and asks, GPU-only render_page with VRAM lending, voxel path-tracing kit - #306
Merged
Conversation
…e time, pixel prechecks (#293) The 2026-09-24/25 diorama run was judged on SwiftShader stills: 35 of 42 surviving render_page results were `software (swiftshader)` at 51-317 MiB free, and the page is a 1-spp path tracer. - render_gl_mode is "angle" | "unavailable"; below the VRAM bound, or with CROW_RENDER_GL=swiftshader, render_page returns `error: ENVIRONMENT --` naming the free VRAM, no browser, no image. - UNMASKED_RENDERER_WEBGL is read over the DevTools pipe on about:blank before the page loads and after the last capture; software (SwiftShader, llvmpipe, lavapipe, ...) or a non-NVIDIA string next to an NVIDIA card is the same ENVIRONMENT error. ANGLE vulkan first, one retry on the measured `--use-gl=angle` line; CROW_RENDER_ANGLE pins one. No SwiftShader flags. - frames (1-4) / frame_ms: an injected page clock (Playwright page.clock model) freezes Date, performance.now, timers and rAF, seeds Math.random, and advances exactly frame_ms before each capture; 2x2 contact sheet. - `render: {render_mode, renderer, frames, contact_sheet, precheck}` line on every capture and ENVIRONMENT error, and crow_core.last_render(). No-GPU smoke (Chromium 152, --disable-gpu): the probe refused SwiftShader (Chromium still falls back without --enable-unsafe-swiftshader); 4 frames x 250 ms pairwise different, a second call byte-identical, 1.6 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LyBV1tX2JWocGLTjFkNYMh
…pause that reports and asks; a frozen judge checklist; a skip is never done (#294, #295, #296) #294: a failed step is never skipped by the engine (was: #289's skip on the second `failed`; 2026-09-24/25 steps 5-8 skipped on software-GL captures). goal_fail classifies: environment (render_mode unavailable, software on a GPU step, blank/uniform/near-black, identical frames on a motion step, no judge) retries twice 15 s apart, then pauses; capability (valid capture, judge not met) reflects, rolls into a fresh context, splits into 2-3 sub-steps (ADaPT), then pauses; "unknown" twice pauses. A rung is climbed once per attempt. Every pause gets one report turn (what, why, 2-3 proposals, the question), shown in window and phone, kept as pause.report; only a typed line resumes. Step budget 60 min active wall clock and a no-progress timer (10 nudges) -- goal_step_minutes / goal_no_progress_turns, CROW_GOAL_STEP_MINUTES / CROW_GOAL_NO_PROGRESS_TURNS. The 25/60-turn caps pause with the report. #295: per-step checklist frozen at step start (accept lines, `accept: N:` per step, the model's one goal_step checklist, else the first judge call); the judge answers yes/no/unknown per item with evidence; pass = every must-item yes; frames / contact sheet from the render contract (`render: {...}` line, parsed in run_tool, render_page untouched); precheck before the judge model; `reference: <image> -- <criterion>` hook, advisory. #296: a `done` whose note starts "skipped" loads as skipped; the model cannot set a user-skipped step done/running; `/goal redo n`; the bar draws skipped in its own --skip hue with a dashed ring, a skip glyph and the word, running stays amber, a paused step gets a pause glyph. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LyBV1tX2JWocGLTjFkNYMh
…, references, skipped colour (#294, #295, #296) CHANGELOG Unreleased (Added/Changed/Fixed), goals-and-subagents.md (the ladder section replaces the #289 skip section; checklist and reference bullets), tools.md (goal_step, judge), settings.md (goal_step_minutes, goal_no_progress_turns), client-flags.md (CROW_GOAL_* row), README tool table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LyBV1tX2JWocGLTjFkNYMh
# Conflicts: # CHANGELOG.md
…orama skill; build_bundle aliases; install.sh --pathtracer / install.ps1 -PathTracer (#298) - kits/pathtracer/crow-pathtracer.js: three 0.186.1 + three-mesh-bvh 0.9.15 + three-gpu-pathtracer 0.0.24, one esbuild 0.28.2 minified ESM, 958,266 bytes; kit.json (sha256, npm integrity, licence hashes), LICENSE.* byte-identical, NOTICE entry, .gitattributes -text so autocrlf keeps the hash. - voxel-kit.js: VoxelGrid, culled-face meshing, one MeshStandardMaterial per palette colour (never vertexColors), createDiorama (telephoto PhysicalCamera, dome, ground, key light, addLamp with the spot below a solid shade, emission <= 1, despeckle, context-loss recovery, __SCENE__/__PT__ probes); scaffold/ and SKILL.md with the recipe and the density rule. - crow_core: build_bundle aliases crow-voxel-kit / crow-pathtracer to the kit; kit skills seeded once and OFF through a skills/.seeded ledger; enable_kit_skill() for the installers; @CROW_KITS@ filled in on skill read. - install.sh --pathtracer, install.ps1 -PathTracer, pack-release stages kits/. - tools/build-pathtracer-kit.sh, tools/check_pathtracer_kit.py, tools/test_pathtracer_kit.py; CI and `just check` run both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LyBV1tX2JWocGLTjFkNYMh
…ild_bundle kit aliases, checker and suite, CHANGELOG (#298) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LyBV1tX2JWocGLTjFkNYMh
…ure, return it in a finally, judge only after the return (#297, crow-nest#117) Under serve the card has 0.07-0.5 GiB free (#271, #293, crow-nest#117), below the 512/1536 MiB GPU gate (#213, #279), so every capture was an ENVIRONMENT error. Below the bound, when this turn's endpoint is local (loopback, not remote), render_page now POSTs /v1/crow/vram/lend with mib = bound - free + 256 and ttl_s = attempts x (load + wait + capture + frames x frame_ms + 15 s close) + 30 s (<= 600), re-reads nvidia-smi and renders if the bound is cleared; still short -> the ENVIRONMENT error names the loan. tool_render_page is now a wrapper around the unchanged body (_render_page) whose finally POSTs /v1/crow/vram/return (503 retried 3x) after the browser is closed or killed, on every exit. _judge_ask returns any loan still out before its request, because serve parks chat requests while lent. 404/501/409/400/no answer/remote: the error as before (no answer also sends the idempotent return). Each lend and return is logged as a render: line with MiB and ms. Tests: 12 new cases against a fake serve on 127.0.0.1:0; the test module pins _render_lend_root to None so no case can lend a live serve's VRAM. Docs: CHANGELOG Unreleased, docs/reference/tools.md, docs/user-guide/ linux.md and browser.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LyBV1tX2JWocGLTjFkNYMh
…props registry and coverage, check_diorama.py (#299) The kit rendered stills only: one progressive path tracer, no handle on a light, nothing that measures an empty scene. Now: - LIVE mode (default): rasterised preview of the same scene/materials, PCFShadowMap + shadow.radius (PCFSoftShadowMap is gone in r182+), the dome as environment, raster stand-ins for area/key lights, and d.animate((t, dt, scene)) every frame. t comes only from the rAF timestamp, so render_page's frozen clock (#293) gives 4 distinct, byte-repeatable frames. - PHOTO mode: pose at t, pt.setScene once, the existing sample loop, despeckle and context-loss restore. Key P / hover-only Foto/Live button; ?mode=live|photo&t=<s>&ui=0. Drag orbit, wheel/pinch zoom; in photo mode a camera change calls pt.updateCamera(). - d.part(name, grid, {pivot}) -> THREE.Group; d.addSpot(); addLamp / addAreaLight / addSpot return a light rig. Mode-only lights are hidden on the light itself (the path tracer checks only the light's own .visible). - g.prop(name, build) records voxels + bbox; coverage(grid) = terrain columns with a prop above their top cell / terrain columns; propKind(); parseMode(). window.__SCENE__ and the [crow-scene] console line carry props, propKinds, coverage, parts, animated, mode, errors. - kits/pathtracer/check_diorama.py: renders via crow_core.tool_render_page (live frames=4 twice, photo wait 20 s) and prints PASS/FAIL for gpu, probe, samples, props, kinds, voxels, coverage, motion, repeat, photo, errors; exit 0 only when all pass. - Scaffold: props through g.prop, a pinwheel part turned by d.animate. Tests: 22 new cases in tools/test_pathtracer_kit.py (15 -> 37), red on the #298 kit (15 failures, 7 errors), green with it. GPU smoke (scaffold, RTX 5090, ANGLE/Vulkan, 1024^2): live 60 fps, photo 296 samples at 20 s, frame pairs 0.69-1.26 % changed, repeat byte-identical, 0 errors; density checks fail on the scaffold by design. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LyBV1tX2JWocGLTjFkNYMh
…heck command), user guide, scaffold images, CHANGELOG (#299) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LyBV1tX2JWocGLTjFkNYMh
…301) read_file opened every file as UTF-8 with errors="replace", so a PNG came back as up to 16,000 characters of mojibake. The 2026-09-25 lighthouse run read reference/island.png that way (16,056 chars, ~5,946 tokens), blamed read_image and saved "read_image returns raw bytes" into .crow/MEMORY.md. tool_read_file now sniffs the first 8,000 bytes before either branch: a NUL byte (git's buffer_is_binary rule) or a PNG/JPEG/GIF/WebP/BMP/%PDF- signature is refused with one line naming the kind, the size (width x height for PNG/GIF) and the tool to use: read_image for images, pdftotext for a PDF, file/xxd for other binaries. Content decides, not the extension. No ratio heuristic: multi-byte UTF-8 split at the sniff edge stays text (OpenHands software-agent-sdk#5038). The read_file declaration says so. read_image on a missing <stamp>-crop.png whose frame exists now says a crop is written only under 50 % coverage and names the frame; the model asked twice for a crop that never existed and got a bare "no such image". Tests: ReadFileRefusesBinaryTests (10), 8 red without the fix. Docs: CHANGELOG Unreleased, docs/reference/tools.md read_file section and read_image missing-crop row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LyBV1tX2JWocGLTjFkNYMh
…converges (#TBD-pt-wait) The 20 s wait cap (#213) assumed a longer wait never buys a frame. For a progressive path tracer it buys samples: the lighthouse run's night scene held 295 samples at 20 s and stayed grainy. Pages carrying the voxel kit's [crow-pt] marker now get RENDER_WAIT_PT_MAX_MS = 120000; all other pages keep 20 s. The skill tells the agent to use 60-90 s for dark scenes instead of shrinking the scene. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LyBV1tX2JWocGLTjFkNYMh
…, CHANGELOG (#302) The code landed in 6be80f6, whose message still carries the placeholder #TBD-pt-wait; the ticket is #302. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LyBV1tX2JWocGLTjFkNYMh
…303) Live 2026-09-25 15:19: `crow --root .../lighthouse-test` bound the folder in ready(), then _probe restored the last chat (no crow_root) and _adopt_chat_root fell back to restore_root(), i.e. roots.json `active` (diorama-test, a 09:45 pick). Chip, goal bar and the next turn moved to the wrong working area. - ready(): a stated --root is kept as _root_stated and written as `active` (in the window only; a terminal --root still never moves the window, #92). - _adopt_chat_root(stated=): the stated root outranks the template and the restored chat's own record, becomes the chat's own (chosen), and a different own root queues #224's notice. - _probe: after the pin, a head that names another working area is re-pinned with MEMORY_COST_NOTE; a head already naming the stated root keeps its cache. - 4 regression tests (2 red without the fix); prompt_head() source count 3->4. - Docs: CHANGELOG Unreleased/Fixed, docs/user-guide/window.md row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LyBV1tX2JWocGLTjFkNYMh
…TBD-check-lend) The checker runs as its own process, outside any Crow turn, so crow_core had no endpoint and render_page refused every capture with ENVIRONMENT while serve held the card (lighthouse run, 2026-09-25 ~15:35, 280 MiB free; all 10 checks failed). --serve (default http://127.0.0.1:8099/v1, env CROW_SERVE_URL, 'none' = never) binds crow_core's turn endpoint before the first capture. Three tests, red without the change (3 errors), green with it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LyBV1tX2JWocGLTjFkNYMh
…OG (#304) CHANGELOG Unreleased/Fixed entry for 4abae90 (commit message still says #TBD-check-lend) and the checker's --serve option in docs/user-guide/voxel-kit.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LyBV1tX2JWocGLTjFkNYMh
Every scroll container in the window and the phone mirror drew its thumb at rest (robin, 2026-09-25: the per-turn stats line, the Code panel, the chat). The thumb is now transparent by default; one capture-phase, passive scroll listener on the document sets `is-scrolling` on the scrolled element and clears it 800 ms after the last event, a held pointer keeps it, and `@media (hover:hover)` shows it under a mouse pointer (never on a phone, where :hover sticks after a tap). A transparent ::-webkit-scrollbar-corner joins it. Only the colour changes: --sbw, #flow's stable gutter and the #235 track margins stay, so nothing moves. The standard scrollbar-color fallback is gated behind @supports not selector(::-webkit-scrollbar): in Chromium 121+ and WebKitGTK 2.52.3+ a non-auto scrollbar-color (inherited) would switch the ::-webkit-scrollbar path off. Tests: 8 cases in ScrollbarsShowOnlyWhileScrolledTests (CSS strings, phone page, and the script run in node with a fake clock), all red at ced7f6c. Docs: CHANGELOG Unreleased, docs/user-guide/window.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LyBV1tX2JWocGLTjFkNYMh
…he moving parts; a person's page fills the window - LIVE (default): the static island is path-traced and keeps converging while the camera rests; only the animated parts (d.part) are rasterised on top, depth-tested against the island. robin opened the old flat raster preview after a 6/6 run and it looked nothing like the photos. - ?mode=raster (alias preview) keeps the old flat preview. check_diorama's motion and repeat captures now use it: the accumulating path tracer would make a still scene "move" and differ run to run. - A page opened by a person (ui not 0) fills the window at devicePixelRatio (cap 2) and follows resizes; ui=0 (captures, the checker) keeps the fixed width x height at pixel ratio 1, so captures stay comparable. - SKILL.md, voxel-kit.md, scaffold: the three modes and the canvas rule; photo wait 60-120 s on dark scenes (#302); check_diorama --serve (#304). - tools/test_pathtracer_kit: raster mode parsing, the raster motion query. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he checker and scaffold kits\ is copied with -Recurse, and check_diorama.py (#299) runs from the kit folder, so a build machine's kits/pathtracer/__pycache__ would ship as cli\'s did before; install.sh's payload already drops it. The required-file list now also names check_diorama.py and scaffold\{index.html,scene.js}. Not run: no PowerShell on the Linux release machine. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
VERSION 2.7.0 in cli/crow.py, install.ps1's -Version default, the README badge and manifests/operating-point.json. CHANGELOG: Unreleased becomes 2.7.0 — 2026-09-25 (release summary; Added / Changed / Fixed / Known limitations, every item with its issue numbers: #293-#299, #301-#305 and the voxel kit's path-traced live mode), new empty Unreleased. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CheckerBorrowsVramTests (4abae90) sat below `if __name__ == "__main__": unittest.main()`, so `python tools/test_pathtracer_kit.py` -- what CI runs -- never defined it: 38 tests ran, `-m unittest` ran 41. The main block moves to the end; the script now runs all 41. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…follow-up) Windows opens a directory with PermissionError, not IsADirectoryError, so the #301 test test_missing_files_and_directories_answer_as_before failed on the windows-latest runner of the v2.7.0 release PR (#306): "permission denied" instead of "is a directory -- use list_dir". Check isdir first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Sep 25, 2026
This was referenced Sep 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
24 commits on local
mainsince origin/main (v2.6.0, 21d7505), 12 tickets, all released pending robin's live check. Headb3fc1dc./goal redo, and skipped is violet with its own glyph, apart from the amber running step (goal: robin's skip of step 4 is stored as 'done' (counted 5/9), and the bar draws 'skipped' in the same amber as 'running' #296)render:record instead of SwiftShader pixels), captures up to 4 frames at controlled page time with a 2x2 contact sheet, and returns pixel prechecks (render_page silently rasterised on SwiftShader and captured one still: a 16-hour diorama run was judged on 1-spp CPU noise (35 of 42 results software) -- GPU only with a renderer check, deterministic multi-frame capture, pixel prechecks #293)crow-voxel-kit/crow-pathtracer); opt-in withinstall.sh --pathtracer/install.ps1 -PathTracer(Crow ships no renderer for the voxel-diorama task: agents hand-write a WebGL2 ray tracer (16 h of noise, #293-#295) -- vendor the proven three-gpu-pathtracer recipe as a kit with a skill, build_bundle aliases and an opt-in installer flag #298)d.animate, parts, light rigs, orbit) + PHOTO mode (path tracer at t, key P /?mode=photo&t=),g.propregistry and terrain coverage against empty scenes,kits/pathtracer/check_diorama.pyas a goal check (voxel kit v2: animation, orbit, photo mode, props registry, diorama checker -- the kit renders stills only and nothing measures an 'empty' or static diorama #299)[crow-pt]marker) wait up to 120 s so the photo converges; all other pages keep 20 s; the skill asks for 60-120 s on dark scenes, never a smaller scene (render_page cut path-traced pages to 20 s like any other page: the lighthouse night photo stopped at ~295 samples, stayed grainy, and the goal paused offering to shrink the scene -- kit pages need a longer wait ceiling #302)--root DIRkeeps DIR when it restores the last chat (was: roots.jsonactive, the last chip pick) and makes DIR the next start's folder; a restored chat that recorded another folder moves to DIR with A working-area rebind is silent to the model #224's notice (A window started with --root loses it to roots.json 'active' when the last chat is restored: lighthouse-test run bound diorama-test, goal bar gone #303)check_diorama.py,check.sh) borrows VRAM from the local serve like a Crow turn:--serve(default http://127.0.0.1:8099/v1, env CROW_SERVE_URL,none= never) (check_diorama.py runs outside a Crow turn, so render_page never borrows VRAM from serve: the lighthouse gate failed all 10 checks at 280 MiB free #304)?mode=rasterkeeps the flat preview and carries the checker's motion/repeat captures; a person's page fills the window at devicePixelRatio (cap 2),ui=0keeps the fixed canvas (9e4daa4)--rootrow in client-flags.md; pack-release.ps1 strips kits__pycache__and requires check_diorama.py + scaffold; tools/test_pathtracer_kit.py now runs its check_diorama.py runs outside a Crow turn, so render_page never borrows VRAM from serve: the lighthouse gate failed all 10 checks at 280 MiB free #304 tests when invoked as a script (CI ran 38 of 41)Scope
Resolved issues (close on merge)
Resolved, pending live verification
robin asked to close all of these after the release, each with a
## Released in v2.7.0comment that says "live check pending".Refs #293 — render_page silently rasterised on SwiftShader and captured one still: GPU only with a renderer check, deterministic multi-frame capture, pixel prechecks
Live check: N1 serve up + crow-nest#110 1024 MiB reserve, panel closed: render_page on a WebGL page ->
renderercontains "NVIDIA", 64-sample accumulation frame < 5 s wall; N3 rotating cubeframes=4, frame_ms=250-> 4 pairwise different frames,identical_framesfalse, a second call byte-identical (sha256sum)Refs #294 — goal: a step that fails twice is skipped without asking; failures need a class, a ladder, a budget and a PAUSE that reports and asks
Live check: N4 diorama goal while serve holds the GPU -> 2 environment retries 15 s apart, then "Paused · step N" + the model's report (found / why / 2-3 proposals / question) in window and phone; no step skipped without /goal skip; a typed line resumes
Refs #295 — judge: rubric picked at judge time, 1-10 on one frame, no 'unknown', no precheck
Live check: /goal … | accept: 5: puddles reflect the neon | reference: <robin's image> -- lighting mood -> goal.json step 5 has a frozen checklist, judge.checklist yes/no/unknown, judge.images = frames/contact sheet; a software/unavailable capture gives judge_called false, class environment
Refs #296 — goal: robin's skip of step 4 stored as 'done'; 'skipped' drawn in the running step's amber
Live check: window on localconf/testcases/diorama-test -> step 4 violet, dashed ring, "skipped", counter 4/9; running step amber; same on the phone; /goal redo 4 reopens it
Refs #297 — render_page cannot render under crow-nest serve (0.07-0.5 GiB free vs 512/1536 MiB) and never borrows VRAM from the idle engine: lend around the capture, return in a finally, judge after the return
Live check: serve with crow-nest#117 up, Crow session: render_page on a WebGL page ->
render: {"render_mode": "gpu", ...}naming the NVIDIA renderer; crow.log onerender: lent ... MiB+ onerender: returned ... MiB; engine.log one[lend] lent+ one[lend] returned; the next model step starts without waiting out the TTL; ajudgecall afterwards answers normallyRefs #298 — Crow ships no renderer for the voxel-diorama task: vendor the proven three-gpu-pathtracer recipe as a kit with a skill, build_bundle aliases and an opt-in installer flag
Live check:
install.sh --pathtracer-> kit matches kit.json, skill switched on, esbuild named; goal "path-traced isometric voxel bedroom as one offline index.html like " -> model reads skill voxel-diorama, writes src/scene.js from the scaffold (no own shaders), build_bundle -> index.html, render_page 20 s shows[crow-pt] renderer: ANGLE (NVIDIA ...), samples >= 200; robin judges the 6 M4 items vs the reference; within 2 hRefs #299 — voxel kit v2: animation, orbit, photo mode, props registry, diorama checker -- the kit renders stills only and nothing measures an 'empty' or static diorama
Live check: open the built scaffold index.html -> fills the window sharp at the display's pixel ratio, the island path-traces and converges while the camera rests, the pinwheel turns on top, drag orbits, wheel zooms, P -> photo converges, P -> animation resumes at the paused t;
?mode=rastershows the flat preview; lighthouse goal withcheck: localconf/testcases/diorama-check/check.sh index.html-> exit 0 only on a dense (>= 40 props, >= 12 kinds, >= 60k voxels, coverage >= 0.5), animated, GPU-rendered page; robin judges the photo vs the referencesRefs #301 — read_file dumps an image's bytes as text: the lighthouse run read reference/island.png as 16,000 chars of mojibake, blamed read_image and saved that into MEMORY.md
Live check: goal run whose brief names a reference PNG -> any
read_fileon a .png in session.json is answerederror: … is a PNG image (WxH, N bytes) -- read_file returns text only; use read_image to see itand the next call is read_image on it; no new .crow/MEMORY.md line says read_image returns bytesRefs #302 — render_page cut path-traced pages to 20 s like any other page: the lighthouse night photo stopped at ~295 samples and stayed grainy
Live check: lighthouse goal with
check: localconf/testcases/diorama-check/check.sh index.html-> the photo render_page uses wait_ms 60000-120000, console[crow-pt] samples=>= 1,000 (was ~295), the check's samples row passes at --min-samples 1000, no proposal to shrink the scene for grain; robin judges the night photo's grain vs reference/island.pngRefs #303 — A window started with --root loses it to roots.json 'active' when the last chat is restored: lighthouse-test run bound diorama-test, goal bar gone
Live check: with roots.json
activeon another folder,crow --base-url http://127.0.0.1:8099/v1 --root <lighthouse-test>-> chip reads lighthouse-test, goal bar back (paused at step 2), ~/.config/crow/roots.jsonactive= lighthouse-testRefs #304 — check_diorama.py runs outside a Crow turn, so render_page never borrows VRAM from serve: the lighthouse gate failed all 10 checks at 280 MiB free
Live check: serve with crow-nest#117 up and holding the card,
bash ~/Projects/localconf/testcases/diorama-check/check.sh index.htmlon a built diorama ->gpurow PASS (was FAIL at 280 MiB free); crow.log onerender: lent ... MiB+ onerender: returned ... MiBper capture during the check (was: norender:line)Refs #305 — Every scroll strip in the window and the phone mirror shows its scrollbar thumb at rest: scrollbars should appear only while scrolled or hovered
Live check: window, long chat with a code block: pointer outside -> no thumb in #flow, the stats line, the Code panel; pointer over each -> its thumb shows; PageDown with the pointer on the rail -> chat thumb shows and goes ~0.8 s after the last key; drag a thumb and hold -> stays until release; column does not shift. Phone: no bar at rest, iOS indicator only while swiping, none stuck after a tap
Issues shipped (already closed)
Checks
Head
b3fc1dc(local main; suites run at 01008b6, code unchanged since except tools/test_pathtracer_kit.py re-run at 8d14456; CHANGELOG text after), 2026-09-25, runtime venv (~/.local/share/crow/venv, Python 3.14), Linux:bash install.sh --selftest47 checks, 0 failed2.7.0 — 2026-09-25
Goal runs stop skipping silently, render_page renders on the GPU only, and voxel dioramas get a path-tracing
kit. A failed goal step climbs a classified ladder and then pauses and asks robin; the judge answers a frozen
yes/no/unknown checklist on clock-stepped frames; a skip is never stored as done (#294–#296). render_page drops the
SwiftShader fallback, captures up to 4 frames at controlled page time with pixel prechecks, borrows VRAM from an
idle crow-nest serve around the capture, and lets path-traced kit pages converge for up to 120 s (#293, #297,
#302). The voxel kit vendors three-gpu-pathtracer with a skill, a scaffold, animation, a photo mode, a props
registry and a diorama checker, opt-in with
install.sh --pathtracer/install.ps1 -PathTracer(#298, #299,#304). Also:
read_filerefuses binary files and namesread_image(#301),--rootsurvives the restored chat(#303), and scrollbars show only while used (#305).
Everything on local
mainsince 2.6.0 (21d7505): 24 commits, 12 tickets (#293–#299, #301–#305), 2026-09-25.Most numbers come from the 2026-09-24/25 diorama and lighthouse goal runs. #300 (image generation beside a live
session) is not in this release. Tickets are released pending robin's live check.
Added
Voxel kit: path-traced voxel dioramas as one offline page (Crow ships no renderer for the voxel-diorama task: agents hand-write a WebGL2 ray tracer (16 h of noise, #293-#295) -- vendor the proven three-gpu-pathtracer recipe as a kit with a skill, build_bundle aliases and an opt-in installer flag #298, 2026-09-25).
kits/pathtracer/ships in every install. It holds three 0.186.1 + three-mesh-bvh 0.9.15 + three-gpu-pathtracer 0.0.24 as one esbuild-minified ES module (958,266 bytes; sha256, npm integrity and licence hashes inkit.json; the MIT texts beside it; a NOTICE entry). Next to it:voxel-kit.js, a scaffold, and the skillvoxel-diorama.MeshStandardMaterialper palette colour; a telephoto near-isometric camera with slight depth of field; a dome, a ground plane and a soft key light; lamps with the spot light below a solid shade; emission capped at 1; a despeckle pass; context-loss recovery; andwindow.__SCENE__/__PT__probes.build_bundleresolvescrow-voxel-kitandcrow-pathtracerto the installed kit, so the library never passes through the model.install.sh --pathtracer/install.ps1 -PathTracerverify the bundle, switch the skill on and name the esbuild. A kit skill is seeded once, switched off, through askills/.seededledger: 0 prompt tokens until someone switches it on.Voxel kit v2: animated live preview, photo mode, orbit, props registry, diorama checker (voxel kit v2: animation, orbit, photo mode, props registry, diorama checker -- the kit renders stills only and nothing measures an 'empty' or static diorama #299, 2026-09-25).
?mode=raster; see Changed): a rasterised three.js preview of the same scene and materials, with real lights, soft shadow maps (PCFShadowMap+shadow.radius;PCFSoftShadowMapis gone in three r182+) and the scene'sd.animate((t, dt, scene) => …)every frame. PHOTO mode pauses at t and path-traces as before (despeckle, context-loss restore). Switch with key P or a small Foto/Live button that shows only under the pointer.?mode=photo&t=<s>opens the photo directly,?mode=livethe preview,&ui=0hides the button. Drag orbits, wheel or pinch zooms; in photo mode a camera change restarts the accumulation.d.part(name, grid, {pivot})returns a movableTHREE.Groupbuilt from its own grid;d.addSpot({...});addLamp/addAreaLight/addSpotreturn a light rig the callback can rotate (a lighthouse beam). Time comes only from the page clock, so render_page's 4 frames (render_page silently rasterised on SwiftShader and captured one still: a 16-hour diorama run was judged on 1-spp CPU noise (35 of 42 results software) -- GPU only with a renderer check, deterministic multi-frame capture, pixel prechecks #293) differ and repeat byte-identically.g.prop(name, build)records the name, voxels and bbox of each prop.window.__SCENE__and the console line[crow-scene] {json}reportprops,propKinds,coverage(terrain columns with a prop above their top cell, divided by all terrain columns),parts,animated,modeanderrors. The skill sets the targets: ≥ 40 props of ≥ 12 kinds, ≥ 60,000 voxels, coverage ≥ 0.5.kits/pathtracer/check_diorama.py <index.html>is a goalcheck:command. It renders through Crow's ownrender_pageand prints PASS/FAIL for gpu, probe, samples, props, kinds, voxels, coverage, motion (all 6 frame pairs > 0.5 % changed), repeat, photo precheck and errors; it exits 0 only when all pass.render_page captures frames at controlled page time (render_page silently rasterised on SwiftShader and captured one still: a 16-hour diorama run was judged on 1-spp CPU noise (35 of 42 results software) -- GPU only with a renderer check, deterministic multi-frame capture, pixel prechecks #293, 2026-09-25).
frames(1–4) andframe_ms(16–5000, default 500): with more than one frame the page's clock is frozen from its first script
(
Date,performance.now, timers,requestAnimationFrame, a seededMath.random), and each capture followsexactly
frame_msof page time. Also written: a 2×2 contact sheet at half size,render-<stamp>-sheet.png.No-GPU smoke on Chromium 152 (canvas 2D,
--disable-gpu, 640×400, 4 frames × 250 ms): 1.6 s per call, 4pairwise different frames, a second call byte-identical (sha256). Not measured on the GPU; that is robin's live
check.
A structured render record (render_page silently rasterised on SwiftShader and captured one still: a 16-hour diorama run was judged on 1-spp CPU noise (35 of 42 results software) -- GPU only with a renderer check, deterministic multi-frame capture, pixel prechecks #293). Every capture and every ENVIRONMENT error carries one line,
render: {render_mode, renderer, frames, contact_sheet, precheck}, andcrow_core.last_render()returns thesame dict.
precheckholdsuniform,distinct_colours,one_colour_pct,dark_pctandclipped_pct(lastframe), plus
max_frame_diffandidentical_frames(all frame pairs; a pixel counts as changed when a channelmoves by more than 25, and the frames count as moving from 0.5 % changed).
render_page borrows VRAM from an idle crow-nest serve (render_page cannot render under crow-nest serve: 0.07-0.5 GiB free against the 512/1536 MiB bound, and Crow never borrows VRAM from the idle engine -- lend around the capture (crow-nest#117), return in a finally, judge after the return #297, crow-nest#117, 2026-09-25). Below the VRAM bound,
when this turn's endpoint is local (loopback), render_page asks serve for the shortfall + 256 MiB
(
POST /v1/crow/vram/lend, TTL = the render's own ceiling + 30 s, at most 600 s), reads the free VRAM again, andrenders on the GPU if it now clears the bound. The loan goes back (
POST /v1/crow/vram/return, a 503 retried 3×)in a
finallyafter the browser is closed or killed, on a capture, an error, a timeout or an exception, and beforethe result reaches the model, so
judgeonly ever asks after the return. Still short after the loan: theENVIRONMENT error says lending was tried and how much was lent. llama.cpp or an older serve (404), lending off
(501), a loan already out (409), no answer (then the idempotent return is sent anyway), or a remote endpoint: the
ENVIRONMENT error as before. Each loan and return is a
render:line incrow.logwith MiB and milliseconds.Before: under serve, 0.07–0.5 GiB free (render_page says 'software (swiftshader)' but not why: 49 of 49 captures fell back while serve held the RTX 5090, and the model concluded the machine has no GPU #271, render_page silently rasterised on SwiftShader and captured one still: a 16-hour diorama run was judged on 1-spp CPU noise (35 of 42 results software) -- GPU only with a renderer check, deterministic multi-frame capture, pixel prechecks #293, crow-nest#117) and every capture refused. Tested against a
fake serve only; not measured live.
Frozen per-step checklist for the judge, with yes/no/unknown, frames, a precheck and reference images
(judge: the rubric is picked at judge time and scored 1-10 on one frame with no 'unknown' and no precheck -- steps 5-8 were judged on software frames against model-written criteria; needs a frozen per-step yes/no/unknown checklist, frames, a precheck, and a reference hook #295, 2026-09-25). Each visual step gets a checklist when it starts. It comes from
accept:lines(
accept: 5: <item>binds to step 5), or the model writes it once withgoal_step(n, "running", checklist=[…]),or the first
judgecall writes it. A second write is refused, and a replan keeps it. The judge answers each itemyes/no/unknown with one line of evidence.
doneneeds every must-item "yes" (optional:items do not gate)."unknown" twice on one step pauses the goal. When the render recorded clock-stepped
framesor acontact_sheet, the judge sees them. A capture that is software-rendered on a GPU step, uniform, black, under 16colours, or frozen on a step that needs motion never reaches the judge model: it is an environment failure.
reference: <image> -- <criterion>in/goalsends robin's reference image for a pairwise item. These items areadvisory until calibrated. Before, on 2026-09-25, 8 judge calls on steps 5–8 used criteria the model wrote at
judge time, each scored 1–10 on one software frame. Not measured live.
Step budget and no-progress timer (goal: a step that fails twice is skipped without asking -- 4 of 9 diorama steps auto-skipped on software-GL captures; failures need a class (environment vs capability), a ladder, a budget, and a PAUSE that reports and asks #294). A goal step gets 60 min of active wall clock (
goal_step_minutes,CROW_GOAL_STEP_MINUTES). A step with a checklist pauses after 10 nudges with no item newly passed(
goal_no_progress_turns,CROW_GOAL_NO_PROGRESS_TURNS). The clock stops while the goal is paused. On2026-09-24/25 step 5 carried 8 idle night hours (43,130 s). Not measured live.
Changed
Voxel kit: live mode path-traces the island; a person's page fills the window (voxel kit v2: animation, orbit, photo mode, props registry, diorama checker -- the kit renders stills only and nothing measures an 'empty' or static diorama #299 follow-up, 9e4daa4,
2026-09-25). LIVE (the default) now path-traces the static island, which keeps converging while the camera rests,
and rasterises only the animated parts (
d.part) on top, depth-tested against the island.?mode=raster(aliaspreview) keeps voxel kit v2: animation, orbit, photo mode, props registry, diorama checker -- the kit renders stills only and nothing measures an 'empty' or static diorama #299's flat raster preview, andcheck_diorama.pytakes its motion and repeat captures there,because the accumulating path tracer would make a still scene "move". A page opened by a person (
uinot 0)fills the window at
devicePixelRatio(cap 2) and follows resizes;ui=0(Crow's captures, the checker) keepsthe fixed
width×heightat pixel ratio 1. Before: robin opened the flat raster preview after a 6/6 run and itlooked nothing like the photos, and the fixed 1024×1024 canvas at pixel ratio 1 looked pixelated on his scaled
1440p screen. Node tests for the mode parser and the checker only; the live mode is not measured on the GPU.
Scrollbars show only while their strip is scrolled or pointed at (Every scroll strip in the window and the phone mirror shows its scrollbar thumb at rest (stats line, Code panel, chat): scrollbars should appear only while scrolled or hovered #305, 2026-09-25). Every scroll container in
the window and the phone mirror — the chat, the per-turn stats line (
[24 rounds | … tok/s | prefill …]), codeblocks, tables, the Code/Tool-Calls, git, goal and Subtasks panels, the rail, settings, menus — now draws a
transparent thumb at rest. It appears on any scroll (wheel, keyboard, touch, drag) and goes 0.8 s after the last
scroll event; a held pointer keeps it; a mouse pointer over the strip shows it (
@media (hover:hover), so not ona phone). Only the colour changes: bar widths, the 10 px chat gutter and the column do not move. Before: the thumb
was drawn permanently on every strip. Source and node-run tests only; not yet seen in WebKitGTK, WebView2 or on
iOS (robin's live check).
render_page renders on the GPU only (render_page silently rasterised on SwiftShader and captured one still: a 16-hour diorama run was judged on 1-spp CPU noise (35 of 42 results software) -- GPU only with a renderer check, deterministic multi-frame capture, pixel prechecks #293, 2026-09-25). The SwiftShader fallback is gone. Below the VRAM
bound (512 MiB, or 1,536 MiB with the panel open), or when the browser's own
UNMASKED_RENDERER_WEBGLissoftware (SwiftShader, llvmpipe, lavapipe) or not the NVIDIA card, the result is
error: ENVIRONMENT -- ...naming the free VRAM or the renderer string, and no image comes back. The renderer is read before the page
loads and again after the last capture. Before: in the 2026-09-24/25 diorama run, 35 of the 42 surviving
results were
software (swiftshader)captures taken at 51–317 MiB free. ANGLE runs on Vulkan first, with oneretry on
--use-gl=angle(CROW_RENDER_ANGLEpins one).CROW_RENDER_GL=swiftshaderis now a refusal. Chromium152 still hands WebGL to SwiftShader under
--disable-gpu, even without--enable-unsafe-swiftshader(measured,the smoke above); the renderer check is what caught it. On Windows the renderer cannot be read (no DevTools pipe),
so it says
unverified, andframes> 1 is refused there.render_page lets path-traced kit pages wait up to 120 s (render_page cut path-traced pages to 20 s like any other page: the lighthouse night photo stopped at ~295 samples, stayed grainy, and the goal paused offering to shrink the scene -- kit pages need a longer wait ceiling #302, 2026-09-25). A local page that carries the
voxel kit's
[crow-pt]marker gets await_msceiling of 120,000; every other page keeps 20,000 (render_page's software-WebGL renderer ran away to 54 GiB and froze the machine, and its blank captures left the model grading empty screens - the render subprocess needs a memory ceiling, GPU-when-free, and degenerate-capture detection #213). Thepathtracer skill asks for 60,000–120,000 on night or dark scenes and says never to shrink the scene to fit a short
capture. Before: the 2026-09-25 lighthouse goal run capped every photo at 20 s, which held ~295 samples (291.5,
295.5, 298), stayed grainy, and paused step 2 with an offer to shrink the scene. At the measured ~15 samples/s at
1024² (RTX 5090), 90 s holds ~1,350 samples. Not measured live.
A failed goal step is never skipped by the engine; it climbs a ladder and then pauses and asks (goal: a step that fails twice is skipped without asking -- 4 of 9 diorama steps auto-skipped on software-GL captures; failures need a class (environment vs capability), a ladder, a budget, and a PAUSE that reports and asks #294,
2026-09-25, replaces goal: a 'failed' step is sent back as the next step forever (step 4 looped ~40 min after the model failed it honestly), and robin has no /goal skip #289's skip). Each failure has a class. Environment (render_mode unavailable, software on a
GPU step, blank or identical frames, no judge answered): two retries 15 s apart, then pause. Capability (a valid
capture the judge does not pass): a written reflection, then attempt 3 in a fresh context, then a split into 2–3
sub-steps (
goal_step(n, "split", substeps=[…]), reported withsub), then pause. The 25-turn and 60-turn capsand the budget pause too. On every pause the model writes robin a report: what it found, why it cannot go on,
2–3 proposals, and whether he has more input. The report is shown in the window and on the phone. Only a typed
line resumes. Before: the 2026-09-24/25 diorama run ended "complete with 4 skipped", with steps 5–8 skipped on
software-GL captures and nobody asked. After: 0 skips without
/goal skipin the unit replay. Not measured live.Fixed
done, and skipped drawn in the running step's amber (goal: robin's skip of step 4 is stored as 'done' (counted 5/9), and the bar draws 'skipped' in the same amber as 'running' #296). Adonestep whose note startswith "skipped" loads as
skipped, which repairs the 2026-09-24 hand edit of step 4 (counted 5/9, now 4/9). Themodel can no longer set a user-skipped step
doneorrunning;/goal redo <n>reopens it. In the goal bar(window and phone), skipped has its own colour (
--skip, violet), a dashed ring with a skip glyph and the word"skipped". Running keeps amber with a dot. A paused step shows a red pause glyph, and the head reads
"Paused · step N".
read_filerefuses images and other binary files, and namesread_image(read_file dumps an image's bytes as text: the lighthouse run read reference/island.png as 16,000 chars of mojibake, blamed read_image and saved that into MEMORY.md -- refuse binary files and name read_image #301, 2026-09-25). A file whosefirst 8,000 bytes hold a NUL, or that starts with a PNG/JPEG/GIF/WebP/BMP/PDF signature, is detected by its
content, not its extension. It gets one line instead of bytes decoded as text:
error: …/island.png is a PNG image (543x768, 502,797 bytes) -- read_file returns text only; use read_image to see it. A PDF namespdftotext, otherbinaries
file/xxd. Before: the 2026-09-25 lighthouse run got 16,056 characters (~5,946 tokens) of mojibake forreference/island.png, blamedread_imageand saved "read_image returns raw bytes" into.crow/MEMORY.md.UTF-8 text reads as before, including a multi-byte character at the 8,000-byte edge.
read_imageon a missing-crop.pngwhose frame exists now says a crop is written only under 50 % coverage and names the frame. Before: abare
no such image, twice in the same run. Not measured live.--rootkeeps that folder when it restores the last chat (A window started with --root loses it to roots.json 'active' when the last chat is restored: lighthouse-test run bound diorama-test, goal bar gone #303, 2026-09-25).crow --root DIRnow binds DIR for the restored chat and saves DIR as the folder the next start opens (activeinroots.json), the same as a pick in the chip. Before: on 2026-09-25 15:19,--root …/lighthouse-testrestored thechat "Voxel" (no folder of its own) into
diorama-test, the last folder picked in the chip (09:45). The goal bardisappeared, and the next line ran in the wrong folder. A restored chat that recorded another folder is moved to DIR:
the model gets A working-area rebind is silent to the model #224's notice, and the memory head is rebuilt only if it named the other folder. Without
--root,nothing changes. The terminal already let
--rootwin. Covered by 4 window tests, 2 of them red without the fix;not verified live.
check_diorama.pyborrows VRAM from serve like a Crow turn does (check_diorama.py runs outside a Crow turn, so render_page never borrows VRAM from serve: the lighthouse gate failed all 10 checks at 280 MiB free #304, 2026-09-25). The checker runs as itsown process, outside any Crow turn, so render_page cannot render under crow-nest serve: 0.07-0.5 GiB free against the 512/1536 MiB bound, and Crow never borrows VRAM from the idle engine -- lend around the capture (crow-nest#117), return in a finally, judge after the return #297's lending had no endpoint and never asked serve. It now names the local
serve before the first capture:
--serve(defaulthttp://127.0.0.1:8099/v1, envCROW_SERVE_URL,none= neverborrow). Before: on 2026-09-25 ~15:35 the lighthouse goal's
check.shfailed all 10 checks with the ENVIRONMENTerror at 280 MiB free while serve held the card, and
crow.loghad norender:line for it; render_page inside thesame turn had borrowed 582 MiB twice minutes earlier. Covered by 3 tests, red without the fix; not verified live.
Known limitations
reference-grade stills: proven with a hand-built scene (2026-09-25, RTX 5090). In the 2026-09-25
lighthouse goal run the agent-built diorama stayed far below robin's reference images: crude props, a flat
pond, and a lighthouse beam that read as a windmill. robin did not accept the result.
check_diorama.pyand the judge'schecklist measure the GPU, sample counts, prop counts and kinds, voxels, coverage, motion, repeatability, a
near-uniform/near-black precheck and page errors. A page can pass every gate and still look nothing like the
reference;
reference:items are advisory until calibrated, and--referenceonly prints luma/saturation.serve and GPU smokes of the scaffold only. The lend (render_page cannot render under crow-nest serve: 0.07-0.5 GiB free against the 512/1536 MiB bound, and Crow never borrows VRAM from the idle engine -- lend around the capture (crow-nest#117), return in a finally, judge after the return #297, check_diorama.py runs outside a Crow turn, so render_page never borrows VRAM from serve: the lighthouse gate failed all 10 checks at 280 MiB free #304) was tested against a fake serve; the path-traced
live mode (9e4daa4) and the 120 s wait (render_page cut path-traced pages to 20 s like any other page: the lighthouse night photo stopped at ~295 samples, stayed grainy, and the goal paused offering to shrink the scene -- kit pages need a longer wait ceiling #302) were not run on the GPU for this release.
unverifiedand refusesframes> 1 (render_page silently rasterised on SwiftShader and captured one still: a 16-hour diorama run was judged on 1-spp CPU noise (35 of 42 results software) -- GPU only with a renderer check, deterministic multi-frame capture, pixel prechecks #293).install.ps1 -PathTracer,install.ps1 -Selftestandtools/pack-release.ps1were not runfor this release (no PowerShell on the Linux release machine).
check_gui_prereqs(not in CI) reports 2 of 3 prerequisites: point (ii), 26 glyph problems over the 2 shippedGoogle Sans Code faces, unchanged since 2.6.0.
Windows installer bundle (CrowSetup.exe: full-stack installer (Crow + crow-nest + CNQ) #196);
sampling_no_thinking's presence_penalty 1.5 (presence_penalty 1.5 in the unused no-thinking sampling row #246).🤖 Generated with Claude Code
https://claude.ai/code/session_01LyBV1tX2JWocGLTjFkNYMh