Repository navigation
content icon: --strict, --min-contrast and optional --ocr checks (#57) - #127
Conversation
Family-ink fails instead of warning when `--strict` is given on `content icon check` or `content icon normalize`, or when the family sets `icon_strict_family_ink: true`. Off by default; the default output and exit codes are unchanged. With no family palette the family-ink check is still not emitted. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The contrast-on-card floor is now configurable per run (`--min-contrast`) and per family (`icon_min_contrast`, schema range 1 to 21). Precedence is CLI > family key > 3.0, so the default output is unchanged. 4.5:1 is documented as the recommended card-plate floor but stays off by default. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
`content icon check` and `content icon normalize` take `--ocr`. When tesseract is on PATH the icon is flattened on white and read; words of 2+ alphanumerics warn (fail with --strict). Without the tool, or if it fails, no-text stays a manual review and the logs name needs-input:ocr-unavailable or needs-input:ocr-failed. The result and exit code do not change when the tool is missing. Off by default; the default manual entry is unchanged. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…rn (E3.2.3 fix) The OCR commit converted scripts/ctshowcase/content.py to CRLF, so the whole file showed as changed (5394 lines) instead of the real ~78. The base file is LF; this restores LF so the diff against base is the 78 real lines. Also removes stray spaces on the second return in _cmd_icon. No behaviour change. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
icon_qa returned before the OCR step when the PNG could not be validated, so ocr=True produced a result without `logs`. The early return now carries `logs: []` only when ocr is on; the default shape is unchanged. Adds a malformed-PNG regression test. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…review) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ze --strict (E3 review) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
The account paying for this security review has reached its Codex usage limits. The payer can check the Codex usage dashboard. For personal accounts, using credits requires enabling “Use credits for security reviews” in Code review settings. If you do not manage the paying account, contact this repository's admins. |
… with logs=[] (#60) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…AT comment (#60) Fixes ruff E401 at tests/test_content.py (import struct, zlib) by splitting the import, and corrects the comment: the icon_qa API raised OSError('broken data stream'); the CLI already caught it globally (JSON result=usage, logs=['usage'], exit 2). No noqa, no config change. Deviation: the Haiku worker produced no result, so the Sonnet owner made this edit itself. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Bounded native OCR validation for exact head Executed the pinned Five assertions passed (exit 0): OCR disabled keeps manual review; actual Tesseract reads white Evidence scope: current-container, direct |
…bad arguments still exit 2 (#60) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…60) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…colour (card_fill), not a --card flag (#60) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Add test_icon_normalize_cli_undecodable_idat_exits_1_with_logs_and_missing_input_exit_2: a structurally valid PNG with corrupt IDAT must yield exit 1, result fail, out null, an art-fail:icon-qa log and a png fail check, with no output written; a nonexistent or omitted --in stays exit 2. Expected to fail until the content.py normalize handler lands. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…ecodable sources (#57, #60) Read --in as bytes first (missing or unreadable stays a usage error, exit 2). A readable source that cannot be decoded now goes through icon_qa on the raw bytes and returns result=fail with logs and out=null, exit 1, nothing written, instead of raising from Image.open/load. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…xit 1), missing --in stays exit 2 (#60) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…alize (#57) Pre-create the normalize destination with known bytes and assert it is unchanged after the undecodable-IDAT failure, with and without --ocr, and after the usage (exit 2) cases. Test-only change. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
icon_qa gains a `source` keyword. Every png failure now returns logs == ["unreadable-image:<source>"] (or <bytes> when no source is known), with and without --ocr. The icon check and icon normalize CLI paths pass the real file path. The normalize decode-failure branch appends the same unreadable-image entry before art-fail:icon-qa, without duplicating it. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
#57) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ailure (#57) Catch IndexError at both image-decoding boundaries (icon_qa and the normalize source load) so icon check and icon normalize report the structured PNG failure (exit 1, unreadable-image log, out null, destination preserved) with and without --ocr. Reproduced first with a synthetic PNG (valid IHDR, zlib-compressed valid IDAT, zero-length iCCP with correct CRC, IEND): validate_png accepts it and, before the source fix, icon_qa, icon check and icon normalize raised IndexError from PIL PngImagePlugin. Regression test added in tests/test_content.py covering API, check and normalize, with and without --ocr, plus missing --in exiting 2. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…57) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…trol bytes (#57) The malformed PNG is now the valid control with a zero-length CRC-valid iCCP chunk inserted before IEND, so the pixel payload is identical. The unused opaque helper is removed. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Adds skip-marked controls that invoke the installed tesseract binary without monkeypatching: dark-on-light positive, text-free negative, white lettering on a dark card through icon_qa (warn, and fail under strict), a text-free icon pass, and the icon check CLI with --ocr. They skip with an explicit reason when tesseract is absent; the VM and hosted CI run them. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…md; accurate real-OCR skip reason (#57) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Change and affected skills
What:
content icon checkgains--strict(fails onfamily-ink; family keyicon_strict_family_ink), a configurable contrast floor--min-contrast, and an opt-in OCR no-text check--ocr(runstesseractwhen it is onPATH; with--strictan OCR text finding fails; skipped with a log entry when the tool is absent). The icon is composited on the configured family card colour (card_fill) before OCR reads it. Undecodable PNG data now yields a structured QA failure instead of a traceback:icon_qareturnsresult: failwith alogsentry naming the unreadable image once,icon checkexits 1, andicon normalizeexits 1 withoutnull and leaves an existing destination file byte-identical; this includes a PNG with a CRC-valid zero-lengthiCCPchunk after IDAT, which made Pillow raiseIndexError. Schema,references/content.md,SKILL.md(icon command reference) and the CHANGELOG are updated.Why:
family-inkwas only a warning, so an old off-brand icon passed withwarn; the fixed 3:1 floor accepted a dim 3.17:1 icon;no-textwas manual only; a corrupt PNG crashed the QA command.Resolves #57
Resolves #58
Resolves #59
Resolves #60
Prior work: searched this repository's PRs and issues for the icon check, strict mode, contrast floor and OCR. Related but not duplicating: merged PR #20, merged PRs #14 to #19, merged PR #124 (footer), open issue #7 with open PR #13, and the already-resolved icon issues 8 to 12 (not linked here). No other open PR covers issues #57 to #60.
Evidence (exact head
334eaaf958d954239981663a7326e32214dfc667, basemaina30979d4481c3f2124faceb2cf602bb71c95fea8integrated by a normal merge)vm-334eaaf958d954239981663a7326e32214dfc667-result.json): package tests, package validator, full pytest and quality validator passed; pytest 1224 passed, 11 skipped, 37 subtests. The skips are the pre-existing absent external fixtures. This VM has Tesseract 5.3.4 installed (an optional test dependency installed only in the VM; the repository gains no auto-install code).real-ocr-tests-334eaaf...-vm.json):pytest -k real_tesseract -vv -rsselected 5 tests and all 5 PASSED with no skips: dark text on light, a text-free control, white lettering on a dark card (warnsno-text, fails under--strict), a text-free icon passing, and the CLIicon check. They use synthetic PIL images and the real discovery/subprocess path (no mocks); they skip only wheretesseractis not installed, which is why they show as skipped in a local run on a host without it.validate-package(ubuntu, windows, macos),pytest(ubuntu, windows, macos), andvalidate, for the push and pull-request matrices: 14 check runs, all completed with success.claude-opus-5-5) and Codexgpt-6.1-solat max effort each returned an explicit approve with zero findings. Earlier reviews of older heads (including a Sol request for the real-engine tests, now satisfied) do not carry over.tests/test_org_labels_live.py::test_sparse_default_checkout_uses_index_not_visible_files[Northwind-Labs]andtests/test_review_action_commit.py::test_template_icon_removal_is_committed. Both failed at their fixturegit commit(exit 128) and the log does not print the Git stderr, so the cause is unknown (I do not claim a signing cause). The two tests were rerun unchanged in a focused retry with a stderr tracer and passed (2 passed in 5.10 s). This is NOT a passing local full run and is not claimed as one; the exact-head VM and hosted CI are the passing evidence. Earlier local runs on earlier heads failed at the same kind of fixture commit step (one of them printedCannot open A for signing); those logs are preserved and are not evidence for this head.2c76d3aroot's VM replays covered the seven real-Tesseract controls, corrupt-IDAT API/CLI handling,normalizewith and without OCR preserving the destination, and the trailing-iCCPAPI plus all four CLI paths with a valid paired control.Limitations and tracked suggestions
tesseractthe check logsneeds-input:ocr-unavailableand stays manual._ocr_wordscomposites on white again after_flatten_on_card(harmless and redundant),_ocr_wordsand_flatten_on_cardcatch a narrower exception set thanicon_qa,icon_strict_family_inkis read withbool()(a non-schema string would enable strict mode if schema validation were bypassed; the default stays OFF), the--stricthelp text does not yet mention the OCR text finding, and a Windows-native full-suite receipt for this exact head is absent (hostedwindows-latestpytest is green).Boundaries
Activation keys, consent, approval gates and specialist entry points are unchanged. No brand decision is altered and no brand mark was created or redrawn. Defaults are unchanged: strict mode and OCR are opt-in. No registry release, visibility change or live external action is implied. Shared validators (
scripts/validate_package.py,tests/test_package_contract.py) are byte-identical tomain.Family consistency
Single-repository change; no sibling skill PRs are affected.
🤖 Generated with Claude Code