Commit Graph
5 Commits
Author SHA1 Message Date
Tool Backend b544968cb8 change: Implement 4: Filter candidates by real format and minimum size from downloaded bytes
Repository Validate / repository-checks (pull_request) Successful in 3s
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 commands `python -m tools.example --help` exited 0 and `import tools` resolved. 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
2026-11-03 02:11:36 +00:00
Tool Backend 6a7538c07f change: Implement 3: Complete the native-process CLI, JSON request handling, status vocabulary and exit codes
Repository Validate / repository-checks (pull_request) Successful in 3s
Review fix for issue #5: aligned the public CLI's size/resize semantics with the active product obligations instead of the previous exact-match behaviour.

- `tools/example.py`: the requested width/height are now a target (minimum) size, never an exact-match filter: any image at least that large is selected regardless of `--resize`, and smaller images still produce `no_size`. `--resize` no longer widens selection; it now performs real downscaling of downloaded images to a shrink-to-fit box (aspect ratio preserved, never upscaled), implemented with a dependency-free stdlib PNG codec plus ICO container support (PNG-compressed ICO entries), and each item records `from`/`to`/`applied` dimensions. Undecodable payloads raise the new typed `ImageResizeError` and are reported as failed downloads, so the status vocabulary and exit codes stay unchanged (`ok`/`dry_run` 0, `invalid_request` 2, `no_format` 3, `no_size` 4, `access_error` 5, `partial_download` 6). Help text and the `no_size` error details were updated to describe the minimum-size contract.
- `tools/errors.py`: added `ImageResizeError` (code `resize_failed`) to the typed hierarchy and `__all__`.
- `tests/test_cli.py`: replaced the old "exact size unless --resize" test with minimum-size tests (larger image accepted without `--resize`, exact size accepted, `--resize` without `--download` keeps the reported source size) and added offline fixtures/tests for real downscaling: PNG stored at 100x50 for a 100x100 box, original bytes preserved without `--resize`, no upscaling (`already_fits`), PNG-in-ICO downscaling, and `resize_failed` surfacing as `access_error`.
- `examples/input.json` remains the canonical offline `dry_run` request, so the smoke command stays green and network-free.

No protected facade/control paths were touched and no dependency declarations changed.

Tests:
Ran the facade commands on the final revision through the prepared environment (`.venv` on PATH): `python -m compileall tools tests` → exit 0 (tools.validate); `pytest` → 83 passed (tools.test); `python -m tools.example examples/input.json` → exit 0 with one JSON document, status `dry_run` (tools.smoke); `python -m tools.example --help` → exit 0 (help-command acceptance). `tools/envelope.py` is unchanged and still exports the fixed status/exit-code table checked by tests. Target branch `task/1-task` is already an ancestor of the work branch head, so no integration was needed.

Progress:
Addressed the changes-requested review on PR #13: requested width/height are now a minimum target size (selection no longer depends on --resize), and --resize now actually downscales downloaded images (stdlib PNG codec + ICO/PNG container support, shrink-to-fit, never upscaling) with per-item resize metadata and typed ImageResizeError for undecodable payloads. Updated tests/test_cli.py with minimum-size and offline resize/download contract tests (83 passed). Facade validate/test/smoke/help all green; examples/input.json stays the canonical dry_run request.

Published by developer-agent through Tool Backend.

Closes #5
2026-11-03 02:03:37 +00:00
Tool Backend f20e58e85c change: Implement 3: Complete the native-process CLI, JSON request handling, status vocabulary and exit codes
Repository Validate / repository-checks (pull_request) Successful in 3s
Completed the native-process CLI for `python -m tools.example`. Added `tools/envelope.py` with the fixed status vocabulary (`ok`, `dry_run`, `invalid_request`, `no_format`, `no_size`, `access_error`, `partial_download`) mapped to exit codes 0/0/2/3/4/5/6 and the `Response`/`ResponseBuilder` JSON envelope (keys: status, source, request, items, error). Extended `tools/errors.py` with request errors (`RequestError`, `RequestDocumentError`, `RequestFieldError`, `UnsupportedFormatError`) and `ACCEPTED_FORMATS`. Rewrote `tools/example.py` as a plain executable CLI accepting either a JSON request document or flags (--topic, --width, --height, --format png|ico, --source, --count, --download, --output-dir, --resize, --dry-run), with a custom parser that turns usage errors into `invalid_request` envelopes, unknown sources reported together with the available source ids, unsupported formats rejected with the accepted values, and format/size filtering, download and partial-download handling mapped onto the shared status table. `examples/input.json` is now the canonical offline dry_run request, and `tests/test_cli.py` (new) plus a rewritten `tests/test_example.py` cover the status/exit-code table, single-JSON-document output, help text, unknown source/unsupported format rejection and the download branches.

Tests:
Facade checks run from /workspace with the prepared .venv: `python -m compileall tools tests` (tools.validate) OK; `pytest` (tools.test) 76 passed; `python -m tools.example examples/input.json` (tools.smoke) exit 0 with a single `dry_run` JSON document; `python -m tools.example --help` exits 0 and documents every parameter plus the JSON document contract. Origin target branch `origin/task/1-task` (1f929c5) is an ancestor of the work branch, so the target is integrated and the checks above ran on the integrated revision. No dependency changes were needed, so no prepare_environment update was required.

