change: Implement 4: Filter candidates by real format and minimum size from downloaded bytes #14
Merged
ai-reviewer
merged 2 commits from 2026-11-03 02:21:27 +00:00
change/6-implement-4-filter-candidates-by-real-format-and-minimum-size- into task/1-task
Labels
Clear labels
auto:allowed
claim:active
needs:architecture
role:architect
role:developer
role:product-manager
role:reviewer
role:team-lead
role:tester
status:accepted
status:blocked
status:done
status:draft-acceptance
status:draft-development
status:idea
status:in-architecture
status:in-development
status:in-lead
status:in-review
status:in-test
status:needs-development
status:needs-test
status:proposed
status:ready-for-architecture
status:ready-for-development
status:ready-for-lead
status:ready-for-test
status:waiting-for-acceptance
status:waiting-for-dependency
type:bug
Autonomous agents may claim this issue when role and status labels match.
A non-expired agent claim exists in issue or pull request comments.
Requires architecture planning before implementation.
Turns architecture-marked proposals into ADR-like plans and lead-ready follow-up work.
Implements code and tests, opens PRs, and fixes CI failures.
Creates product proposals from ideas and feedback.
Reviews diffs, tests, CI reports, and returns or accepts work.
Clarifies scope, decomposes tasks, and gates development readiness.
Runs final product acceptance and creates developer bug follow-ups.
Work passed review and can be reported or merged by the outer workflow.
Automation cannot continue without human input.
Workflow item is complete.
Teamlead-agent has created a final acceptance task draft that is not claimable yet.
Teamlead-agent has created a developer task draft that is not claimable yet.
Raw user idea or feedback that has not been shaped yet.
Architect is writing an ADR-like plan or lead follow-up.
Developer is implementing or fixing the task.
Teamlead-agent is decomposing architecture into developer work.
PR is ready for review after CI/deploy feedback.
Tester-agent is running final product acceptance.
Review or CI found issues that must return to development.
Work needs final acceptance testing or retesting.
Product proposal exists, but it is not ready for development.
Proposal needs architecture review before team lead planning.
Task is scoped and may be assigned to a developer.
Proposal is ready for technical planning.
Merged product work is ready for final tester acceptance.
Final acceptance is waiting for all implementation dependencies to complete.
Task is scoped but cannot be claimed until prerequisite child tasks are complete.
Broken or incorrect behavior.
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: projects/product-54354138-1#14
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Added tools/images.py with stdlib-only detection of the real image format and pixel dimensions from downloaded bytes (PNG signature + IHDR chunk, ICO ICONDIR/ICONDIRENTRY directory including the 0-means-256 encoding) plus stdlib-only builders (png_bytes, ico_bytes) so tests/sources can produce genuine payloads. Wired it into the CLI search path in tools/example.py: a bounded candidate window derived from --count (candidate_cap = count * 4, capped at 50) is fetched from the selected source, each payload is parsed, and only items whose actual format equals the requested png/ico and whose actual width and height are both at least the requested minimum are kept. Kept items report source, url, format and the actual width/height (plus size_bytes and metadata as extras); verified bytes are reused for --download instead of being fetched twice. Statuses stay distinct: ok (0), no_format (3) when hits were only other formats, no_size (4) when format-matching items were all too small, access_error (5) when bytes could not be fetched or are not readable images, partial_download (6) when some candidates could not be read. All metadata-based filtering (FORMAT_MATCHERS, _matches_format, _matches_size) was removed so only parsed bytes decide. Added tests/test_images.py with detector/builder unit tests and CLI tests using an injected fake byte-fetch callable (no network), and updated tests/test_cli.py fixtures to real image bytes where the previous metadata-only assumption no longer holds.
Tests:
Facade commands run with .venv/bin on PATH after prepare_environment(component=tools, mode=restore):
python -m compileall tools tests(tools.validate) succeeded;pytest(tools.test) => 114 passed, including the new tests/test_images.py (31 tests);python -m tools.example examples/input.json(tools.smoke) exited 0 with a single dry_run JSON document; acceptance commandspython -m tools.example --helpexited 0 andimport toolsresolved. Target branch task/1-task is at the same commit as this work branch (git rev-list count 0 ahead / 0 behind), so it is already integrated; facade/protected paths untouched.Progress:
Implemented byte-based format and minimum-size filtering for the image search path. New tools/images.py: detect_format (png/ico plus jpeg/gif/bmp/webp so "other format" is distinguishable from "unreadable"), read_png_dimensions (IHDR), read_ico_dimensions (ICONDIR entries, 0 => 256, largest entry), inspect_image/ImageInfo, ImageFormatError (code image_unreadable), and png_bytes/ico_bytes builders. tools/example.py: candidate_cap(count) bounded window (count*4, max 50) requested from the source, _select() fetches and parses every candidate, KeptImage.to_item() reports source/url/format/actual width+height/size_bytes, _download() writes the already verified bytes, and _execute() distinguishes ok/no_format/no_size/access_error/partial_download with the existing exit codes; metadata-based filtering removed. tests/test_images.py added (31 tests) and tests/test_cli.py fixtures aligned to real image bytes. All facade commands pass: compileall, pytest (114 passed), smoke, help, package import.
Published by developer-agent through Tool Backend.
Closes #6
Developer-agent completed implementation and CI passed, but updating the PR title/body failed.
Intended title: change: Implement 4: Filter candidates by real format and minimum size from downloaded bytes
Commit:
b544968cb8CI: CI passed.
Intended body:
Added tools/images.py with stdlib-only detection of the real image format and pixel dimensions from downloaded bytes (PNG signature + IHDR chunk, ICO ICONDIR/ICONDIRENTRY directory including the 0-means-256 encoding) plus stdlib-only builders (png_bytes, ico_bytes) so tests/sources can produce genuine payloads. Wired it into the CLI search path in tools/example.py: a bounded candidate window derived from --count (candidate_cap = count * 4, capped at 50) is fetched from the selected source, each payload is parsed, and only items whose actual format equals the requested png/ico and whose actual width and height are both at least the requested minimum are kept. Kept items report source, url, format and the actual width/height (plus size_bytes and metadata as extras); verified bytes are reused for --download instead of being fetched twice. Statuses stay distinct: ok (0), no_format (3) when hits were only other formats, no_size (4) when format-matching items were all too small, access_error (5) when bytes could not be fetched or are not readable images, partial_download (6) when some candidates could not be read. All metadata-based filtering (FORMAT_MATCHERS, _matches_format, _matches_size) was removed so only parsed bytes decide. Added tests/test_images.py with detector/builder unit tests and CLI tests using an injected fake byte-fetch callable (no network), and updated tests/test_cli.py fixtures to real image bytes where the previous metadata-only assumption no longer holds.
Tests:
Facade commands run with .venv/bin on PATH after prepare_environment(component=tools, mode=restore):
python -m compileall tools tests(tools.validate) succeeded;pytest(tools.test) => 114 passed, including the new tests/test_images.py (31 tests);python -m tools.example examples/input.json(tools.smoke) exited 0 with a single dry_run JSON document; acceptance commandspython -m tools.example --helpexited 0 andimport toolsresolved. Target branch task/1-task is at the same commit as this work branch (git rev-list count 0 ahead / 0 behind), so it is already integrated; facade/protected paths untouched.Published by developer-agent through Tool Backend.
The declared acceptance checks do pass (tools.validate/test/smoke and tests/test_images.py green on
b544968), and the byte-based PNG/ICO detection and minimum-size filtering are implemented well. However the classification of non-PNG/ICO hits is too narrow for the approved live source, and it turns common, fully working searches intopartial_download(exit 6) andno_format/no_sizeoutcomes intoaccess_error(exit 5). That regresses REQ-007/REQ-008/AC-004/AC-006 behavior rather than just adding the requested feature.Findings:
tools/images.detect_formatonly recognises png/ico/jpeg/gif/bmp/webp (evidence-3), so any other real image served by the approved source is treated as unreadable in_selectand appended tofailures(evidence-6). Wikimedia Commons search is not format-filtered (gsrsearchover namespace 6, tools/sources.py:370-423, evidence-15) and the repository's own Commons fixture already contains animage/svg+xmlhit (tests/test_sources.py:117-130, evidence-25); TIFF/SVG/PDF bodies are therefore routine. Consequence 1: a normal png search that keeps real pngs and also hits one SVG returnspartial_download/exit 6 instead ofok/exit 0 (tools/example.py:936-951, evidence-9), so the success path regresses on the live source. Consequence 2: an ico request whose hits are svg+png returnsaccess_error/candidate_unreadable(tools/example.py:899-917, evidence-8) instead of the explicit no-format message required by REQ-007/AC-004, and a topic with only too-small pngs plus one SVG returns access_error instead ofno_size(REQ-008/AC-006).test_ico_request_with_only_png_payloads_yields_no_formatandtest_no_format_and_no_size_and_access_error_stay_distinct(evidence-11, evidence-23) only use recognised other formats, so this path is untested. Fix by recognising the other container families the approved hosts serve (svg/tiff/pdf/...) or by classifying any parseable image body that is not the requested format as a format rejection, and add tests for (a) png hit + svg hit => ok, (b) ico request with an svg hit => no_format, (c) genuinely non-image/HTML body => access_error.STATUS_PARTIAL_DOWNLOAD(exit 6) when candidates merely failed to parse (tools/example.py:936-951, evidence-9), while the accepted status vocabulary documents that status as 'Some downloads succeeded and some failed' (tools/envelope.py:55-63, evidence-7). No test covers this branch. Either keepokplus a warnings/notes field for non-fatal unreadable candidates, or revise the documented vocabulary through the proper change path instead of redefining it inside a feature PR.details["format_matches"]is filled withselection.size_rejects(tools/example.py:899-909, evidence-6/evidence-9) and the new tests assert that value (evidence-11:328, evidence-23), so the field name misreports what it counts; consider renaming tosize_rejected/ reporting the real format-match count.Developer-agent completed implementation and CI passed, but updating the PR title/body failed.
Intended title: change: Implement 4: Filter candidates by real format and minimum size from downloaded bytes
Commit:
d5bb24fc01CI: CI passed.
Intended body:
Fix for review-fix on issue #6: non-PNG/ICO hits in the bounded candidate window are now classified as format mismatches instead of access problems, so working searches no longer degrade to
partial_download(exit 6) oraccess_error(exit 5).Changes:
tools/images.py:detect_formatnow also names the formats a live image source commonly serves instead (svg, tiff, pdf) alongside png/ico/jpeg/gif/bmp/webp, so "the topic has images, but not in the requested format" is describable;ImageFormatErroris documented as reserved for genuinely unusable bytes (unknown container, or a png/ico header that is truncated/corrupt).tools/example.py:_select()counts any payload whose detected format differs from the request (including payloads that are not a recognised image at all, e.g. an HTML error page) as a format rejection and recordsobserved_formats; only an unfetchable candidate is a failure.Selectiongainedother_formats._execute()now returnsno_sizewhen format-matching items were all too small,no_formatwhen the topic only offered other formats (or no hits at all), andaccess_erroronly when not a single candidate could be fetched; a stray unfetchable candidate no longer demotes a successful search topartial_download. Status/exit-code vocabulary (ok/no_format3/no_size4/access_error5/partial_download6) is unchanged.tests/test_images.py: added svg/tiff/pdf fixtures and detection cases, plus regression tests for the reviewed behaviour — mixed-format window stillok(exit 0), ico request with only foreign formats yieldsno_format(exit 3) withobserved_formats, too-small format matches still yieldno_size(exit 4), non-image body yieldsno_formatnotaccess_error, unfetchable candidate does not demote a usable search, corrupt png does not hide a valid match, empty window isno_format, fetch failure stillaccess_error.tests/test_cli.py: the former "unreadable payload is an access error" test now assertsno_formatfor a non-image body, matching the classification.Filtering still uses only the parsed downloaded bytes (PNG IHDR / ICO directory) and never source-reported metadata, and the candidate cap derived from
--countis unchanged.Tests:
Ran the facade commands from /workspace with the prepared virtualenv on PATH:
python -m compileall tools tests(validate, ok),python -m pytest(test, 129 passed),python -m tools.example examples/input.json(smoke, exit 0, dry_run envelope) andpython -m tools.example --help(acceptance, exit 0). Dependencies restored via prepare_environment (component tools, mode restore). Target branch origin/task/1-task is already an ancestor of the work branch HEAD (verified with git merge-base), so no extra integration commit was needed; the checks ran on the integrated revision.Published by developer-agent through Tool Backend.
Approved. Byte-based format and minimum-size filtering is implemented as specified and the earlier changes-requested findings are fixed. On exact head
d5bb24fc01the facade commands ran in CI: tools.validate compiled tools/images.py, tools.test passed with 129 tests including tests/test_images.py, tools.smoke emitted the dry-run envelope and acceptance.help-command exited 0 (AGENT_CI_RESULT records tools.validate/tools.test/tools.smoke all status=passed). Filtering uses only parsed bytes (PNG IHDR / ICO ICONDIR with 0=>256), different-format and unrecognised payloads are format rejections rather than access errors, no_size/no_format/access_error stay distinct, and verified bytes are reused for --download. Protected facade paths and dependency manifests are untouched.Findings:
Acceptance evidence for
d5bb24fc01c12b9a79ff7db3681db32f5f7da9f9: