Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,9 @@ child.
wheel and adds the shared bounded OPC/OOXML container security capability.
03B3B3B merged through PR #234. It adds bounded DOCX extraction and durable
omission facts on the shared OOXML boundary. 03B3B3C merged through PR #235 and
adds bounded PPTX slide/notes extraction. 03B3B3D is the active successor and
adds only bounded XLSX cell extraction. Image, AUTH, and sufficiency work remain
adds bounded PPTX slide/notes extraction. 03B3B3D merged through PR #238 and
adds bounded XLSX cell extraction. 03B3B4 is the active successor and adds only
bounded PNG/JPEG/WebP structural metadata. AUTH and sufficiency work remain
inactive.

AUTH `WS-XINT-002-04B` follows the complete hidden split-03B series and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,20 @@ P2
```text
backend/pyproject.toml
backend/uv.lock
backend/config/guide_extractor_dependencies.json
backend/app/modules/artifacts/guide_images.py
backend/app/modules/artifacts/guide_extraction.py
backend/app/modules/artifacts/guide_extraction_worker.py
backend/scripts/check_guide_extractor_dependencies.py
backend/scripts/run_test_lanes.py
backend/tests/test_guide_images.py
backend/tests/fixtures/guide_images/**
backend/tests/test_artifact_architecture.py
backend/tests/test_guide_bindings.py
backend/tests/test_guide_extraction.py
backend/tests/test_guide_extractor_dependencies.py
backend/tests/test_guide_formats.py
docs/spec_artifact_storage_service.md
docs/operations_backend_testing.md
.agent-loop/initiatives/WS-ART-001-immutable-artifact-storage/**
```

Expand All @@ -47,27 +53,104 @@ PDF/OOXML packages, framework/AUTH/Celery/submission changes.

## Acceptance criteria

- Exact 03B2 PNG/JPEG/WebP classification is required.
- Output is a fixed metadata schema and cannot satisfy required textual guide
semantics.
- Do not edit the approved dependency allowlist. Install `Pillow==12.3.0` only
through the two exact hash-bound direct wheel URLs already approved by
03B3B1, using mutually exclusive `python_version == "3.11"` and
`python_version == "3.12"` markers. Unsupported Python/platform combinations,
source distributions, alternate indexes, compatible pins, or fallback
packages fail the dependency gate; no unapproved artifact is resolved.
- Exact 03B2 `png`, `jpeg`, or `webp` classification is required. The isolated
child independently re-parses the complete payload signature and structural
header; its detected format and dimensions must agree with Pillow. The child
receives no trusted classifier facts and never relies on filename or MIME.
- Policy `guide-extraction-v6` emits exactly this compact, sorted JSON object:

```json
{"bit_depth":8,"color_model":"rgb","detected_format":"png","frame_count":1,"height":480,"transparency":false,"width":640}
```

The exact keys are `bit_depth`, `color_model`, `detected_format`,
`frame_count`, `height`, `transparency`, and `width`. Integers are positive,
transparency is boolean, detected format is `png|jpeg|webp`, and color model
is one of `grayscale`, `grayscale_alpha`, `indexed`, `rgb`, `rgba`, `ycbcr`,
or `cmyk`. No source filename, MIME, pixel sample, text, parser diagnostic, or
arbitrary metadata key/value is permitted.
- Image omission facts remain exactly `{"truncated":false,"omitted":false}`:
non-structural metadata is outside the image capability rather than partial
canonical content. The v6 result is a structural fact and cannot satisfy a
required textual guide-source item; 03B4 must consume it only as typed image
metadata and must not concatenate its JSON into textual sufficiency input.
- Parser imports and execution exist only in the isolated extraction child,
never API, materialization/provider, AUTH, Celery, or agent assembly paths.
- Dimensions above 16,384 or 40 megapixels fail before decode/allocation.
- Output is allowlisted to detected format, width, height, frame count, color
model, bit depth, and transparency. Discard EXIF, XMP, IPTC, ICC payloads,
PNG text chunks, comments, thumbnails, geolocation, and every other ancillary
metadata value; fixtures prove this for PNG, JPEG, and WebP.
- The extraction child imports the approved adapter before descriptor-only seccomp, as it
does for other approved parsers. After seccomp, the adapter parses signature,
dimensions, bit depth/color type, transparency markers, and structural frame
declarations before `Image.open`, decoder entry, seeking frames, or any
image-sized allocation. It rejects zero dimensions, either dimension above
16,384, or pixel product above 40,000,000 first. Pillow decompression-bomb
warnings/errors are fatal and sanitized.
- PNG semantics come from valid signature/IHDR/chunk structure: color types
`0,2,3,4,6` normalize respectively to grayscale, rgb, indexed,
grayscale_alpha, and rgba; bit depth must be valid for that color type;
alpha color types or a valid `tRNS` marker set transparency. APNG `acTL`
declares exact frame count. JPEG uses a supported SOF marker: precision must
be 8, one component is grayscale, three are ycbcr, four are cmyk, frame count
is one, and transparency is false; baseline/progressive encoding does not
alter canonical color model. WebP validates RIFF/WEBP and VP8, VP8L, or VP8X
structure: bit depth is 8, alpha normalizes rgb to rgba, and animation frame
count comes from validated ANMF structure. Pillow format, size, normalized
mode/transparency, and `n_frames` must agree with the independent facts.
- Frame count `1..1000` is accepted; zero or conflicting declarations are
malformed and 1001 fails `limit_exceeded/image_frame_limit` before frame
iteration or decode. No frame pixels are loaded. A static or animated image
produces metadata only and never satisfies textual guide semantics.
- Discard EXIF, XMP, IPTC, ICC payloads, PNG text chunks, JPEG comments,
thumbnails, geolocation, and every other ancillary metadata value. The
adapter never calls `getexif`, never serializes `Image.info`, and never emits
raw metadata values. PNG/JPEG/WebP fixtures containing distinct sentinel
secrets prove none reaches canonical output, omission facts, errors, or
parent protocol output.
- Stable adapter failures are:

```text
malformed/image_invalid_header
malformed/image_truncated
malformed/image_format_mismatch
malformed/image_decoder_mismatch
malformed/image_decoder_rejected
unsupported/image_bit_depth
unsupported/image_color_model
limit_exceeded/image_dimension_limit
limit_exceeded/image_pixel_limit
limit_exceeded/image_frame_limit
limit_exceeded/image_decompression_bomb
```

Unexpected native/parser loss remains the existing sanitized
`parser_failure/parser_failure` or supervisor `parser_failure/executor_lost`;
wall/CPU/memory/input/output failures retain the existing framework codes.
- Inherit D42's 32 MiB input, 4 MiB output, CPU/wall-clock, address-space,
descriptor, child/core, termination, and scratch-cleanup rules. Prove these
resource boundaries plus malformed, truncated, decompression-bomb, 16,384
dimension, 40-megapixel, crash, timeout, cancellation, and cleanup outcomes.
accepted/16,385 rejected dimension, 40,000,000 accepted/smallest feasible
over-limit pixel product, 1000/1001 frame, crash, timeout, cancellation, and
cleanup outcomes. Because 40,000,001 factors only as `53 * 754717`, it cannot
isolate the pixel gate while remaining under the 16,384 dimension limit;
tests assert the exact production constant, a production-feasible first
over-limit product, and an exact configured-limit/one-over transition.
- Tests prove the real 03B2 classifier and isolated extraction child agree for every
format; malformed/truncated and metadata-bearing fixtures cover PNG, JPEG,
and WebP; child-only imports and v6 persistence/replay are deterministic.
The dedicated image module belongs to the existing canonical hosted semantic
lane without changing lane or repository coverage policy.

## Verification commands

