Files
qbit-document-generation/docs/EXPECTED-PRINT-INTENT.md
ee782b7d6e
QRun-IO/ci-policy: ci / fork-guard (push) Skipped
QRun-IO/ci-policy: ci / head-profile (push) Skipped
QRun-IO/ci-policy: ci / application-template-0 (push) Skipped
QRun-IO/ci-policy: ci / gradle-1 (push) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (push) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (push) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (push) Skipped
QRun-IO/ci-policy: ci / node-8 (push) Skipped
QRun-IO/ci-policy: ci / node-9 (push) Skipped
QRun-IO/ci-policy: ci / node-10 (push) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (push) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (push) Skipped
QRun-IO/ci-policy: ci / source-config-13 (push) Skipped
QRun-IO/ci-policy: ci / source-config-14 (push) Skipped
QRun-IO/ci-policy: ci / android-0 (push) Skipped
QRun-IO/ci-policy: ci / original-business (push) Skipped
QRun-IO/ci-policy: ci / original-mcp (push) Skipped
QRun-IO/ci-policy: ci / original-voyage (push) Skipped
QRun-IO/ci-policy: ci / original-cli (push) Skipped
QRun-IO/ci-policy: ci / not-enrolled (push) Failing after 7s
QRun-IO/ci-policy: native / fork-deny (push) Skipped
QRun-IO/ci-policy: native / dotnet-0 (push) Skipped
QRun-IO/ci-policy: ci / repository-policy (push) Skipped
QRun-IO/ci-policy: ci / java-core-2-head (push) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (push) Skipped
QRun-IO/ci-policy: ci / ci-result (push) Failing after 5s
QRun-IO/ci-policy: native / native-result (push) Successful in 6s
chore: preserve unfinished document generation source and status
Checkpoint implementation at user-requested stop; document known failures and incomplete release qualification.

Co-Authored-By: Claude <noreply@anthropic.com>

Co-Authored-By: GPT-6 Codex <noreply@openai.com>
2026-11-04 10:45:09 -06:00

9.0 KiB

Expected generation intent and read-only print acceptance

DocumentGenerationService.Request accepts an optional Expected(templateId, revisionId, Stock) tuple. Existing five-argument construction keeps its prior behavior: accept the current publication on first use and reuse the accepted publication for the same actor/request key. An explicit expected tuple compares every dimension, margin and DPI against the immutable published revision.

Admission shares the template row lock used by publication. The current publication comparison and generation request insertion commit together before context execution, render, intake or print. The lock needs template read ceilings; it does not grant template editing rights. Existing publication/edit paths retain their mutation ceilings. The accepted request's revision reference and immutable revision stock snapshot retain the tuple without another registry or schema.

Result.accepted returns that exact tuple. Result.intentSha256 is a versioned, length-framed digest of the accepted actor/key/kind/source/template/revision/stock/ format/destination identity. It contains no source payload. Supplying or omitting an equivalent expected tuple does not create a second accepted intent. A later publication does not alter completed replay; a new key carrying the old tuple is refused. Preview keeps its separate draft-fingerprint contract and refuses an expected published tuple.

lookupAcceptedPrint(Request) requires an explicit tuple and destination, the current actor's view/print capabilities, native source/retained-owner read authority, and credential/channel read ceilings. It queries the actor's existing generation request and passes its original operation ID to the optional printing adapter. It never calls generate, reconcile, submit, advances state, or writes a generation audit event. An absent request or absent accepted print returns empty; unavailable or uncertain provider lookup throws. A found receipt can therefore report the Printing owner's committed acceptance while Generation remains INCOMPLETE after a lost response. Lookup does not repair that state.

The default PrintDispatch.find refuses unsupported lookup. The optional Printing bridge uses only RetainedPrintFacade.find, validates its exact retained source, actor, destination and acceptance event, and does not read output bytes. No Printing or Record Files types enter the Generation foundation. Server acceptance is not a device observation or proof of physical output.

The coordinated owner gate passed 61 foundation tests (34 service, five expected intent, 22 file-H2 queue tests) and ten Printing bridge tests, with zero failures, errors and Checkstyle violations. Evidence: target/expected-print-intent-verification.log, terminal session 83722. This is source/package verification, not a published artifact, deployment or physical-print qualification.

Selected media and typed refusals: subsequent candidate

The subsequent source candidate adds nullable GenerationPorts.MediaProfile(id, revision) to Request and PrintRequest. Existing five- and six-argument Request constructors, five-argument PrintRequest and four-/six-argument Result constructors remain available. Profile IDs use [A-Za-z0-9._-]{1,128} and revisions use exact lowercase SHA-256 hex, matching the Printing owner's public media contract. Neither destination nor absent media implies a default choice.

