Conversation
The canvas and the Production Preview both showed something about render fidelity, but neither named the render origin, the document-wide preview authority never left Approximate, and no surface said that an approximate overprint render is not proof of print-safe output (#28). Add pdfquick::tokens::classifyPreviewFidelityState / previewFidelityOriginName / resolvePreviewFidelityStateVisual and project them through EditorHost as previewFidelityStateName / previewFidelityOriginName / previewFidelityVisual / previewFidelityColor / previewFidelitySummary / previewRequiresAuthoritative. Both the ordinary canvas banner and the Production Preview render that one projection: fidelity unavailable/stale/exact/approximate/authoritative, origin none/fast-canvas/output-preview. The overprint-sensitive fast render is the `approximate` state, whose summary says its pixels cannot stand as proof of print-safe output and which no state resolves to a pass. EditorHost::ensureAuthoritativePreview() is the explicit switch the preview surfaces call before a page is presented as proof: it moves the current page to the output-preview render and returns false when it is already authoritative or exact. The Production Preview gains a "proof this page" affordance bound to it. Proof: UnitTestsLoopStateVisual (classification, origin, distinct shapes, never a pass), UnitTestsEditorHost::previewFidelityNamesTheOriginAndSwitchesExplicitly (the projection and the explicit switch on a real overprint fixture), and UnitTestsPageSurface::fastCanvasOverprintRenderIsNotTheAuthoritativeGolden (the fast render does not match overprint-cmyk-mode1-on.png while the authoritative render does, and the fast diagnostics report the approximation).
Run the product Quick accessibility qualification against a staged copy of the installed tree, not the developer build tree, on Linux and Windows. The native and software runs write separate records with distinct claims; Windows drives the native UI Automation client and requires it; Linux records the OS accessibility lane unavailable. scripts/ci/verify_quick_accessibility_evidence.py fails closed when a required lane is missing, failed, captured outside an installed tree, or replaced by a software-only record.
Present the before/after artifact identities, the technical finding delta, the preserved attributes and the unresolved risk Core already produced for the plan or run on screen; let the operator navigate material deltas; and block a stale preview or mismatched plan digest instead of silently refreshing it. - ComparePane.qml reads EditorHost.compareReview(), a read-only projection of fixPlanIdentity/fixPreview/fixRecheck/fixSignOff (no second comparison model in QML); isWorkspaceEnabled(Compare) is now true and the placeholder pane is retired. - UnitTestsProductOperatorLoop: a golden comparison fixture pins the Core PDFRepairFindingDelta classification, and a stale-preview test proves the guard blocks and refuses navigation; UnitTestsShellWorkspace proves Compare is reachable.
…erator shell (#27) Add the operator-journey proof that a missing or partial inspection stays visible and never reads as a clear document, in both the mapped product-operator-loop suite and the Quick interaction smoke. - UnitTests/tst_productoperatorloop.cpp: run the shipped representative fixture (bleed-missing.pdf) under the corpus restriction-pages profile through the real preflight engine, and assert the observable progress, the incomplete verdict, the operator summary/limitation and Core's incomplete visual treatment (never a pass). - tools/ProductQuickAccessibilitySmoke: add an incomplete-inspection fixture to the software-backend interaction smoke and chain it after the existing finding-navigation (representative) fixture. The representative open/select-find/navigate journey already shipped with the L02 slices; it is covered, not re-implemented. No Core, editor-host or QML behavior changes.
Add the acceptance fixture for issue #30: an unapproved plan, a rejected plan, a plan whose document revision has moved, and a plan replanned after approval each neither execute nor publish anything; a run requires an approval given for the plan displayed at that moment; and completion presents the revalidated verdict with the 64-hex digest of the artifact it published, distinct from the as-received input. The fixture is mutation-probed: removing the execution arming check, the revision comparison, the superseded predicate or the review clearing each makes it fail. The reviewed-digest equality in fixLifecycleStateName() and the fixPlanIsCurrent() clause in approveActionListPlan() are defensive against a state the journey offers no path to, and are documented as such rather than claimed as coverage.
…he async open Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…lane on any platform Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ritative-but-inexact visible Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…rkspace Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…or-shell L03-01: keep a partial inspection visible in the one-document Quick operator shell (#27)
…l-journey Prove the plan and approval journey publishes only an approved plan
…qualification chore(ci): qualify native accessibility against the installed package (#31)
…review Separate the preview fidelity and origin from the fast canvas (#28)
L03-03: Add the Compare and review workspace (#29)
…into the plan digest (#33) Make PDFRepairRegistry the single registration authority: registerOperation now returns PDFOperationResult and refuses a null operation, an empty id, and a duplicate id (first registration wins). The two registration units capture the result and Q_ASSERT it so a debug build fails loudly. A public default constructor is the isolated-registry test seam; instance() stays the production singleton. Bind registry identity into the plan contract: PDFRepairRegistry::digest() is the SHA-256 of the canonical {id, version} set, and computeOperationPlanDigest adds it as registry_digest to the operation-plan envelope, so a registry change moves every plan digest and invalidates bound approvals. Envelope schema_version stays "1.0": additions are non-breaking. Close the four failure gates before candidate computation: - duplicate registration is refused instead of silently replaced; - unknown operation stays refused at find()/add(nullptr); - ambiguous parameters are refused by PDFRepairTransaction::add() through the shared validateJsonSchemaFragment() (lifted from pdfactionlist.cpp, which now delegates to it), and PdfTool repair refuses a repeated --param key; - PDFRepairTransactionOptions::expectedSourceSha256 refuses a stale source revision at the top of analyze() before any operation runs. Tests: UnitTestsRepairOperation gains duplicate-registration, unknown-operation, ambiguous-parameter and stale-revision slots; UnitTestsGovernedExecution gains registry-digest format/stability and a plan digest pinned to the committed operation-plan golden vector (envelope test updated for registry_digest); UnitTestsPdfToolContract gains the repeated --param refusal with a positive control. Fixtures: canonical-json/ operation-plan-envelope.json plus expected-plan-digests.json pin the cross-platform plan-envelope bytes. Verified locally (MSVC + Qt 6.11.1, Ninja, C:/.dev/repos/loop-build-l04): - cmake --build --target LoopLibCore PdfTool UnitTestsRepairOperation UnitTestsGovernedExecution UnitTestsPdfToolContract UnitTestsActionList: ok - ctest -R '^(UnitTestsRepairOperation|UnitTestsGovernedExecution|UnitTestsPdfToolContract|UnitTestsActionList)$': 4/4 passed (75.7 s) - clang-format --dry-run --Werror on every touched C++ file: clean - python scripts/agent/check-architecture.py (static): ok - python scripts/agent/test_architecture_contracts.py: OK (1 skipped) - python scripts/ci/test_correction_operation_catalog.py: OK - python scripts/generate-architecture-catalogs.py --check: ok Skipped lanes: packaging:linux-build / packaging:windows-build are hosted CI; the mapped check-change proof runs at the coordinator. UnitTestsActionList ran as a manual regression check (target not mapped in agent-policy). Migration decision (recorded in docs/GOVERNED_EXECUTION.md): legacy pdftool addbleed/rgbtocmyk stay registry-metadata consumers; convergence is L04-05 (#37) scope.
…33) validateJsonSchemaFragment recorded required/additionalProperties violations without failing, so a caller that consumes only the bool (the Action List planner) accepted an unknown key or a missing required parameter. The lifted behaviour is preserved everywhere else; the structural branches now fail the result, and PDFRepairTransaction::add() refuses on that single contract again. Tests: a validator-contract slot in UnitTestsRepairOperation (missing required, unknown key, nullptr error sink, valid control) and an Action List slot in UnitTestsActionList (unknown key + missing required refused on a recipe). UnitTestsRepairOperation / UnitTestsActionList / UnitTestsGovernedExecution / UnitTestsEditorHost / UnitTestsProductOperatorLoop: 5/5 passed.
…heck (#33) valuesEqual serialised both sides through QJsonValue::toObject(), which yields an empty object for any non-object value, so every pair of scalars compared equal and the enum branch accepted any correctly-typed value (an add-bleed mode of "stretch" passed an enum of mirror/pixel-repeat). Compare the typed values instead, and pin the refusal with validateJsonSchemaFragment_rejectsValuesOutsideTheAllowedSet. Measured: the new slot fails on the pre-fix validator and passes with the fix; UnitTestsRepairOperation 34/34, UnitTestsActionList cli-parity/dry-run slots pass.
…nd enforce it at every write (#34) Carry the operation-declared save policy to every Action List surface and hold each write boundary to it. - Every Action List step plan now reports the declared policy as save_policy (plan() and the execute-time rebuild), and PDFActionListExecutionResult carries the conservative merge over all step operations (PDFActionListExecutor::mergedSavePolicy, mirroring PDFRepairTransaction::savePolicy()), serialized as save_policy. Interpretation (P1): "preserved attributes" are the declared consequences themselves - mode, invalidates_signatures, reversible_in_session, rationale - not a new field; nothing may substitute a different policy for them. - computeActionListPlanDigest binds the merged policy into the action-list-plan envelope, parity with computeOperationPlanDigest (P2). - PDFActionListExecutionOptions::requestedSavePolicy may ask for more safety than the declaration; a weaker request is refused with action-list.save-policy-refused before any step work, in plan() and execute() alike (dry runs included). Stricter requests are accepted; within one execution the refusal is not retried past (execute() consumes the refused plan result). The executor holds no cross-call latch because options are supplied per call. - PdfTool action-list run/batch and the Editor publication boundary build the PDFSaveRequest and run pdf::validateSaveRequest before writing the candidate (save-policy.refused + processing failure on refusal). The run path's ad-hoc output==input check is subsumed: the Core validator covers every reachable collision (the input exists whenever the write is attempted) and also refuses canonical-path aliases. - PdfTool repair arms transactionOptions.sourcePath and validates the publish destination through validateOperationSaveRequest with appendInPlace=false (P3: repair never appends in place), closing the --output <source> --overwrite hole before any publication side effect. Tests: UnitTestsActionList gains stepPlansCarryDeclaredSavePolicy, executeRefusesWeakenedRequestedSavePolicy, executionResultReportsMergedSavePolicy, fullClassStepCannotBeNarrowed and oracleClassStepFailsClosedWithoutIndependentValidation; UnitTestsOperationImpact gains undeclaredImpactSelectsFullRevalidation; UnitTestsPdfToolContract gains repairRefusesToWriteOverItsOwnInput. RED vs guard: stepPlansCarryDeclaredSavePolicy is RED (verified by mutation: removing the two step-plan assignments fails it); repairRefusesToWriteOverItsOwnInput is RED (verified by mutation: without the publish validation the run exits 0 and publishes over its own input); executeRefusesWeakenedRequestedSavePolicy and executionResultReportsMergedSavePolicy exercise API added by this change. fullClassStepCannotBeNarrowed, oracleClassStepFailsClosedWithoutIndependentValidation and undeclaredImpactSelectsFullRevalidation pass unchanged pre-change and pin existing fail-closed behaviour at the Action List target (guards). Mapping: UnitTestsActionList is claimed by the core and interaction policy lanes and leaves migration.deferred_targets; UnitTests/tst_actionlisttest.cpp joins interaction.paths and UnitTests/tst_operationimpacttest.cpp joins core.paths. Verified: clang-format --dry-run --Werror on all touched C++ files (clean); build of LoopLibCore/PdfTool/LoopLibInteraction/UnitTestsActionList/ UnitTestsRepairOperation/UnitTestsOperationImpact/UnitTestsGovernedExecution/ UnitTestsPdfToolContract; ctest -R '^(UnitTestsActionList|UnitTestsRepairOperation|UnitTestsOperationImpact|UnitTestsGovernedExecution|UnitTestsPdfToolContract)$' - 5/5 passed; scripts/agent/check-architecture.py, scripts/agent/test_architecture_contracts.py, scripts/ci/test_correction_operation_catalog.py, scripts/generate-architecture-catalogs.py --check - all green. Skipped lanes: packaging linux-build/windows-build (hosted CI), sealed-eval stub check_independent_validation_gate.py (holdout only).
L04-02: enforce operation-owned save and impact policy (#34)
mberrys
added this pull request to stack #223
October 6, 2026 03:56
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
Promotes the current
devtrain towardunstable. This range delivers the remaining L03 Quick operator-workspace slices in this train—partial-inspection proof, explicit production-preview fidelity/origin, Compare review, exact-plan approval proof, and installed-package accessibility qualification—alongside the first two L04 governed-operation contract slices for registry/plan identity and operation-owned save/impact policy.GitHub currently reports the branches as diverged:
devis 28 commits ahead of and 3 commits behindunstable. The merge base isc9546514d219ab7d6dd9ba681f15f593eb90b441; the currentunstabletip is275e272331a9ba607bc2eb74f8ee38641f726aa4; this PR head is1077bd864e85e3862f4254e87e6497b9a3a66bd3.Release changelog
dev → unstable — Quick workspace and governed repair contracts
This promotion advances the operator-facing Quick workspace and the governed repair boundary without changing persistence formats or introducing a second PDF/publication authority. It does not claim final stable-branch release admission.
Scope: merge base
c9546514→1077bd86. GitHub compare reports 28 commits ahead; the available connector does not expose a verified non-merge-only count. Currentunstableis also 3 commits ahead of the merge base.Added
ComparePanepresents exact before/candidate/published artifact identities, finding deltas, preserved attributes, and unresolved risk from Core-owned review data; stale previews and mismatched plan digests are blocked instead of silently refreshed.Changed
PDFRepairRegistrybecomes the single registration authority; registry identity is bound into deterministic plan digests; parameter/schema validation, duplicate registration, stale-source revision, and repeated CLI-parameter failures are rejected before candidate execution.PdfTool repairrefuses publication over its trusted input even with--overwrite.Internal
Refs / admission status
This PR targets
unstable, not the defaultstablebranch, so it intentionally does not close the implementation issues here.Refs #27 — software Quick interaction fixtures are recorded; the evidence manifest still notes the Windows-only native-UIA host-inspection lane was not run for this slice.
Refs #28 — focused fidelity tests are recorded locally; remaining mapped Quick/interaction coverage is delegated to hosted Linux/Windows CI.
Refs #29 — evidence manifest reports no unresolved items; keep open through stable/default-branch admission.
Refs #30 — evidence manifest reports no unresolved items; keep open through stable/default-branch admission.
Refs #31 — installed-package accessibility evidence manifest reports no unresolved items; keep open through stable/default-branch admission.
Refs #33 — independent-validation/sealed-eval coverage and canonical digest-stability enforcement remain outstanding in the evidence manifest; hosted packaging lanes are cited rather than locally reproduced.
Refs #34 — independent-validation/sealed-eval coverage remains outstanding, and the evidence manifest notes focused Action List/PdfTool runtime tests for the PR 212 review fixes were not rerun against that earlier isolated head.
Verification
At PR head
1077bd864e85e3862f4254e87e6497b9a3a66bd3, GitHub currently returns no PR-triggered workflow runs or combined status checks through the connected API, so this body does not claim a green head-level CI result.The committed evidence manifests in this range name the mapped unit, architecture, packaging, differential, fixture, and security lanes supporting each slice and explicitly retain the unresolved items listed above.
Breaking changes
None. These changes tighten execution/publication validation and add/extend operator surfaces and evidence contracts; no persistence-format or public schema migration is introduced by the changelog fragments in this promotion.
Security and rollback
unstableto its prior admitted tip and re-run the promotion after reconciling the three commits currently unique tounstable.Docs
Updated in this range, including governed execution, incremental save, Quick accessibility, renderer differentials, repair operations, shell/workspace contracts, CI, and packaging/licensing documentation.