```bash
(cd backend && python scripts/check_guide_extractor_dependencies.py)
(cd backend && uv run python scripts/check_guide_extractor_dependencies.py)
(cd backend && uv run ruff check app tests scripts)
(cd backend && uv run pytest -q tests/test_guide_images.py tests/test_guide_extraction.py tests/test_artifact_architecture.py --cov=app.modules.artifacts --cov-report=term-missing --cov-fail-under=90)
(cd backend && uv run pytest -q tests/test_guide_images.py --cov=app.modules.artifacts.guide_images --cov-branch --cov-report=term-missing --cov-fail-under=90)
(cd backend && uv run pytest -q tests/test_guide_formats.py tests/test_guide_images.py tests/test_guide_extraction.py tests/test_artifact_architecture.py tests/test_guide_extractor_dependencies.py)
(metadata_dir="$(mktemp -d)" && trap 'rm -rf "$metadata_dir"' EXIT && (cd backend && WORKSTREAM_TEST_ADMIN_DATABASE_URL=postgresql+asyncpg://workstream:workstream@localhost:5433/postgres .venv/bin/python scripts/run_isolated_tests.py --metadata-json "$metadata_dir/result.json" --timeout-seconds 12600 -- .venv/bin/python -m pytest -q --ignore=tests/test_isolated_database_runner.py --cov=app --cov-report=term-missing --cov-fail-under=78))
python3 scripts/check_stale_artifact_contracts.py
python3 scripts/check_markdown_links.py
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# WS-ART-001-03B3B4 External Review Response

## Comments addressed

- Hosted Agent Gates identified ambiguous human-worker vocabulary in two chunk
contract lines. Both now say `extraction child`, matching the isolated parser
process and avoiding confusion with the Workstream contributor role.
- CodeRabbit identified valid Pillow mode normalization for 16-bit
grayscale-alpha PNG. Decoder agreement now accepts Pillow's `RGBA` mode
while retaining the independent header-derived `grayscale_alpha` identity,
with a real 16-bit regression fixture.
- CodeRabbit's `NoReturn`, pytest monkeypatch, architecture-test separation,
and compound-modifier clarity suggestions were applied as small in-scope
maintainability improvements.

## Comments deferred

- CodeRabbit's generic docstring warning is not applicable: the repository's
authoritative hosted Docstring Coverage step passed on the reviewed head.

## Human decisions needed

None.

## Commands rerun

- `python3 scripts/check_stale_authorization_docs.py`
- `python3 scripts/check_stale_artifact_contracts.py`
- `python3 scripts/check_markdown_links.py`
- `git diff --check`
- Focused image, architecture, extraction, and dependency tests with the image
branch-coverage gate.

## Remaining risks

No unresolved external finding remains. Hosted Backend and Agent Gates pass on
the repaired head, and CodeRabbit's final incremental review reported no new
finding. Human approval remains required before merge.
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# WS-ART-001-03B3B4 PR Trust Bundle

## Chunk

`WS-ART-001-03B3B4` — Image Metadata Extractors (L1).

## Goal and human-approved intent

Install only the pre-approved Pillow artifact and add bounded PNG, JPEG, and
WebP structural metadata extraction after exact guide-source classification.
Original verified bytes remain authoritative. This hidden chunk does not add
OCR, raw pixels, AUTH activation, guide sufficiency, submission ZIP handling,
or a second parser/storage path.

## What changed and why

- Added an isolated image adapter that independently validates complete image
structure before Pillow decoder entry and emits only canonical metadata.
- Advanced supported images to `guide-extraction-v6` through the existing
extraction worker and immutable persistence/replay path.
- Installed only the approved hash-bound CPython 3.11/3.12 manylinux x86_64
Pillow wheels and made unsupported Python/platform combinations fail closed.
- Added direct, isolated-worker, architecture, dependency, persistence/replay,
semantic-lane, and documentation proof for all three formats.

## Design chosen and alternatives rejected

The existing worker loads the approved Pillow plugins before descriptor-only
seccomp, then the image adapter re-parses PNG chunks/CRCs, JPEG SOF structure,
or WebP RIFF structure before `Image.open`. Pillow must agree on format,
dimensions, mode, and frame count. Rejected alternatives were request-path
parsing, trusting MIME/classifier facts, direct provider access, OCR, pixel
loading, metadata propagation, unapproved wheels, and generic download or AUTH
capabilities.

## Scope control and product behavior

Only hidden guide-source extraction changes. Guide source items may be PNG,
JPEG, or WebP; contributor submissions remain one outer ZIP. Image output is
typed structural metadata and cannot satisfy required textual guide semantics.
No route, AUTH action, Celery continuation, agent invocation, submission,
review, contribution, payment, or reputation behavior changes.

## Acceptance criteria proof

- Compact sorted v6 JSON has the exact fixed keys and closed format/color
enums; omission facts remain exactly non-truncated/non-omitted.
- PNG validates signature, CRC, IHDR, `tRNS`, APNG sequences/count/bounds;
JPEG validates SOF precision/components; WebP validates RIFF, VP8/VP8L/VP8X,
alpha, animation headers, frame bounds, and counts before decoder entry.
- Limits cover 16,384 dimensions, 40,000,000 pixels, and 1,000 frames,
including exact one-over and smallest feasible production pixel cases.
- EXIF, XMP, IPTC, ICC, comments, text chunks, thumbnails, raw pixels, parser
diagnostics, and sentinels never reach canonical output or error protocol.
- The dependency gate binds exact approved wheel URLs/hashes, requires matching
runtime declarations, and rejects unsupported Python, OS, architecture, or
libc facts.

## Tests and checks run

- Dependency gate, `uv lock --check`, Ruff, stale-contract scan, Markdown links,
and `git diff --check` — pass.
- Image/dependency-focused suite — 108 passed; image branch coverage 90.78%.
- Image/extraction/dependency suite with real isolated PNG/JPEG/WebP workers —
204 passed; image branch coverage 90.78%.
- Format/image/extraction/architecture/dependency integration suite — 247
passed.
- DB-backed persistence/replay and repository-wide coverage remain assigned to
hosted Backend/Agent Gates; no local full-suite run was used.

## Test delta and CI integrity

No test, assertion, lane, workflow, or coverage threshold was removed, skipped,
or weakened. All three formats have direct stable-code tests, real isolated
worker tests, and DB persistence/replay parametrization. The image module joins
the existing semantic lane. Hosted Artifact 90 percent and repository 78
percent coverage gates remain unchanged. The large lockfile deletion is
mechanical pruning of Python 3.13+ wheel records after narrowing the supported
backend range to the approved 3.11/3.12 native-wheel matrix.

## Reviewer results

Plan, architecture, security, product/ops, QA, senior engineering, docs,
reuse/dedup, CI integrity, and test-delta reviews pass. Valid findings for PNG transparency,
APNG declaration validation, runtime/platform enforcement, missing runtime
dependency detection, pixel-boundary proof, cross-format isolated/persistence
proof, exact documentation, and lockfile scope were repaired or verified and
re-reviewed.

## External review

The first Agent Gates run found two ambiguous uses of `worker` in the chunk
contract; both were corrected to `extraction child`. CodeRabbit's valid
16-bit grayscale-alpha decoder-mode finding and maintainability suggestions
were fixed; its final incremental review reported no new finding. Hosted
Backend and Agent Gates pass on the repaired head. These external checks
supplement but do not replace internal review.

## Remaining risks and follow-up work

Classifier and child image limits are intentionally independently enforced, so
tests pin their agreement to prevent drift. ART-03B4 must consume v6 image JSON
as typed metadata rather than textual sufficiency input. AUTH activation and
legacy cutover remain later separate chunks.

## Human review focus and merge ownership

Review pre-decoder hostile-container validation, metadata minimization, native
wheel/runtime enforcement, v6 replay identity, and the absence of OCR, raw
pixels, AUTH activation, or sufficiency invocation. A human owns merge
approval; the agent will not merge this PR.
7 changes: 6 additions & 1 deletion backend/app/modules/artifacts/guide_extraction.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@
DOCX_EXTRACTION_POLICY_VERSION = "guide-extraction-v3"
PPTX_EXTRACTION_POLICY_VERSION = "guide-extraction-v4"
XLSX_EXTRACTION_POLICY_VERSION = "guide-extraction-v5"
IMAGE_EXTRACTION_POLICY_VERSION = "guide-extraction-v6"
EXTRACTOR_VERSION = "1"
MAXIMUM_INPUT_BYTES = 32 * 1024 * 1024
MAXIMUM_OUTPUT_BYTES = 4 * 1024 * 1024
MAXIMUM_PROTOCOL_BYTES = (MAXIMUM_OUTPUT_BYTES * 6) + 1024
WALL_TIMEOUT_SECONDS = 60
_SUPPORTED = frozenset({"plain_text", "markdown", "json", "csv", "pdf", "docx", "pptx", "xlsx"})
_SUPPORTED = frozenset(
{"plain_text", "markdown", "json", "csv", "pdf", "docx", "pptx", "xlsx", "png", "jpeg", "webp"}
)
_DEFAULT_OMISSION_FACTS = {"truncated": False, "omitted": False}
_DOCX_OMISSION_KEYS = frozenset(
{
Expand Down Expand Up @@ -245,4 +248,6 @@ def extraction_policy_version(detected_format: str) -> str:
return PPTX_EXTRACTION_POLICY_VERSION
if detected_format == "xlsx":
return XLSX_EXTRACTION_POLICY_VERSION
if detected_format in {"png", "jpeg", "webp"}:
return IMAGE_EXTRACTION_POLICY_VERSION
return EXTRACTION_POLICY_VERSION
Loading
Loading