feat(bridge): accept renderer 0.2.0, structured diagnostics, ZplRaster and preview; render target dpi end to end (P5, P7) #15

Merged
james.maes merged 2 commits from feat/bridge-preview into develop 2026-11-12 18:28:13 +00:00
Owner

Packages P5 (bridge: accepted renderers, diagnostics, ZplRaster, preview) and P7 (render target dpi) of docs/design/LABEL-LAYOUT-FONTS-PREVIEW.md, as one pull request.

Why

On develop the renderer speaks qqq-renderer-0.2.0 and the host bridge accepted only qqq-renderer-0.1.0, so the bridge refused its own renderer. A label could not be previewed before printing, and a request could not say which printer resolution to render for (RenderRequest.targetDpi was validated and then ignored by the bridge). This makes the host side whole again, adds the preview, and carries the render target end to end.

The version rule

One table, in RendererVersion:

Renderer Answers Refusals it may name Version 2 scene, render target, preview
0.1.0 the released fields only (5 for VALIDATE, 10 for RENDER, 6 for an error) the 13 released codes refused: RENDERER_TOO_OLD
0.2.0 the same, with diagnostics (and detail on an error) those and the 15 scene codes rendered
any other rejected
  • The temporary RENDERER_0_1_REFUSALS range is gone. Each set is written out, so a code added to FailureCode later belongs to no renderer until someone decides which (a test says so).
  • A job is framed exactly as renderer 0.1.0 reads it unless it needs more, so version 1 printing is the same request against either image.
  • The bridge asks before it sends. Nothing tells the bridge which image answers, so before a job that only 0.2.0 can do it validates a fixed, empty version 1 scene on a fixed stock; every released renderer answers that with its version. An old image is refused with RENDERER_TOO_OLD and receives nothing of the version 2 scene or the record. After a verified answer of 0.2.0 the question is not repeated. An answer from 0.1.0 to such a job is refused whatever it says, and the next job asks again. Cost: one extra worker run for the first version 2 job of an adapter, and one for each version 2 job against an old image.

Diagnostics from an untrusted worker

WorkerAnswer reads the frame; SceneFacts holds the two facts the host reads from the source it sends (its schema, and the id of each element and child). The host still lays out and validates nothing.

  • A version 2 refusal is RenderException(code, diagnostic) instead of the opaque failure.
  • Every field is checked again on the host: code and severity against the closed vocabulary, numbers as whole integers in bounds, the element against the ids the scene that was sent declares, and each pointer segment against array indexes, the schema's property names (held equal to renderer/schema/zpl-scene-2.schema.json by a test) and those ids.
  • An answer holds at most 2048 diagnostics in 1 MiB beside its label. The VALIDATE frame was 4096 bytes; for a job that can return diagnostics it is 4096 bytes plus that megabyte, and no frame ever passes the frame of the largest document the deployment admits, which is what the transport was sized for.
  • A refused answer that carries any part of a label is rejected by name, as is a refused answer to a job that did not collect (the print path never does) and an accepted answer that holds an error.

Preview

