Repository navigation
L04-01: reconcile the operation registry and canonical plan contract (#33) - #208
Merged
Merged
Conversation
This was referenced Oct 5, 2026
…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.
mberrys
force-pushed
the
l04-01-registry-plan-contract
branch
from
October 5, 2026 07:14
3d7649f to
3effd51
Compare
…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.
mberrys
force-pushed
the
l04-01-registry-plan-contract
branch
from
October 5, 2026 13:00
bd9d12d to
cbad561
Compare
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.
Summary
L04-01 (#33), the first slice of the L04 Governed Operation Engine stack (8 slices, one per issue; refs #5). It makes
PDFRepairRegistrythe single registration authority, binds a versioned registry digest into the canonical operation-plan digest, and closes the four failure gates before candidate computation: duplicate registration, unknown operation, ambiguous parameter, stale revision.Stack position
Slice 1 of 8. Base:
dev. Slices 2-8 (l04-02...l04-08) are stacked on this branch; land bottom-up so each child merges cleanly.Per-criterion: already satisfied vs built here
registerOperationrefused silently replacing (insert_or_assign); now refuses null/empty/duplicate, first wins; both registration TUs assert.duplicateRegistration_isRefusedPDFRepairRegistry::digest()+registry_digestin theoperation-planenvelope; pinned golden vectorUnitTests/testdata/canonical-json/operation-plan-envelope.json+expected-plan-digests.json;planDigest_matchesPinnedGoldenVectorduplicateRegistration_isRefusedunknownOperation_isRefusedBeforeCandidatevalidateJsonSchemaFragmentused byPDFRepairTransaction::add(); repeated CLI--paramrefused.ambiguousParameters_areRefusedBeforeAnalyze,repairRefusesRepeatedParameterAssignmentPDFRepairTransactionOptions::expectedSourceSha256checked at the top ofanalyze();staleRevision_isRefusedBeforeAnalyzeaddbleed/rgbtocmykstay registry-metadata consumers; convergence deferred to L04-05 (#37). docs/GOVERNED_EXECUTION.mdRequired proof
python scripts/agent/check-change.py --base e1a14184 --head cbad5614 --head-branch l04-01-registry-plan-contract --build-dir <loop-build-l04>-> status: pass, 83/83 checks (re-run after the third commit and after the stack was rebased onto the currentdev) (builds LoopLibCore/PdfTool/loop-pdf-worker, 52 mapped suites, clang-tidy on all changed sources, format, changelog, architecture contracts).focused_testsonUnitTestsPdfWorkerIsolationonly (208.8 s, vs 145-159 s when green). Standalone reruns of that suite at the same head: 2/2 pass; the same suite passed in the concurrent batch proof at the base tree. A timing-sensitive fault-injection suite (135-150 s windows), measured flake rate 1 in 4 today; the re-run proof is fully green.clang-format --dry-run --Werror: clean on every touched C++ file.check-architecture.py(static +--base 35ff26fcdiff mode),test_architecture_contracts.py,test_correction_operation_catalog.py,generate-architecture-catalogs.py --check-> all green.registry_digestc05cab19c20096d2208463e4159884c1d350f9c8accc5e9144086ac1f5371315; plan/envelope digestd712302a4682021ff3d7a1c9d214cdc0a7a4041b4651138be670c51e3846e711.agent-faston this head.Notes
Closes #33(issue closes on the promotion merge; this PR lands ondev).required/additionalPropertiesviolations without failing, so the Action List planner accepted unknown keys. It now fails on any recorded violation, pinned byvalidateJsonSchemaFragment_reportsStructuralViolationsandrejectsUnknownAndMissingStepParameters.valuesEqualserialised both sides throughQJsonValue::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 ofstretchpassed an enum ofmirror/pixel-repeat). The earlier "unreachable because every enum schema carries a type check" justification was wrong: the type check only rejects wrong-typed values. It now compares typed values, pinned byvalidateJsonSchemaFragment_rejectsValuesOutsideTheAllowedSet(measured red on the pre-fix validator, green after).Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.