Skip to content

[codex] Guard COW row merges against target trigger rewrites#45

Merged
adamziel merged 1 commit into
trunkfrom
codex/cow-merge-logic-hardening
May 14, 2026
Merged

[codex] Guard COW row merges against target trigger rewrites#45
adamziel merged 1 commit into
trunkfrom
codex/cow-merge-logic-hardening

Conversation

@adamziel
Copy link
Copy Markdown
Contributor

@adamziel adamziel commented May 14, 2026

What it does

Prevents COW mergeback from silently accepting source row inserts, updates, or reviewed source conflict resolutions when target-side triggers rewrite the row during the apply step.

When a source row write succeeds, ForkPress now verifies the row in target still matches the audited source or merged payload. If a target trigger rewrote or removed it, the write is rolled back to a per-row savepoint and the case stays reviewable instead of being recorded as a misleading source-applied result.

Rationale

The merge logic already handled SQLite constraint failures, unique collisions, foreign-key ordering, and trigger/schema validation. The remaining rough edge was successful target-trigger side effects: SQLite could accept the write, a target trigger could change the row, and the audit log would still claim the source payload landed exactly. That is a bad surprise for plugin tables with triggers.

The same guard is needed when a reviewer explicitly chooses the source side of a row conflict. A reviewed source choice should either land the audited source payload byte-for-byte at the row level, or fail validation and leave both the target DB and resolution metadata unchanged.

Implementation

  • Added savepoint-backed source insert/update wrappers.
  • Added a rowid-preserving insert guard for keyless row conflict resolution paths.
  • After insert/update, reload the applied target row by primary key or keyless rowid.
  • Compare the actual target row with the intended payload.
  • Roll back trigger-mutated writes and record or preserve a reviewable conflict with trigger-specific wording.
  • Route reviewed source row resolutions through the same postcondition guard.
  • Preserve the existing full-transaction rollback behavior for RAISE(ROLLBACK) triggers, where SQLite removes savepoints as part of the transaction abort.
  • Preserve the existing cell-conflict SQL hook messages so rollback regression tests still exercise target and metadata failure paths.

Testing instructions

php -l scripts/cow/merge.php
php -l tests/cow/merge.php
php tests/cow/merge.php
git diff --check

php tests/cow/merge.php passes with 2038 assertions, including coverage for target triggers rewriting source inserts/updates and reviewed source resolutions remaining validation-gated with no resolution metadata recorded.

@adamziel adamziel force-pushed the codex/cow-merge-logic-hardening branch from 42090e5 to 9eba805 Compare May 14, 2026 20:26
@adamziel adamziel marked this pull request as ready for review May 14, 2026 20:27
@adamziel adamziel merged commit 938dd30 into trunk May 14, 2026
8 of 9 checks passed
@adamziel adamziel deleted the codex/cow-merge-logic-hardening branch May 14, 2026 20:28
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