preview(PreviewRequest) is the print path stopped before retention: the same job with "diagnostics": "COLLECT", the same integrity checks, the same ZplOutputPolicy, then ZplRaster decodes the page from the accepted label bytes into PreviewPage(png, widthDots, heightDots). A test holds the print and preview jobs equal but for the request id and that one field, and the preview's size and hash equal to the bytes render returns.

  • ZplRaster is its own strict reader of the policy's grammar. Its entry point runs the policy first, so no caller can draw bytes it did not accept. ^GFA and ^GB are painted exactly; each ^A0N field (version 1 only) is a hatched placeholder and the page says exact: false with the warning PRINTER_FONT_NOT_SHOWN. The PNG is written by the class itself (one bit per dot); no image or font is decoded on the host.
  • A refused scene is drawn on the host from the diagnostics' boxes, and from nothing else. The design left this open after the scene 2 core: the worker no longer returns a partial label. The two options were to draw from the boxes, or to add a separately named field holding the partial canvas. The second would show the elements that had no error, but it puts a printable label with elements missing on the host, and it makes the host draw bytes the output policy never accepted as the label. With boxes alone no such bytes exist outside the worker, so no host can print a partial label, whatever it does. The preview of a refused scene is therefore a page on which each error's box is outlined and hatched at its place, with printable: false, no output (outputBytes 0, the hash of no bytes), and no page at all when no error has a box. Recorded in sections 6.1 and B.1 of the design.
  • A refusal is a result, not an exception: a collected REFUSED answer, an error with its detail, TARGET_REQUIRES_SCENE_2, RENDERER_TOO_OLD and PREVIEW_TOO_LARGE (more than sixteen million dots, decided before any worker is contacted) all come back as diagnostics. An opaque failure stays the generic exception.
  • The bridge writes and logs nothing for a preview. The production Docker transport journals every worker run it starts (container and request identity, never source or context); a preview's run is journalled like a validation's.
  • PreviewRequest and RenderRequest now hold their own unmodifiable copy of the context (null values allowed) and print only the format and render target (and the request id); neither wire form changes. This was a review finding on the ports.

Render target

DocumentGenerationService.Request gains a trailing Integer targetDpi (absent, 203, 300 or 600). Every earlier constructor stays.

  • Stored in a new nullable column, compared on retry and lookup with destination, media and copies, and framed into the intent digest as target-dpi/v1 when stated. A request without one has exactly the digest it had (a test recomputes it from the framing, and the migration test shows an earlier row still matches).
  • Sent to the worker with a version 2 scene, and the label is held by the output policy to the page at that resolution, so a worker that renders at another resolution is refused in either direction.
  • No path is left where 300 dpi is asked for and another resolution comes back. Generation refuses a target for a PDF, and refuses any target unless the configured renderer declares the new IsolatedRenderer.supportsRenderTarget() (false by default, so an adapter written before the target cannot be handed one and ignore it); both before any row. The bridge refuses a version 1 scene or a PDF asked for another resolution than its stock's as TARGET_REQUIRES_SCENE_2 before any worker is contacted (renderer 0.1.0 does not read the field at all).
  • The generate process form offers the target and the request status shows it. Batches, the queue and reprints carry none.

What a host must apply

One statement, from the shipped script for its database (foundation/src/main/resources/db/migrations/):

Database Script Statement
H2 20261112-render-target-dpi-h2.sql ALTER TABLE document_generation_request ADD COLUMN target_dpi INTEGER NULL;
MySQL 20261112-render-target-dpi-mysql.sql ALTER TABLE document_generation_request ADD COLUMN target_dpi INT NULL;
PostgreSQL 20261112-render-target-dpi-postgres.sql ALTER TABLE document_generation_request ADD COLUMN IF NOT EXISTS target_dpi INTEGER NULL;

Earlier rows stay null and read as no target. A host with its own IsolatedRenderer adapter that wants render targets overrides supportsRenderTarget(); the two bridges in this repository already do.

Also

renderer/qualification.json said qqq-renderer-0.1.0 and qqq-barcodes-1; it now says what the worker reports (qqq-renderer-0.2.0, qqq-barcodes-9), and a test holds it to the worker's answer. It still says qualified: false.

Where the design and the code did not match

  • Section 11.3 has the bridge refuse an old image by name but gives it no way to know the image's version before it sends. The version question above is new; the section records it.
  • Section 6.1 drew a refused preview from the worker's partial bitmap. Since the scene 2 core a refused answer has no label; the decision above replaces it.
  • Section 2.3 says only the renderer reads the scene. To refuse a version 1 target before sending, and to check diagnostics, the host now reads the schema and the element ids of the source, and nothing else.
  • Work plan, P5 lists RendererJson.java as edited: the scalar-only answer reader in it is removed, and WorkerAnswer reads the whole frame.
  • Work plan, P7 lists GenerationStore.java and DocumentGenerationSchema.java: neither needed a change (the bootstrap DDL is generated from the table metadata), and PrintingDispatch.java does not carry the value.
  • With renderer 0.2.0 a label source that is not JSON, or has no known schema, is refused as SCENE_INVALID with a pointer where 0.1.0 gave the opaque failure. The bridge accepts that from 0.2.0 only.

Tests

  • Version rule matrix (VersionRuleTest): renderer 0.1.0 and 0.2.0 against version 1 and version 2 scenes, what each is sent, the version question and that it holds nothing of the scene or record, a renderer replaced by an older one, an unknown version, and each render target rule.
  • Hostile answers (WorkerAnswerTest): bad pointer segments, an undeclared element (also one that appears only in a sample), oversized frames, texts and diagnostic counts, every wrong field count, a refused answer carrying each part of a label, wrong shapes, each rejected by its name.
  • ZplRaster (ZplRasterTest): every scene 2 golden at 203, 300 and 600 dpi decodes dot for dot to the golden picture of the canvas, and the PNG read back by the platform decoder holds the same dots; all 42 released version 1 labels decode; malformed strips and oversized pages are refused by name.
  • Preview (PreviewTest): the golden picture at three resolutions through the bridge, print and preview equality, no files written, the refused page placed at the golden's own corner at three resolutions, old image, too large, version 1 placeholders, and that only an accepted label becomes a picture.
  • Mutation campaigns extended to the page decoder and the answer frames, each with an independent reading of what was accepted (docs/security/fuzzing.md).
  • Render target (GenerationExpectedIntentTest, GenerationProcessesTest, GenerationPortsContractTest): carried, stored, part of the intent, digest framing recomputed independently, refusals before any row, typed refusal replayed without rendering, and the migration on H2, MySQL and PostgreSQL (GenerationQueueJdbcTest and its two real-engine subclasses).
  • Worker side (HostContractTest in renderer): the qualification record, the version question, and that collecting diagnostics does not change a label that prints.
  • The version 1 corpus is untouched and green in both modules.

Build

./mvnw --batch-mode --no-transfer-progress -s <settings> -T1C verify on the head bf546e1, from a clean tree: BUILD SUCCESS, all eleven modules, no skipped test and no skipped gate, 0 Checkstyle violations, every coverage check met.

Module Tests
foundation 264
renderer 306
bridges/renderer 207
the other five modules with tests 76
all 853

Line coverage in bridges/renderer (JaCoCo, this build): ZplRaster 183 of 183 lines and 132 of 132 branches; WorkerAnswer 122 of 122 and 152 of 152; SceneFacts 59 of 59; RendererVersion 14 of 14. Those four are gated at 100% of lines by name. IsolatedRendererBridge, an existing class under the bundle gate, is at 182 of 198 lines. The bundle is at 90.9% of instructions and 88.4% of lines, and its gate rises from 85% and 82% to 90% and 88%, as the POM says to do when coverage rises. The numbers were measured on Windows; the gate is a whole-percent floor under them.

Deliberately left out

  • The preview service, action and record widget (P6b), PDF previews (P11) and the dg: asset branch (P8).
  • A picture of the elements that had no error in a refused preview: see the decision above.
  • A render target for batches, the queue and reprints.
  • RemoteRendererBridge.preview and supportsRenderTarget delegate to the bridge in one line each; that module has no test tree and none was added.
  • No container image was built or pushed, and no release branch was touched.

🤖 Generated with Claude Code

Packages **P5** (bridge: accepted renderers, diagnostics, `ZplRaster`, preview) and **P7** (render target dpi) of `docs/design/LABEL-LAYOUT-FONTS-PREVIEW.md`, as one pull request. ## Why On `develop` the renderer speaks `qqq-renderer-0.2.0` and the host bridge accepted only `qqq-renderer-0.1.0`, so the bridge refused its own renderer. A label could not be previewed before printing, and a request could not say which printer resolution to render for (`RenderRequest.targetDpi` was validated and then ignored by the bridge). This makes the host side whole again, adds the preview, and carries the render target end to end. ## The version rule One table, in `RendererVersion`: | Renderer | Answers | Refusals it may name | Version 2 scene, render target, preview | |---|---|---|---| | `0.1.0` | the released fields only (5 for VALIDATE, 10 for RENDER, 6 for an error) | the 13 released codes | refused: `RENDERER_TOO_OLD` | | `0.2.0` | the same, with `diagnostics` (and `detail` on an error) | those and the 15 scene codes | rendered | | any other | rejected | | | - The temporary `RENDERER_0_1_REFUSALS` range is gone. Each set is written out, so a code added to `FailureCode` later belongs to no renderer until someone decides which (a test says so). - A job is framed exactly as renderer 0.1.0 reads it unless it needs more, so version 1 printing is the same request against either image. - **The bridge asks before it sends.** Nothing tells the bridge which image answers, so before a job that only 0.2.0 can do it validates a fixed, empty version 1 scene on a fixed stock; every released renderer answers that with its version. An old image is refused with `RENDERER_TOO_OLD` and receives nothing of the version 2 scene or the record. After a verified answer of 0.2.0 the question is not repeated. An answer from 0.1.0 to such a job is refused whatever it says, and the next job asks again. Cost: one extra worker run for the first version 2 job of an adapter, and one for each version 2 job against an old image. ## Diagnostics from an untrusted worker `WorkerAnswer` reads the frame; `SceneFacts` holds the two facts the host reads from the source it sends (its `schema`, and the `id` of each element and child). The host still lays out and validates nothing. - A version 2 refusal is `RenderException(code, diagnostic)` instead of the opaque failure. - Every field is checked again on the host: code and severity against the closed vocabulary, numbers as whole integers in bounds, the element against the ids the scene that was sent declares, and each pointer segment against array indexes, the schema's property names (held equal to `renderer/schema/zpl-scene-2.schema.json` by a test) and those ids. - An answer holds at most 2048 diagnostics in 1 MiB beside its label. The VALIDATE frame was 4096 bytes; for a job that can return diagnostics it is 4096 bytes plus that megabyte, and no frame ever passes the frame of the largest document the deployment admits, which is what the transport was sized for. - A refused answer that carries any part of a label is rejected by name, as is a refused answer to a job that did not collect (the print path never does) and an accepted answer that holds an error. ## Preview `preview(PreviewRequest)` is the print path stopped before retention: the same job with `"diagnostics": "COLLECT"`, the same integrity checks, the same `ZplOutputPolicy`, then `ZplRaster` decodes the page from the accepted label bytes into `PreviewPage(png, widthDots, heightDots)`. A test holds the print and preview jobs equal but for the request id and that one field, and the preview's size and hash equal to the bytes `render` returns. - **`ZplRaster`** is its own strict reader of the policy's grammar. Its entry point runs the policy first, so no caller can draw bytes it did not accept. `^GFA` and `^GB` are painted exactly; each `^A0N` field (version 1 only) is a hatched placeholder and the page says `exact: false` with the warning `PRINTER_FONT_NOT_SHOWN`. The PNG is written by the class itself (one bit per dot); no image or font is decoded on the host. - **A refused scene is drawn on the host from the diagnostics' boxes, and from nothing else.** The design left this open after the scene 2 core: the worker no longer returns a partial label. The two options were to draw from the boxes, or to add a separately named field holding the partial canvas. The second would show the elements that had no error, but it puts a printable label with elements missing on the host, and it makes the host draw bytes the output policy never accepted as the label. With boxes alone no such bytes exist outside the worker, so no host can print a partial label, whatever it does. The preview of a refused scene is therefore a page on which each error's box is outlined and hatched at its place, with `printable: false`, no output (`outputBytes` 0, the hash of no bytes), and no page at all when no error has a box. Recorded in sections 6.1 and B.1 of the design. - **A refusal is a result, not an exception**: a collected `REFUSED` answer, an error with its `detail`, `TARGET_REQUIRES_SCENE_2`, `RENDERER_TOO_OLD` and `PREVIEW_TOO_LARGE` (more than sixteen million dots, decided before any worker is contacted) all come back as diagnostics. An opaque failure stays the generic exception. - The bridge writes and logs nothing for a preview. The production Docker transport journals every worker run it starts (container and request identity, never source or context); a preview's run is journalled like a validation's. - `PreviewRequest` and `RenderRequest` now hold their own unmodifiable copy of the context (null values allowed) and print only the format and render target (and the request id); neither wire form changes. This was a review finding on the ports. ## Render target `DocumentGenerationService.Request` gains a trailing `Integer targetDpi` (absent, 203, 300 or 600). Every earlier constructor stays. - Stored in a new nullable column, compared on retry and lookup with destination, media and copies, and framed into the intent digest as `target-dpi/v1` when stated. A request without one has exactly the digest it had (a test recomputes it from the framing, and the migration test shows an earlier row still matches). - Sent to the worker with a version 2 scene, and the label is held by the output policy to the page at that resolution, so a worker that renders at another resolution is refused in either direction. - **No path is left where 300 dpi is asked for and another resolution comes back.** Generation refuses a target for a PDF, and refuses any target unless the configured renderer declares the new `IsolatedRenderer.supportsRenderTarget()` (false by default, so an adapter written before the target cannot be handed one and ignore it); both before any row. The bridge refuses a version 1 scene or a PDF asked for another resolution than its stock's as `TARGET_REQUIRES_SCENE_2` before any worker is contacted (renderer 0.1.0 does not read the field at all). - The generate process form offers the target and the request status shows it. Batches, the queue and reprints carry none. ### What a host must apply One statement, from the shipped script for its database (`foundation/src/main/resources/db/migrations/`): | Database | Script | Statement | |---|---|---| | H2 | `20261112-render-target-dpi-h2.sql` | `ALTER TABLE document_generation_request ADD COLUMN target_dpi INTEGER NULL;` | | MySQL | `20261112-render-target-dpi-mysql.sql` | `ALTER TABLE document_generation_request ADD COLUMN target_dpi INT NULL;` | | PostgreSQL | `20261112-render-target-dpi-postgres.sql` | `ALTER TABLE document_generation_request ADD COLUMN IF NOT EXISTS target_dpi INTEGER NULL;` | Earlier rows stay null and read as no target. A host with its own `IsolatedRenderer` adapter that wants render targets overrides `supportsRenderTarget()`; the two bridges in this repository already do. ## Also `renderer/qualification.json` said `qqq-renderer-0.1.0` and `qqq-barcodes-1`; it now says what the worker reports (`qqq-renderer-0.2.0`, `qqq-barcodes-9`), and a test holds it to the worker's answer. It still says `qualified: false`. ## Where the design and the code did not match - **Section 11.3** has the bridge refuse an old image by name but gives it no way to know the image's version before it sends. The version question above is new; the section records it. - **Section 6.1** drew a refused preview from the worker's partial bitmap. Since the scene 2 core a refused answer has no label; the decision above replaces it. - **Section 2.3** says only the renderer reads the scene. To refuse a version 1 target before sending, and to check diagnostics, the host now reads the `schema` and the element ids of the source, and nothing else. - **Work plan, P5** lists `RendererJson.java` as edited: the scalar-only answer reader in it is removed, and `WorkerAnswer` reads the whole frame. - **Work plan, P7** lists `GenerationStore.java` and `DocumentGenerationSchema.java`: neither needed a change (the bootstrap DDL is generated from the table metadata), and `PrintingDispatch.java` does not carry the value. - With renderer 0.2.0 a label source that is not JSON, or has no known schema, is refused as `SCENE_INVALID` with a pointer where 0.1.0 gave the opaque failure. The bridge accepts that from 0.2.0 only. ## Tests - **Version rule matrix** (`VersionRuleTest`): renderer 0.1.0 and 0.2.0 against version 1 and version 2 scenes, what each is sent, the version question and that it holds nothing of the scene or record, a renderer replaced by an older one, an unknown version, and each render target rule. - **Hostile answers** (`WorkerAnswerTest`): bad pointer segments, an undeclared element (also one that appears only in a sample), oversized frames, texts and diagnostic counts, every wrong field count, a refused answer carrying each part of a label, wrong shapes, each rejected by its name. - **`ZplRaster`** (`ZplRasterTest`): every scene 2 golden at 203, 300 and 600 dpi decodes dot for dot to the golden picture of the canvas, and the PNG read back by the platform decoder holds the same dots; all 42 released version 1 labels decode; malformed strips and oversized pages are refused by name. - **Preview** (`PreviewTest`): the golden picture at three resolutions through the bridge, print and preview equality, no files written, the refused page placed at the golden's own corner at three resolutions, old image, too large, version 1 placeholders, and that only an accepted label becomes a picture. - **Mutation campaigns** extended to the page decoder and the answer frames, each with an independent reading of what was accepted (`docs/security/fuzzing.md`). - **Render target** (`GenerationExpectedIntentTest`, `GenerationProcessesTest`, `GenerationPortsContractTest`): carried, stored, part of the intent, digest framing recomputed independently, refusals before any row, typed refusal replayed without rendering, and the migration on H2, MySQL and PostgreSQL (`GenerationQueueJdbcTest` and its two real-engine subclasses). - **Worker side** (`HostContractTest` in `renderer`): the qualification record, the version question, and that collecting diagnostics does not change a label that prints. - The version 1 corpus is untouched and green in both modules. ## Build `./mvnw --batch-mode --no-transfer-progress -s <settings> -T1C verify` on the head `bf546e1`, from a clean tree: **BUILD SUCCESS**, all eleven modules, no skipped test and no skipped gate, 0 Checkstyle violations, every coverage check met. | Module | Tests | |---|---| | `foundation` | 264 | | `renderer` | 306 | | `bridges/renderer` | 207 | | the other five modules with tests | 76 | | all | 853 | Line coverage in `bridges/renderer` (JaCoCo, this build): `ZplRaster` 183 of 183 lines and 132 of 132 branches; `WorkerAnswer` 122 of 122 and 152 of 152; `SceneFacts` 59 of 59; `RendererVersion` 14 of 14. Those four are gated at 100% of lines by name. `IsolatedRendererBridge`, an existing class under the bundle gate, is at 182 of 198 lines. The bundle is at 90.9% of instructions and 88.4% of lines, and its gate rises from 85% and 82% to 90% and 88%, as the POM says to do when coverage rises. The numbers were measured on Windows; the gate is a whole-percent floor under them. ## Deliberately left out - The preview service, action and record widget (P6b), PDF previews (P11) and the `dg:` asset branch (P8). - A picture of the elements that had no error in a refused preview: see the decision above. - A render target for batches, the queue and reprints. - `RemoteRendererBridge.preview` and `supportsRenderTarget` delegate to the bridge in one line each; that module has no test tree and none was added. - No container image was built or pushed, and no release branch was touched. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
james.maes added 2 commits 2026-11-12 18:09:55 +00:00
A request could not say which printer resolution to render a label for:
RenderRequest.targetDpi existed and nothing set it.

