Releases: bsmi021/pil-agent-plugin
Release list
0.9.1 — Astra-ready skill guidance
0.9.1 — Astra-ready skill guidance. Rewrites the four shipped skill
entrypoints around concise activation, progressive disclosure, task-specific
routing, proportional decision boundaries, and explicit completion. The
measurement, calibration, embedding, reconstruction, and Blender behavior is
unchanged. See the Astra skill audit.
Full diff: https://github.com/bsmi021/pil-agent-plugin/commits/pil-agent-plugin--v0.9.1
0.9.0 — Explicit measurement workflows and agent tooling
0.9.0 — explicit measurement workflows and agent tooling. Adds EXIF/ICC
normalization with preserved alpha and coordinate provenance; model/profile
configuration-aware embedding claims; image-bound raster/polygon masks and a
pipeline that composes the original image tools; bounded translation/rigid/affine
registration with raw/aligned evidence; native ΔE/alpha change regions, heatmaps,
crops and optional standard SSIM; domain profiles with source-group holdouts,
leakage checks, measured error rates and profile identity checks; and a public
tool catalog, failure-preserving batches and optional stdio MCP adapter.
Bootstrap accepts --comparison and --mcp. Legacy default measurements remain
unchanged apart from the release version and corrected embedding eligibility.
New defaults remain diagnostic unless a compatible evaluated profile is used.
See usage and
execution evidence.
Full diff: https://github.com/bsmi021/pil-agent-plugin/commits/v0.9.0
0.8.0 — Platform bootstrap and dependency diagnostics
0.8.0 — platform bootstrap and dependency diagnostics. Adds a Python
bootstrap for a plugin-local environment on Windows, macOS, and Linux, with
opt-in OCR, embedding, and reconstruction dependencies. Its check command
distinguishes a recorded successful bootstrap from current readiness, rechecks
live dependencies, and skips repeat installation only when both pass. A bundled
bootstrap skill guides agents through setup and status checks.
OCR now discovers standard Windows Tesseract installations and supports an
environment override and image-free executable/language diagnostics. Embedding
diagnostics validate runtime/model setup; DLL and model-load failures preserve
exit 2 with empty stdout. PowerShell installation and model/profile configuration
commands are documented. Focused process tests exercise missing/discovered
dependencies, override precedence, paths with spaces, and bootstrap lifecycle.
Model weights remain caller-supplied; embedding claims and calibration results
are unchanged.
Full diff: https://github.com/bsmi021/pil-agent-plugin/commits/v0.8.0
v0.7.0
0.7.0 — one-call profiling and the semantic layers. pil_image_analyze
composes the file-fact, palette and structure tools into a single maximal
profile and adds persistable hex fingerprints, tonal percentiles and clipping
fractions, channel statistics and (uncalibrated) detail diagnostics.
pil_semantic_record seals vision claims to an image's exact sha256 —
attributable and checkable, never a truth claim. pil_ocr reads text with
frame-mapped boxes and feeds those records. pil_embed adds ONNX embedding
fingerprints under the new optional embedding extra, with per-model
preprocessing profiles and advertised capabilities keyed by model sha256:
mobilenetv2-12 is gated for same-image identification only, while CLIP
ViT-B/32 also passes same-venue ranking on full band separation
(runs/2026-09-02-clip-embedding-discrimination/).
Ungated models advertise nothing. Existing tool payload shapes are unchanged.
Full diff: https://github.com/bsmi021/pil-agent-plugin/commits/v0.7.0
0.7.0 — One-call profiling and the semantic layers
0.7.0 — one-call profiling and the semantic layers. pil_image_analyze
composes the file-fact, palette and structure tools into a single maximal
profile and adds persistable hex fingerprints, tonal percentiles and clipping
fractions, channel statistics and (uncalibrated) detail diagnostics.
pil_semantic_record seals vision claims to an image's exact sha256 —
attributable and checkable, never a truth claim. pil_ocr reads text with
frame-mapped boxes and feeds those records. pil_embed adds ONNX embedding
fingerprints under the new optional embedding extra, with per-model
preprocessing profiles and advertised capabilities keyed by model sha256:
mobilenetv2-12 is gated for same-image identification only, while CLIP
ViT-B/32 also passes same-venue ranking on full band separation
(runs/2026-09-02-clip-embedding-discrimination/).
Ungated models advertise nothing. Existing tool payload shapes are unchanged.
Full diff: https://github.com/bsmi021/pil-agent-plugin/commits/v0.7.0
0.6.0 — Constrained multiview reconstruction
Phase 4 adds optional OpenCV and SciPy multiview preparation and template fitting, Blender BVH garment clearance probing and bounded copy fitting, arbitrary locked-framing renders, worst-case multiview review, seven versioned schemas, and the combined image-analysis skill. Core Pillow and NumPy behavior remains backward-compatible. Release validation: 683 passed, 6 skipped; Claude and Codex plugin validation passed; all skills and schemas validated.
0.5.0 — Phase 3 complete
Phase 3 complete
Release 0.5.0 completes Track A5 and Track B1-B3.
Shipped
pil_blender_mesh.py: polygon, vertex, material, and bounding-dimension facts from Blender scene data;geometry.*contract predicates resolve when scene stats are supplied.pil_blender_render.py: deterministic same-machine front/side/back Workbench renders with reference registration and atomic output publication.pil_character_sheet_review.py: worst-case multi-view character-sheet contracts over registered render/reference pairs.pil_alignment.py contrast: verified WCAG contrast arithmetic.- Connected-component, silhouette, and alignment diagnostics with their measured demotion evidence published rather than overclaimed.
PR #7 critic fixes
- A failed render now makes every contract item for that view
UNMEASURABLE; identical sentinel pixels can no longer satisfy layout or palette checks. - Public review JSON uses stable
render://VIEWandhard-fail://VIEWidentifiers and is byte-identical across repeated invocations; deleted temporary paths are not exposed. foreground_support_insufficientnow refuses the comparison.- Blender output is staged atomically; failures remove partial staging data and preserve a caller's existing output file.
- Tests prove all three requested views reach both the manifest and verdict aggregation.
Verification
- Unfiltered suite: 657 passed, 6 skipped, 0 failed.
- Strict Claude plugin validation: passed.
- Manifest/package/tool versions: consistently
0.5.0. - Live validation against the published Agent Plugins schema could not be refreshed during release because DNS resolution failed; repository packaging-conformance tests passed.
Evidence bundles and exact residuals are indexed in docs/index.md.
0.4.0 — Coverage-weighted foreground, and tools that close the loop
Foreground measurement on RGBA input was wrong in a way that mattered, three new tools close the loop between measurement and vision, and phase 2''s last open calibration gate is closed.
The fix that matters most
On RGBA input in --foreground mode, statistics are now coverage-weighted — each partially-covered pixel''s true colour is recovered and weighted by how much of the pixel the object actually covers. Before, it was composited onto black and then counted exactly like a fully-opaque pixel.
| Before | After | |
|---|---|---|
| Foreground luminance error vs truth | −4.3 … −42.8 | ±0.0004 |
| Sub-pixel re-render excursion | up to 21.6 | ±0.0006 |
The second row is the real story. A quarter-pixel re-render of an unchanged asset could move the reading far enough to cross a calibrated threshold and report a change that never happened — precisely the false positive this plugin exists to prevent. Thin diagonal objects were worst hit, which is exactly the asset class it is used on. Full-frame behaviour is byte-identical.
Three new tools
pil_crop— a native-resolution crop of a region the measurement tools already located. An image is resampled to fit a vision encoder, so detail below that resolution never reaches the model; this hands it back. Integer nearest-neighbour upscale only: it magnifies without inventing detail, and refuses high-bit-depth sources rather than silently converting them.pil_annotate— numbered boxes drawn on a copy, so a model can say "box 3" and be understood. Where a numeral cannot be placed cleanly the tool says so, via per-entryglyph_hazardsand aclampedplacement name.pil_image_info— the file facts an image never carries into a vision encoder: true dimensions, whether an alpha channel exists and whether it is used, EXIF including sub-IFD tags, ICC, DPI, frames. It reports what the file claims and refuses to infer past it.--regionon both diff tools scopes every metric to a fractional box — byte-equal to pre-cropping with the realpil_cropbinary, because both consume one frozen parser.
Calibration: phase 2''s last gate closed
Foreground thresholds are now split by mask source, because they differ by more than an order of magnitude:
| Metric | estimate path | no-placement | alpha path |
|---|---|---|---|
luminance_mean_delta_abs |
34.166 | 1.452 | 0.997 |
changed_area_fraction |
0.287 | 0.0045 | 0.0 |
The alpha path is calibrated for the first time (n=380, α=0.01 over 19 RGBA scenes). The estimate path moved from n=100/α=0.05 on a single scene to n=400/α=0.01 over four, and adding thick objects confirmed that rescale_roundtrip''s old dominance was about thin objects specifically — its median-to-next ratio fell from 18.9× to about 3×.
Reading the wrong threshold by an order of magnitude is now the easiest available mistake, so the skill documentation says which is which, explicitly.
Legibility, verified by reading rather than asserting
runs/2026-08-20-annotate-readback/ records five annotated images read by fresh agents given only the image path, forbidden from opening any other file, with the JSON legends written afterwards and never shown. 27 of 27 numerals transcribed correctly.
The bundle also records a first round that failed — three of five blind readers reported numerals as clipped and two downgraded to guesses, on glyphs that were geometrically whole and inside the frame. No assertion in the test suite could have caught that. It took a reader, and that is the point of the exercise.
Honest limits
- Real-image validation still covers one asset from one pipeline, and the alpha path''s controls are synthetic corpus scenes rather than production RGBA renders.
- The foreground estimate path remains placement-dominated (34.166 against a 1.452 non-placement floor). Read
threshold_foreground_estimate_no_placementwhen the change you care about does not move the object on the pixel grid. - Annotation output is pixel-identical across Pillow versions, byte-identical only across runs in one environment. An earlier release claimed byte-identity across versions; that claim was wrong and is corrected.
469 tests (469 passed, 6 skipped on a clean checkout). Version 0.4.0 across all eleven places, now guarded by a glob-driven test demonstrated to fail when a version drifts.
Update an existing install:
claude plugin marketplace update pil-agent-plugin
claude plugin update pil-agent-plugin@pil-agent-plugin🤖 Generated with Claude Code
0.3.0 — Phase 2: trustworthy verdicts
Phase 2 in full: colour distance you can trust perceptually, thresholds derived by measurement instead of guesswork, and verdicts that answer "did my change land, and did anything else drift?" — each carrying an honest statement of what it cannot rule out.
Perceptual colour distance (WP1)
scripts/pil_color.py— hand-rolled D65 sRGB→CIELAB→LCh and CIEDE2000 in pure numpy, reproducing all 34 Sharma/Wu/Dalal reference values to 4 decimal places. Never Pillow's D50convert("LAB"); never normalised by 100 (the sRGB maximum is 119.22, not 100); no verbal perceptibility bands, because none are authoritative.base_palette_distance_de2000/accent_palette_distance_de2000are now the primary colour signal. The Euclidean-RGB distances remain for continuity, demoted.--accent-space lchoffers a perceptual chroma/lightness accent gate. HSV stays the default: calibration measured that the gates disagree on 12% of the sRGB grid, but ranking them needs accent ground truth no synthetic corpus supplies.
Calibrated thresholds and published detection limits (WP2)
calibration/ plus the calibration bundle: Neyman–Pearson thresholds as bootstrap-CI upper bounds, every one recorded with its n and α, over 500 no-change controls and an 11-family perturbation grid with exact ground truth — 1402 units, byte-identical across two passes.
Applied: CHANGE_THRESHOLD 10→20, CELL_MIN_SUPPORT_PIXELS 16→64, FOREGROUND_MIN_FRACTION 0.02→0.022588, entropy_delta demoted (it resolved 2 of 26 perturbation curves). One derivation was rejected — lowering the hue-presence pixel floor to 1 — because the synthetic negative class contains no cross-render anti-aliasing jitter; the reason is recorded in scripts/pil_common.py.
Every metric now publishes a detection limit per perturbation type, which is what lets a null result mean something.
Contract-driven verdicts (WP3 + WP4)
scripts/pil_contract_verdict.py — declare intent, get per-predicate SATISFIED / VIOLATED / UNMEASURABLE with the deciding fields cited:
- Every null result carries its detection limit. "Invariant satisfied" is never presented as a guarantee.
UNMEASURABLEis never approximated.geometry.*needs scene mesh statistics;style.*andidentity.same_characterare not reducible to pixel statistics. They refuse.- Aggregation is worst-case — one diverging view fails the item, and cannot be averaged away.
identity.silhouette_preservedruns foreground-mask IoU, restricted to clean-mask cases.
Validated against real production renders
The thresholds were checked against a game asset's actual revision history, not only the synthetic data they came from (validation ledger):
- Zero false alarms across 160 real no-change controls in full-frame mode.
- 21 of 24 real revision pairs detected in both modes; the three undetected sit below the published 2%-of-frame limit, while the same edit is caught in every isolated view — the limits transfer.
- Support gating stayed quiet on all 18 composition-stable cross-render pairs.
Two gate criteria were refined mid-run with reasons stated and the strict results preserved in the payload. Still open and documented: the alpha foreground path is uncalibrated, and validation covers one asset from one pipeline.
211 tests (205 passed, 6 skipped on a clean checkout). docs/phase3-scope.md is drafted and awaiting sign-off.
Update an existing install:
claude plugin marketplace update pil-agent-plugin
claude plugin update pil-agent-plugin@pil-agent-plugin🤖 Generated with Claude Code
0.2.0 — Foreground separation
Driven by a production miss: run over two immutable views of the same sword on a shared preview background (~98.5% of both frames), full-frame structural similarity read 0.991 — the background was doing the scoring — and the accent-hue verdict fired off a handful of pixels while both images carried accent_area_very_small.
Default-mode guardrails (breaking nothing, catching the miss)
- Both tools now estimate foreground coverage on every run and flag
background_dominant(<10% of frame) on each image and on the diff. - The palette diff flags
accent_support_lowwhen either side is under the vivid-pixel floor. Treat either flag as "these scores describe the backdrop".
--foreground mode on both tools
- Background masked out: from alpha when the file carries real transparency, otherwise pixels near the border-median colour in OKLab (
--background-delta, default 0.035) — the same visible-pixel definition as the Synty asset index. - The structure tool crops to the foreground bounding box (position in frame stops mattering) and scores only grid cells with ≥16 foreground pixels; a cell with foreground on one side only scores its occupancy divergence — that is the shape difference.
- All palette fractions become foreground-relative; a
foregroundblock records mask provenance in every payload.
Support-gated hue verdict
A hue family needs ≥10 pixels and ≥0.02% of frame before its appearance or disappearance can flip accent_hue_shift_detected — a stray anti-aliased pixel no longer flips the verdict. The census itself is never gated.
Measured on the pinned reproduction
| Scenario | Full-frame | --foreground |
|---|---|---|
| Two different swords, same backdrop | 0.95 (flagged) | 0.62 |
| Same sword, translated in frame | 0.95 | 1.0 exactly |
| Same sword, repainted backdrop | changed_area 0.98 | changed_area 0.003 |
New flags: background_dominant, accent_support_low, foreground_too_small, foreground_mask_empty, foreground_mask_mismatch, foreground_aspect_mismatch. All new thresholds are echoed in parameters. 86 tests (80 passed, 6 skipped on a clean checkout). Default-mode JSON changes are purely additive.
Update an existing install with:
claude plugin marketplace update pil-agent-plugin
claude plugin update pil-agent-plugin🤖 Generated with Claude Code