Skip to content

L04-02: enforce operation-owned save and impact policy (#34) - #219

Merged
mberrys merged 3 commits into
devfrom
l04-02-save-impact-policy
Oct 5, 2026
Merged

mberrys merged 3 commits into
devfrom
l04-02-save-impact-policy

Conversation

@mberrys

@mberrys mberrys commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Enforce each operation's declared save policy across Action List plans, execution, plan digests, and publication boundaries. A weaker save request is refused before step execution, and PdfTool repair/action-list output cannot overwrite the trusted source through a different path spelling.

This replaces closed PR #212 against dev, which now contains its prerequisite #208. The existing branch retains the review fixes for empty-selection plans and failed batch save-policy reports. Closes #34.

Validation

  • Review of the rebuilt diff against current dev: no additional findings.
  • 18 source/contract checks passed, including changelog, architecture contracts, generated adapters, and formatting.
  • MSVC syntax checks passed for the affected Action List implementation and focused test sources.
  • Full local proof remains incomplete: no configured build for this head, so build/runtime tests and clang-tidy were unavailable.

The change stays scoped to the operation-owned save/impact policy slice; the updated parent is included without rewriting history.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

…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).
@mberrys
mberrys merged commit 1077bd8 into dev Oct 5, 2026
6 of 13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant