Repository navigation
Foundation: image-aware scaffold, tools/new_recipe.py --mode replay-images (Refs #315) - #443
Draft
CompleteDotTech wants to merge 2 commits into
Draft
CompleteDotTech wants to merge 2 commits into
CompleteDotTech wants to merge 2 commits into
Conversation
…mages (Refs #315) Slice 2 of #315: the scaffolder items only. `python tools/new_recipe.py NN --mode replay-images` scaffolds a replay recipe whose requests carry images. After writing the replay files it runs the new folder's own build_fixtures.py once, which draws 14 example images with jev_cookbook.synthetic_images, writes assets/ and assets-manifest.json (origin generated, licence, attestations) and a complete fixtures/ whose Box labels are derived from the drawn geometry. helpers.py loads each image with image_asset(path, root=ROOT), derives image_reference and keys the replay with replay_key(state, questions, assets=assets). The notebook cells are generated from Python source in the tool: one image answer up close, draw_boxes, the presence/review rule (locate) and localization_report. request_budget.json declares the 14 requests and the two image quantities of docs/backends.md, so the scaffold passes the request audit with no hand edit. tests/test_build_fixtures.py for an image recipe is a variant that also copies assets/ into the temporary folder it rebuilds in, so the committed picture bytes (part of every replay key that carries a picture) are compared, not regenerated. build_fixtures_test_text(recipe) still returns the text variant; build_fixtures_test_text(recipe, "replay-images") returns the image one. The text scaffolds are byte-unchanged: all 100 catalog slugs in both existing modes were scaffolded before and after and the trees compared. MODES keeps its two text values; ALL_MODES adds the image mode. Tests: tests/test_new_recipe.py and the template/scaffolder agreement tests cover the image variant. Deferred to other slices: README/backends/fixtures links, the experimental label, docs/live.md, the docs-link test, a frame variant, and the recipe guard's byte comparison of test_build_fixtures.py for a recipe that has assets/. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…clearly invented (Refs #315) Reading the product names that login_screen draws for the first set of seeds showed two that match real brands (MAFEX and ROMER), and several short ones that could. The manifest of an image recipe attests real_brand false, and docs/synthetic-images.md asks the author to read every drawn name, so the example uses other seeds: v04, t02, t03, t04, t06, t07 and d01 change (14 -> 17, 22 -> 32, 23 -> 34, 24 -> 35, 26 -> 48, 27 -> 54, 31 -> 60), giving longer names that were read by eye (this is not a trademark search). Only the drawing seed changes. The screen states, the gold boxes, the stored answers and every number the notebook prints are unchanged, so the tests' hand-computed values still hold; the one test that edits a row by its text follows the new seed of t07. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
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.
Refs #315 (slice 2 of 2: the scaffolder items). No closing keyword: the docs items are a separate slice.
python tools/new_recipe.py NN --mode replay-imagesscaffolds a replay recipe whose requests carry images. This is a shared interface (the scaffold every multimodal recipe starts from), so it needs two independent reviews.What changed, per item of #315
--imagesor--mode replay-images--mode replay-images(see "Why--mode")tools/new_recipe.pyassets/with generated example images and a manifestassets/screen-<id>.png) andassets-manifest.json, written by the new folder's ownbuild_fixtures.py, which the scaffolder runs once after writing the filestools/new_recipe.pybuild_fixtures.pyusing #314jev_cookbook.synthetic_images.login_screen, saves withcanvas.write(the byte policy), writes the manifest withcanvas.manifest_entry, derives each Box label fromDrawn.label();--forceas in the text scaffold,--overwrite-assetsaccepts a changed picturetools/new_recipe.pyimage_asset(root=...),image_reference,replay_key(..., assets=...)build_request(fields)returns(state, assets); the reference sits once in the statetools/new_recipe.pydraw_boxes, the presence/review rule,localization_reportimage_notebook_cells), no.ipynbedited by hand. Not executed here (see "Not verified")tools/new_recipe.pyrequest_budget.jsonandAuditedBackendroute, including the image profile of docs/backends.mdrequests: 14,profile: {"images": 14, "distinct_images": 14}; setup cell isAuditedBackend(get_backend(fixtures=responses_path(), images=True), label="main"); final cell printsbackend.audit_json(); README paragraph rendered from the declarationtools/new_recipe.pyside_effects.jsonas the template requiresnoneplaceholder (with itsTODOreason) as the text scaffoldstools/new_recipe.pytests/test_build_fixtures.pymust pass unchanged withassets/presenttest_build_fixtures.pyitem")tools/new_recipe.pyvalidate, the asset check, hygiene and its own tests offlinetests/test_new_recipe.pyand the agreement tests cover the image varianttests/test_new_recipe.py,tests/test_template_scaffolder_agreement.pydocs/recipe-template.mdlinksdocs/vision.mdassets/row already linkedvision.mdonmain(a search on this branch's base, not an assumption); this change adds the## Image recipessection, which links it again, and documents the scaffolddocs/recipe-template.mdWhy
--mode--modeis the existing option that selects a variant of the scaffold, so the image variant is one more value of it. A separate--imagesflag could be combined with--mode scripted; no value of--modecan ask for an image recipe that is not a replay recipe.MODESkeeps its two text values (the tests and the recipe guard iterate over it, and the guard'sscaffolder.MODESloop is unchanged);ALL_MODESaddsreplay-imagesand is what--modeandscaffold()accept.What the image scaffold is
A working example, not a skeleton: invented sign-in windows (
login_screen), two questions (present, aNoul: is an error banner shown;location, aBox: where), 6validation, 7testand 1demoexample, a rulelocatethat holds whatever the model answers, and stored answers that are synthetic and deliberately imperfect.TODOmarkers say what to replace.tools/new_recipe.pyitself stays standard library only; the one step that draws the images needs the installed package and Pillow, and if it fails the folder is removed and the command exits 1 (tested).The
test_build_fixtures.pyitemThe comment predicted
FileNotFoundErroronassets/<file>for the text variant. Executed on this scaffold, that is not what happens: thisbuild_fixtures.pywrites the images before it reads them, so the text variant (which copies only the two scripts) passes in the environment that wrote the committed files. What it cannot survive is a different PNG encoder: the committed bytes are part of every replay key that carries a picture, and a rerun that draws the pictures afresh disagrees with the committed fixtures although every pixel is the same. Executed table (asitecustomizethat re-encodes every PNG withcompress_level=1, same pixels, different bytes, and says the environment is not canonical):assets/)build_fixtures_test_text(recipe))test_the_generator_reproduces_the_committed_fixtures), 2 passedSo the scaffold emits a variant,
build_fixtures_test_text(recipe, "replay-images"), that also copiesassets/and adds two checks (images and manifest come back unchanged; a changed picture is refused, not rewritten).build_fixtures_test_text(recipe)with one argument still returns the text variant. A test pins the encoder contrast (and a mutation that drops the copy is caught).Decided limit, for the owner of the recipe guard:
tests/test_recipe_guard.py(open PR #365, so not touched here) compares every replay recipe'stests/test_build_fixtures.pybyte for byte withbuild_fixtures_test_text(recipe), one argument. A published image recipe would be compared with the text variant and fail that check. The one-line follow-up there is to pass"replay-images"when the recipe hasassets/.Checks run (executed on this host, Windows 11, Python 3.14.3)
--mode replayand--mode scriptedfromorigin/main'stools/new_recipe.pyand from this branch's,diff -ron the output trees: identical (800 and 700 files).python -m jev_cookbook.fixtures validate->fixtures valid (mode replay);tools/check_recipe_assets.pyon a git tree holding it -> passed (26 files);tools/check_hygiene.pyon its 26 files -> passed;ruff checkandruff format --check(from the repo root, as CI runs them, ranks 61, 77 and 100) -> passed; its ownpytest tests-> 15 passed.tests/request_audit.audit_recipeover the scaffold with the audit line produced by replaying the notebook'sdecide()calls in plain Python (the demo example, thenvalidation, thentest; not a kernel run): returns 14, which equals the declared count, the two image quantities and the rendered README paragraph. Mutations of the declaration (one request fewer, no profile,imagesordistinct_imagesoff by one, a README paragraph that is not the rendered one) each fail.locate, input -> outcome, executed: p 0.90, box 0.90, thresholds 0.30/0.80 -> located; p 0.10 -> absent; p 0.40 (presence confidence 0.20 < 0.30) -> review; p 0.80 with threshold 0.70 (confidence 0.60) -> review, with 0.60 -> located; p exactly 0.5 -> absent; box confidence None -> review; 0.50 < 0.80 -> review; 0.80 == 0.80 -> located; a threshold outside [0, 1] ->ValueError. A grid of 101 probabilities by 5 thresholds shows a review is never an absence.images=Trueoffline: replay with and without it gives identical results for all 14 examples (executed), so the option changes nothing offline. That it enables the images header in a live run is from docs/backends.md (read, not executed).tests/test_new_recipe.py(the 70 tests that do not reach the notebook executor, 85 items with parametrization) andtests/test_template_scaffolder_agreement.py(20 items), plustest_run_mode_sentence,test_no_private_shared_names,test_recipe_sources,test_docs_examples,test_docs_ruff_version,test_check_recipe_assetsand the scaffolder-dependent tests oftest_template.pyandtest_recipe_guard.py: 618 passed, 7 skipped (-rs: 6 intest_run_mode_sentencefor its closed legacy list, 1 intest_recipe_guardfor a scripted recipe, which has notest_build_fixtures.pyto guard). The collected ids were listed and none of the 51 tests that reach the executor was among them. A second run of neighbouring modules (test_pytest_config,test_catalog_shape,test_fixtures_assets,test_synthetic_images, the docs and asset-checker modules): 1121 passed, 16 skipped (-rs: no symbolic links on this platform, POSIX file modes, and the byte pins, which run only under the pinned Pillow 12.3.0 with a classic zlib).origin/main'stools/new_recipe.py(with the module'sALL_MODESandIMAGE_MODEreferences made tolerant so it collects): 5 failed, 18 errors (the fixture cannot scaffold the mode) and 5 passed (the text-mode regression guards, which are meant to pass on both); the agreement module: 3 failed, 17 passed onmain, 20 passed here. The image rows of the measured-cell comparison pass onmaintoo, becausemain's generator treats any mode that is notscriptedas replay.distinct_imagesoff by one, a row dropped, the folder kept after a failed drawing step, the drawing step skipped,assets=dropped from adecide,images=Truedropped,selectiondropped,assets/no longer copied by the test, a changed picture always overwritten, 0.5 treated as a yes, labels typed instead of drawn, the text variant replaced by the image one, the imagetest_build_fixtures.pynot written,MODESgrowing the image mode, an images mention in the text scaffold, the rectangle gate>instead of>=, the demo example labelled.ruff checkandruff format --checkon the three changed Python files,tools/check_hygiene.py(635 files),tools/check_recipe_assets.py(462 files),tools/render_catalog.py --check,git diff --check: all clean. ruff is 0.15.8 locally;pyproject.tomlpins 0.17.2.Not verified
state,answer-boxes,python-rule,evaluation,evaluation-review) are verified by parsing (ast), ruff, a plain-Python replay of their logic, and calls to the same APIs, not by running them. The newtest_a_fresh_image_scaffold_passes_the_request_audit_with_no_hand_editexecutes the notebook with a kernel, like its text counterpart, and has not been run; neither has any of the 51 existing tests that reach the executor.pytest, ruff 0.17.2, the Python 3.10 leg, and the byte pins under the notebook constraints (Pillow here is 13.0.0 with a zlib-ng build, not the pinned 12.3.0, so the PNG bytes this host writes are not the canonical ones; the committed ones of a real recipe must be recreated under the pin, which the READMETODOsays).Deferred (recorded, not silently dropped)
docs/backends.mdanddocs/fixtures.mdlinks, the experimental label,docs/live.mdand the docs-link test: the docs slice (PR Foundation: link the multimodal docs, label Images and Box experimental, correct docs/live.md (Refs #315) #442, read 2026-10-31).scripted-images) and a siblingrecipes/_templateimage template (not needed: everything is generated fromtools/new_recipe.py).tests/test_build_fixtures.py(above).tests/test_no_private_shared_names.pywalks only the two text modes and is outside this change's allowed files; the newtest_the_image_scaffold_emits_no_private_shared_nameruns the same detector over the image scaffold.Prior work
Searched issues and pull requests before drafting (
gh api search/issuesfor the scaffolder,replay-images, image scaffold,test_build_fixturesand assets; the contracts below were read). No earlier change adds an image path to the scaffolder.request_budget.json/AuditedBackendroute the image scaffold follows; Foundation: declare a recipe's live request count once and audit it against the executed notebook (#213) #361 (the declaration and audit).localization_report), Foundation: shared synthetic image generator with gold boxes and a byte-stability policy (#314) #349 and Foundation: synthetic image follow-ups, Cyrillic and Greek glyphs, rank 80 text (Refs #357) #386 (Foundation: no shared way to generate synthetic test images, so each of forty recipes will hand-roll one, or reach for real photos, brands and screenshots #314 and Catalog rank 80 names a real game; rank 96 needs a non-Latin text decision; synthetic_images follow-ups (#314 reviews) #357, the synthetic image generator and its byte policy), Foundation: deterministic helpers and evaluation for ordered frames (#309) #323 and Foundation: frames follow-ups: clustered-frame tie-break, stride_seconds rule, transition label format (Refs #340) #393 (Foundation: no deterministic helpers or evaluation for ordered frames (sampling, intervals, relative times, windows, transitions) #309, frames), Foundation: account for image copies and bytes, preflight the aggregate cap (#313) #372 (Foundation: image requests are not accounted for: repeated and instruction-embedded images multiply silently and the 32 MiB aggregate caps are never checked #313, image copies and the profile): the contracts the scaffold satisfies.test_build_fixtures.pyitem.README.md,docs/backends.md,docs/live.mdandtests/test_docs_links.py, no file of this change. Open pull requests' file sets were re-read before pushing: no overlap.Generated with Claude Code