Apply the shipped 20261101-print-media-profile-h2.sql or MySQL counterpart before adopting the new metadata. Both additions on document_generation_request are nullable. Old rows keep null profile identity and their prior intent digest. A nonnull choice adds a separately framed media domain/id/revision to the digest; changing, adding or omitting that choice cannot reuse an accepted key. Native request status exposes both columns. Batch/queue APIs have not silently gained media options. Existing reprints retain their prior no-profile contract.

PrintDispatch.supportsMediaProfile() defaults to false. The current legacy Printing bridge still returns that default: its old owner stores arbitrary options but does not enforce current media identity/revision. Generation refuses such a selection before creating an intent, and direct bridge submit/find also refuse before contacting that owner. The bridge's bounded options.mediaProfile encoding is staged for the corrected owner cohort, not enabled by a claim that arbitrary JSON storage establishes media support. A host may adopt a reviewed owner enforcement explicitly with PrintingDispatch.withMediaCapability(...) (a copy; the default instance stays denied; evaluated per request for submit and lookup). Tests against the old facade prove only that the bounded options.mediaProfile binding reaches submit and that revoking the capability denies both paths. A true callback is not proof the owner enforces current media: the Printing 2.2 owner must expose a typed enforcement capability that is wired and verified, with the actual pre-spool profile/stock check, before any Echo production claim. Actual profile/stock compatibility and pre-spool enforcement remain Printing/scanner-owner qualification requirements.

The foundation now defines twelve closed FailureCode values and a RenderException(FailureCode) with only the enum name as its public message. A verified renderer refusal is stored in the existing errorCode column and thrown again on incomplete replay without rendering again. Current native process failure/status surfaces therefore expose the fixed code, not a payload or encoder message. Result adds nullable failureCode and mediaProfile; generic operational failures keep GENERATION_INCOMPLETE. No new error schema or parallel web API is introduced. The paired renderer bridge verifies the complete error envelope before constructing this exception; unknown/malformed responses remain generic refusals.

The coherent media/typed-error test gate passed 135 tests: 94 foundation (34 service, ten expected-intent, 28 process, 22 file-H2 queue), 30 renderer bridge and eleven Printing bridge tests. All reported zero failures, errors, skips and Checkstyle violations. Evidence: target/expected-media-typed-error-verification.log, terminal session 70653. This was the Maven test phase using freshly compiled current classes; it was not a packaging or native-image gate. The earlier 61+10 result above applies to its original source snapshot.

The actual H2 migration test removes both media columns from an accepted legacy null-profile request, applies the shipped additive SQL, and verifies unchanged receipt/digest, one request/submission and no additional audit events. The MySQL DDL was inspected but was not executed in this gate. The selected-media positive test uses an explicitly capable fixture; the actual legacy Printing bridge is separately tested to refuse selected media. No real-owner selected-media support, new renderer image, device observation or physical output is claimed by this gate.

Copies per print: source candidate, 2026-11-02

An owner-handoff source candidate, not a deployed or public API release. Nothing here was built or run; the pinned combined gate is run separately.

DocumentGenerationService.Request and GenerationPorts.PrintRequest gain a trailing Integer copies, normalized in the canonical constructor: absent means one and anything outside 1 through 100 throws IllegalArgumentException("Copies must be an integer from 1 through 100."), so an invalid count cannot reach admission, rendering or submission. Every earlier constructor stays and means one copy. The count is how many copies the one queued job prints; it is never a number of jobs.

  • Persistence. One nullable copies column on the request table; apply 20261102-print-copies-h2.sql or the MySQL counterpart before adopting. Earlier rows stay null and read as one copy.
  • Idempotent retry and lookup. The stored count (null normalized to one) is compared with the request's in the same check as destination and media, so a different count under the same request key is refused as a different generation intent, for both generate and lookupAcceptedPrint. A legacy default-one retry still matches.
  • Digest. One copy (including every legacy row) keeps the exact earlier intent digest; a larger count adds a framed copies/v1 domain.
  • Dispatch. PrintDispatch.supportsCopies() defaults to false. A count above one, or a count above one without a destination, is refused with PRINT_COPIES_UNSUPPORTED or "Copies require a print destination" before any row, render or submit. Reprints and queued/batch generation keep one copy.
  • Printing bridge. PrintingDispatch.supportsCopies() is true and sends options.copies (one submission) only when the count is above one, so options of one-copy requests are byte-for-byte unchanged ({}). The committed acceptance options must carry exactly the requested count; an absent count matches only one copy. It requires a Printing owner artifact whose RetainedPrintFacade honors options.copies (1 through 100); the facade itself is unchanged.