Progress:
Implemented envelope status/exit-code table and response builder, request errors, the full flag/JSON-document CLI in tools/example.py, canonical offline examples/input.json, and CLI tests; facade validate/test/smoke and --help all pass (76 tests).

Published by developer-agent through Tool Backend.

Closes #5
2026-11-03 01:48:00 +00:00
Tool Backend 1f929c5d38 change: Implement 2: Add the pluggable credential-free image source registry and HTTPS fetch layer
Repository Validate / repository-checks (pull_request) Successful in 1s
Added tools/errors.py (typed error hierarchy: ToolkitError -> SourceError -> UnknownSourceError/DuplicateSourceError/SearchRequestError/SourceAccessError with SourceUnreachableError/SourceHTTPError/SourcePayloadError/HostNotAllowedError, each with a stable code, details payload and to_dict()) and tools/sources.py (credential-free image source layer). tools/sources.py provides: an HTTPSClient restricted to the approved hosts commons.wikimedia.org, upload.wikimedia.org and api.openverse.org over HTTPS/443, sending an identifying User-Agent, with bounded exponential retry/backoff (capped) on 429/5xx and typed SourceUnreachableError/SourceHTTPError/SourcePayloadError instead of empty results; a SourceAdapter protocol; WikimediaCommonsAdapter (MediaWiki API generator=search + imageinfo) and OpenverseAdapter (/v1/images/) that pass the caller topic and result limit and normalize results into SourceCandidate records carrying source_id, file_url, reported mime type, dimensions, page URL and a byte-fetch callable; and a SourceRegistry that reports available source ids, rejects duplicate registration and raises UnknownSourceError listing the available ids. No adapter or registry path reads API keys, tokens, registrations or secrets from arguments, environment or config. Added tests/test_sources.py covering registry lookup, duplicate registration, unknown-source errors listing ids, request-URL construction with topic/limit, 429/5xx retry and exhaustion, non-retryable statuses, unreachable hosts, malformed payloads, host allowlist rejection without network contact, candidate normalization and the credential-free guarantee.

Tests:
prepare_environment restore of the tools component (locked env); tools.validate `python -m compileall tools tests` succeeded; tools.test `pytest` succeeded (54 passed, incl. 41 in tests/test_sources.py); tools.smoke `python -m tools.example examples/input.json` returned status ok.

Progress:
Implemented tools/errors.py, tools/sources.py and tests/test_sources.py for the credential-free source registry and HTTPS fetch layer. Verified with the facade commands tools.validate, tools.test (54 passed) and tools.smoke, all green on the integrated branch (target task/1-task was already at HEAD). No protected facade paths touched.

Published by developer-agent through Tool Backend.

Closes #4
2026-11-03 00:08:32 +00:00
Tool Backend d0b7f8f5aa change: Implement 1: Bootstrap the toolkit tree, project config and initial dependency lock
Repository Validate / repository-checks (pull_request) Successful in 2s
Bootstrapped the repository-root toolkit tree required by the facade.

- `tools/__init__.py`: minimal importable package exposing `__version__` only.
- `tools/example.py`: offline CLI (`python -m tools.example REQUEST`) that loads and validates a JSON request document with a validate-only `dry_run` semantics, prints exactly one structured JSON envelope (sorted keys) and fixes the status/exit vocabulary now (0 ok, 1 invalid request document, 2 usage error). `--help` supported.
- `examples/input.json`: canonical offline validate-only request document.
- `tests/test_example.py`: focused tests for the smoke command, envelope shape, request-document validation, `--help`, and the module entrypoint (13 tests).
- `pyproject.toml`: project metadata, setuptools build backend with `packages = ["tools"]`, `dev` extra with `pytest>=8.0`, pytest options `pythonpath=["."]` / `testpaths=["tests"]` so the repository root is importable.
- `uv.lock`: created through the supported dependency update operation for the `tools` component; pinned only from the approved dependency mirror (no public-index fallbacks, no credentials).
- `.gitignore` / `.env.example`: keep build artifacts out of the tree and document that no environment variables are required.

Note: `requires-python` was raised to `>=3.11` because the approved mirror only offers `exceptiongroup<1.0.0rc8`, which cannot satisfy pytest on Python <3.11; the prepared environment (CPython 3.12.3) and CI bootstrap interpreter satisfy this. No protected facade paths were modified.

Tests:
Ran the facade commands with the prepared environment (`.venv/bin/python`):
- `python -m compileall -q tools tests` -> OK (tools.validate)
- `python -m pytest -q` -> 13 passed (tools.test)
- `python -m tools.example examples/input.json` -> single JSON envelope, exit 0 (tools.smoke)
- `python -m tools.example --help` -> usage text, exit 0 (help-command)
Dependency lock/materialization performed via prepare_environment(component='tools', mode='update'): resolved + materialized, `uv.lock` written. Target branch `origin/task/1-task` is already an ancestor of HEAD, so the branch contains the current target.

Progress:
Created tools package (tools/__init__.py, tools/example.py), examples/input.json, tests/test_example.py, .gitignore, .env.example; updated pyproject.toml (metadata, setuptools packages, dev pytest extra, pytest pythonpath/testpaths, requires-python >=3.11 due to approved-mirror exceptiongroup constraint) and generated uv.lock via prepare_environment update. Facade validate/test/smoke and --help all pass locally.

Published by developer-agent through Tool Backend.

Closes #3
2026-11-02 23:23:26 +00:00