DocumentGenerationService.Request gains a trailing targetDpi (absent, 203,
300 or 600; every earlier constructor stays and means none). It is stored
in a new nullable column, target_dpi, on document_generation_request,
compared on retry and lookup beside destination, media and copies, framed
into the intent digest as target-dpi/v1 when stated, and passed to the
renderer. A request without a target has exactly the digest it had, so
earlier rows and their retries are unchanged; the same key with another
target, or with none, is another intent.

Nobody who asks for 300 dpi may silently get another resolution, so a
target is refused before any row exists when it cannot be honoured: for a
PDF, and for any renderer that does not declare the new
IsolatedRenderer.supportsRenderTarget() (false by default, so an adapter
written before the target cannot be handed one and ignore it).

A host applies one statement, from the shipped script for its database:
db/migrations/20261112-render-target-dpi-{h2,mysql,postgres}.sql
(ALTER TABLE document_generation_request ADD COLUMN target_dpi INTEGER
NULL). The generate process form offers the target and the request status
shows it. Batches, the queue and reprints carry none.

Also from the review of the ports: RenderRequest and PreviewRequest hold
their own unmodifiable copy of the context (null values allowed) and
print only their format and render target (and the request id), never the
record data or the template source. Neither wire form changes.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
feat(bridge): accept renderer 0.2.0, read its diagnostics, preview a label (P5)
maven / build (push) Successful in 12m32s
build / maven (push) Successful in 12m32s
bf546e16c5
On develop the renderer speaks qqq-renderer-0.2.0 and the bridge accepted
only 0.1.0, so the bridge refused its own renderer, a label could not be
previewed, and a render target was accepted and then ignored.

The version rule (RendererVersion). The bridge accepts 0.1.0 (the released
fields, the 13 released refusals) and 0.2.0 (the same with diagnostics and
detail, those refusals and the 15 scene refusals); any other version is
rejected. Each set is written out, replacing the temporary enum range. A
job is framed as 0.1.0 reads it unless it needs more. Before a job only
0.2.0 can do (a version 2 scene, a preview) the bridge asks the worker its
version by validating a fixed, empty version 1 scene, so an old image is
refused as RENDERER_TOO_OLD and receives nothing of the scene or the
record; an answer from 0.1.0 to such a job is refused whatever it says.

Diagnostics (WorkerAnswer, SceneFacts). A version 2 refusal is
RenderException(code, diagnostic). The worker is untrusted, so every field
is checked again: the closed vocabulary, whole numbers in bounds, an
element only if the scene that was sent declares it, and a pointer only of
array indexes, schema property names and declared ids. An answer holds at
most 2048 diagnostics in one megabyte beside its label, and no frame
passes the frame of the largest document the deployment admits. A refused
answer that carries any part of a label is rejected.

Render target. targetDpi is sent with a version 2 scene and the label is
held by the output policy to the page at that resolution. A version 1
scene or a PDF asked for another resolution than its stock's is refused as
TARGET_REQUIRES_SCENE_2 before any worker is contacted, because renderer
0.1.0 does not read the field. supportsRenderTarget() is true.

Preview. preview() is the print path stopped before retention: the same
job with collected diagnostics, the same integrity checks and output
policy, and a page decoded by ZplRaster from the accepted label bytes.
ZplRaster is its own strict reader of the policy's grammar and runs the
policy first; it paints ^GFA and ^GB exactly, hatches printer font text
(the page is then not exact) and writes a one-bit PNG itself. Refusals
come back as diagnostics, never as a label. A refused scene is drawn on
the host from the diagnostics' boxes alone: the worker sends no label for
it and the wire gains no field for the partial canvas, so no host can
print a label with elements missing. The design records the decision in
sections 6.1 and B.1, the version question in 11.3 and what the host now
reads from a scene in 8.1.

renderer/qualification.json said qqq-renderer-0.1.0 and qqq-barcodes-1;
it now says what the worker reports, and a test holds it to the answer.

The mutation campaign covers the page decoder and the answer frames. The
new classes are gated at 100% line coverage and the bundle gate rises to
the measured 90% of instructions and 88% of lines. The version 1 corpus is
untouched.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
james.maes merged commit bb25d6c46a into develop 2026-11-12 18:28:13 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: QRun-IO/qbit-document-generation#15