From 89c14c34d9233720d563dd7d40f00583ded72d40 Mon Sep 17 00:00:00 2001 From: Codex Date: Mon, 18 May 2026 13:06:48 +0200 Subject: [PATCH] [codex] Cover source-added trigger view dependencies --- docs/merge-reliability.md | 3 +- tests/cow/schema_review.php | 61 +++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/docs/merge-reliability.md b/docs/merge-reliability.md index f8274e9c..148ea331 100644 --- a/docs/merge-reliability.md +++ b/docs/merge-reliability.md @@ -131,12 +131,11 @@ Recent additions in the current merge-reliability work: plugin-defined unique/logical keys now hold dependent source child rows with an auditable parent unique-collision reason instead of surfacing only a low-level constraint failure. - | Objective item | Evidence on trunk | Remaining gap | | --- | --- | --- | | 1. Real WordPress semantic merge coverage | `tests/cow/e2e.sh` creates source and target branches through runtime WordPress requests, validates each branch-local graph before merge, then merges pages, branch-local page edits/deletes with edited content and authors, postmeta, users/usermeta, authors, comments/commentmeta, hierarchical taxonomy terms, nav menus and menu locations, reusable `wp_block` rows, synced pattern rows, page-to-reusable-block refs, branch-local `wp_template_part`, `wp_template`, and `wp_global_styles` Site Editor rows, `core/image` block refs and featured-image refs to media attachments, options and JSON options with branch user/object IDs, media uploads with attachment parents plus generated-size metadata/files, a CPT-like `forkpress_note`, and plugin-shaped custom tables/files. The semantic E2E merge now requires `status: completed` and a zero-conflict merge run, so runtime-only state cannot hide behind a surviving object graph. The branch UI E2E also submits branch create without `Accept: application/json` or `X-ForkPress-Async`, and fails if WordPress HTML reaches the caller instead of ForkPress JSON. `tests/cow/merge_smoke.php` now fast-gates page create/create, edit/create, delete/create, page-plus-postmeta create/create, page-plus-comment create/create, page-plus-custom-post-type create/create, Events Calendar-shaped event/venue/organizer CPT create/create and edit/delete with event metadata, taxonomy, and option JSON refs, page-plus-taxonomy create/create, page-plus-menu create/create, page-plus-reusable-block create/create, page-plus-navigation-block create/create, page-plus-template-part create/create, page-plus-template create/create, global-styles create/create, page-plus-attachment create/create, page-plus-image-block create/create, page-plus-gallery create/create, page-plus-file-block create/create, page-plus-media-text create/create, and page-plus-options JSON/serialized create/create invariants without starting WordPress: independent main inserts must survive while branch page inserts, edits, deletes, postmeta graph rows, comment users, usermeta, comments, commentmeta, plugin-like custom post type rows, event plugin CPT rows, CPT postmeta, CPT taxonomy relationships, CPT option refs, terms, term-taxonomy rows, page-term relationships, nav menu terms, menu item posts/postmeta/relationships, merged theme-mod menu locations, reusable `wp_block` rows referenced from block comments, `wp_navigation` rows referenced from navigation comments, `wp_template_part` rows referenced from template-part comments, `wp_template` rows referenced from page template assignments, independent `wp_global_styles` rows, attachment rows, featured-image and attachment metadata, `core/image`, `core/gallery`, `core/file`, and `core/media-text` block JSON refs, upload files, and JSON/serialized option references apply cleanly with zero conflicts while preserving branch-specific IDs without rewrite; denormalized built-in category counts, nav-menu counts, and post `comment_count` values are recomputed from merged relationships/comments while custom/plugin taxonomy counts are left alone. It also fast-gates same-object page/postmeta edit-vs-delete conflicts, user/usermeta edit-vs-delete conflicts, comment/commentmeta edit-vs-delete conflicts, custom-post-type edit-vs-delete conflicts across CPT rows, CPT postmeta, CPT option indexes, and CPT taxonomy relationships, taxonomy-term edit-vs-delete conflicts across terms, term-taxonomy rows, termmeta, and page-term relationships, reusable-block edit-vs-delete conflicts across the `wp_block` row and target page cleanup, navigation-block edit-vs-delete conflicts across the `wp_navigation` row and target page cleanup, template-part edit-vs-delete conflicts across the `wp_template_part` row and target page cleanup, template edit-vs-delete conflicts across the `wp_template` row and target page template assignment cleanup, global-styles edit-vs-delete conflicts across `wp_global_styles` rows, navigation-menu edit-vs-delete conflicts across menu terms, term-taxonomy rows, menu item posts, menu item metadata, relationships, serialized nav-widget options, and theme-mod location cleanup, attachment edit-vs-delete conflicts across attachment rows, metadata rows, original files, and generated files, plus JSON and serialized option edit-vs-delete conflicts. `tests/cow/wp_semantic_validator.php` is a focused fast gate for discovered WordPress semantic validators that catch pages left pointing at deleted reusable blocks, synced patterns, navigation blocks, or template parts, child pages or attachments left pointing at deleted `post_parent` rows, posts or attachments left pointing at deleted `post_author` users, postmeta left pointing at deleted posts, usermeta left pointing at deleted users, nav menu items left pointing at deleted parent menu items, pages, or taxonomy terms, featured-image postmeta left pointing at deleted attachment rows/files, `core/audio`, `core/cover`, `core/file`, `core/image`, `core/video`, `core/media-text`, and `core/gallery` block JSON left pointing at deleted attachment rows/files, classic `wp-image-*` and `[gallery ids="..."]` content left pointing at deleted attachment rows/files, `core/avatar` and `core/latest-posts` block JSON left pointing at deleted users, `core/navigation-link` and `core/navigation-submenu` block JSON left pointing at deleted pages or taxonomy terms, `core/query` block JSON including `taxQuery` and `core/latest-posts` category filters left pointing at deleted author users or taxonomy terms, term relationships left pointing at deleted taxonomy terms, term taxonomy rows left pointing at deleted terms, child taxonomy terms left pointing at deleted parent terms, termmeta left pointing at deleted terms, comments left pointing at deleted posts/users/parent comments, commentmeta left pointing at deleted comments, and options/widgets/theme mods, including block, text, and custom HTML widget content, media widgets, pages widgets, and nav menu auto-add options, left pointing at deleted WordPress objects. It also proves the built-in WordPress page-route validator holds duplicate route-visible page slugs under the same parent, the built-in term-route validator holds duplicate taxonomy/parent/slug routes, the built-in user-login validator holds duplicate case-insensitive `wp_users.user_login` identities, the built-in global-styles validator holds duplicate published `wp_global_styles` style keys, and the built-in Site Editor validator holds duplicate published `wp_template` and `wp_template_part` object keys as review conflicts with structured audit payloads, and the built-in block asset validator holds active-plugin `block.json` `file:` references to missing, URL-like, drive-letter, or otherwise unsafe script, style, render, or other standard plugin-local assets as review conflicts, while ignoring duplicates that predate the merge. Built-in WordPress semantic findings are filterable as `semantic_scope=wordpress` instead of being indistinguishable from ordinary plugin-owned validator findings. `tests/cow/merge.php` adds deterministic WordPress row fingerprint and validator coverage. | Add broader concurrent edit/delete matrices for complete WP objects and deterministic repair policies only where the owner object is unambiguous. | | 2. Plugin-specific merge semantics | `docs/plugin-merge-validators.md` defines the validator contract, including rejecting contradictory status/finding output and optional first-class `severity` and `logical_identity` evidence for plugin-defined object identity. `scripts/cow/merge.php` discovers active plugin and mu-plugin validators, records active plugins that did not ship a discoverable validator as durable `plugin-validator-unchecked` audit decisions, runs explicit validators, records plugin-scoped conflicts, validates first-class plugin validator identity, severity, review-guidance, and logical-identity fields, filters plugin audit queues by plugin, plugin object, plugin severity, and plugin logical identity, groups plugin conflict, event, and resolution queues by the same first-class fields, rolls back inline validator failures, and runs explicit plugin drivers with conflict context, reruns discovered plugin validators before recording applied `plugin-driver` resolutions, fingerprints the pre-driver validator files so a mutating driver cannot delete or rewrite the validator it is supposed to satisfy, rejects repairs that leave the same validator finding open, and records `plugin-driver` resolution evidence without allowing generic source/target resolution of plugin conflicts. Plugin-driver resolution of replacement validator evidence now requires the previous reviewed conflict to have a latest current `replacement-evidence` revalidation that points at the replacement conflict; stale originals, unrevalidated replacements, incompatible revalidations, and drifted replacement evidence are rejected. Ordinary cross-run plugin conflict lineage remains resolvable without pretending it is replacement evidence. `wp-plugin/forkpress-wp.php` exposes configured and plugin-shipped active plugin drivers in the branch switcher with opaque allowlist keys, can run an approved driver for a plugin conflict row, and refreshes the needs-action queue after the driver records its result. `tests/cow/plugin_validator.php` is a focused fast gate for discovered validator review of plugin-owned DB/JSON/file graphs and serialized/JSON option/postmeta/file graphs, plugin-scoped audit output for incoherent JSON, missing or unsafe file references including URL-like and Windows drive-letter plugin file references, stale serialized/JSON asset references, identical validator rerun dedupe, contradictory validator output rejection, malformed validator identity rejection, replacement-evidence revalidation when validator findings change after review, guarded plugin-driver application of the current replacement conflict, cross-run plugin conflict lineage resolution, explicit plugin source-evidence drift recorded by validator reruns, plugin `severity` audit fields, plugin object/severity/logical-identity filters and groupings, generic merge-resolve rejection for plugin conflicts, explicit plugin-driver runner and direct recorder repair resolution audit, non-clearing applied driver rejection, ForkPress-owned driver rollback, validator deletion/rewrite bypass rejection, and `logical_identity` drift returning reviewed findings to `needs-action`. `tests/cow/branch_ui.php` fast-gates the WordPress UI allowlist and active-plugin discovery so browser requests cannot choose arbitrary driver paths. `tests/cow/merge.php` covers clean custom-table graph merges, validator findings, plugin and plugin-severity conflict grouping, audit/review grouping, validator rerun evidence, file-root context, active-plugin discovery, unchecked active-plugin coverage reporting, explicit-ID plugin graph validation, contradictory validator output rejection, and failed-validator rollback. `tests/cow/e2e.sh` covers a runtime plugin-shaped graph across custom table parent/child rows, child JSON payload refs, JSON, serialized data, options, postmeta, CPT data, and branch-owned file contents. | Add validators for real plugins and add merge drivers only for plugin-owned repairs that can prove correctness. | -| 3. Remaining review-only schema cases | `scripts/cow/merge.php` validates source-added views/triggers/indexes, preserves invalid dependency cases as conflicts, and supports safe schema object resolution for deterministic subsets. `tests/cow/schema_review.php` is a focused fast gate proving acyclic source-added dependent views, views depending on source-added tables, trigger programs, and triggers depending on source-added tables including trigger `WHEN` clauses apply in dependency order, source-changed views and triggers apply automatically when the target kept the base object and validation passes, source-changed views/triggers can depend on source-added tables that materialize earlier in the same merge, source-changed triggers can depend on source-added views in trigger bodies or `WHEN` clauses, source-changed view rewrites that would invalidate target trigger bodies stay reviewable, cyclic source-added views/triggers stay reviewable, source-added triggers with missing target dependencies stay gated until the dependency is restored, source-added table/view drops with unresolved dependent target views/triggers/schema objects stay reviewable with blocked source choices until the dependencies are resolved, source-added expression unique indexes blocked by target rows stay reviewable until the blocking rows are removed, and reviewed source-added or source-changed indexes/views/triggers, dropped-table restores, and table rebuild conflicts return to `needs-action` with current source SQL evidence when the reviewed source SQL changes after review. Source-added/source-changed index, view, and trigger source drift can be guarded and source-applied after revalidation when the current source SQL validates against the current target. Dropped-table restore source drift can be guarded and source-applied after revalidation when the target table is still absent and a dry-run restore of the current source payload validates. Table rebuild conflicts also retain rebuild-plan evidence for direct indexes/triggers, dependent views, and dependent view triggers, so dependency-only source drift returns reviewed conflicts to `needs-action`; compatible table rebuild source drift can now replace prior source-applied rebuild dependencies and source-apply after revalidation when the dry-run planner proves the current source rebuild works. It also covers target-side SQL drift for reviewed source-added or source-changed view, trigger, and index conflicts, source-dropped index conflicts, dropped-table restore, and table rebuild conflicts, including current target SQL evidence; source-added/source-changed index, source-dropped index, view, and trigger target drift can be classified as `compatible-schema-*-target-drift` and applied with `--after-revalidate` when dry-run source replacement or drop validates against the current target. `tests/cow/merge.php` covers broader cyclic/invalid view and trigger dependency handling, source-added dependent view/trigger/index ordering, and rebuild validation cases. | Improve dependency planning for more safe reorderings. Add guarded schema revalidation resolution flows beyond validated source/target drift cases where the schema planner can prove compatibility. Cyclic or semantically ambiguous cases should stay review-only. | +| 3. Remaining review-only schema cases | `scripts/cow/merge.php` validates source-added views/triggers/indexes, preserves invalid dependency cases as conflicts, and supports safe schema object resolution for deterministic subsets. `tests/cow/schema_review.php` is a focused fast gate proving acyclic source-added dependent views, views depending on source-added tables, trigger programs, and triggers depending on source-added tables including trigger `WHEN` clauses apply in dependency order, source-added triggers can depend on source-added views in trigger bodies and `WHEN` clauses, source-changed views and triggers apply automatically when the target kept the base object and validation passes, source-changed views/triggers can depend on source-added tables that materialize earlier in the same merge, source-changed triggers can depend on source-added views in trigger bodies or `WHEN` clauses, source-changed view rewrites that would invalidate target trigger bodies stay reviewable, cyclic source-added views/triggers stay reviewable, source-added triggers with missing target dependencies stay gated until the dependency is restored, source-added table/view drops with unresolved dependent target views/triggers/schema objects stay reviewable with blocked source choices until the dependencies are resolved, source-added expression unique indexes blocked by target rows stay reviewable until the blocking rows are removed, and reviewed source-added or source-changed indexes/views/triggers, dropped-table restores, and table rebuild conflicts return to `needs-action` with current source SQL evidence when the reviewed source SQL changes after review. Source-added/source-changed index, view, and trigger source drift can be guarded and source-applied after revalidation when the current source SQL validates against the current target. Dropped-table restore source drift can be guarded and source-applied after revalidation when the target table is still absent and a dry-run restore of the current source payload validates. Table rebuild conflicts also retain rebuild-plan evidence for direct indexes/triggers, dependent views, and dependent view triggers, so dependency-only source drift returns reviewed conflicts to `needs-action`; compatible table rebuild source drift can now replace prior source-applied rebuild dependencies and source-apply after revalidation when the dry-run planner proves the current source rebuild works. It also covers target-side SQL drift for reviewed source-added or source-changed view, trigger, and index conflicts, source-dropped index conflicts, dropped-table restore, and table rebuild conflicts, including current target SQL evidence; source-added/source-changed index, source-dropped index, view, and trigger target drift can be classified as `compatible-schema-*-target-drift` and applied with `--after-revalidate` when dry-run source replacement or drop validates against the current target. `tests/cow/merge.php` covers broader cyclic/invalid view and trigger dependency handling, source-added dependent view/trigger/index ordering, and rebuild validation cases. | Improve dependency planning for more safe reorderings. Add guarded schema revalidation resolution flows beyond validated source/target drift cases where the schema planner can prove compatibility. Cyclic or semantically ambiguous cases should stay review-only. | | 4. Filesystem merge hardening | `tests/cow/filesystem.php` is a focused fast gate for safe source text/binary file application, conflicting binary file edits staying target-kept with hash payload metadata instead of text decoding, safe relative symlinks can merge, unsafe symlinks to absolute paths, root-escaping paths, self-references, and ForkPress-managed paths remain conflicts, reviewed source resolution cannot force-apply unsafe symlinks, reviewed source directory-subtree resolution cannot force-apply unsafe symlinks nested inside a replacement directory, and unsupported special source filesystem entries remain review-held and cannot be source-applied on platforms with FIFO support. Directory/file and file/directory replacements get type-specific review conflicts, unchanged target descendants and source descendants under reviewed replacements are held until review, source directory deletions with target-side descendants are held with source resolution blocked before any descendant deletion, reviewed source replacements can apply supported file/dir/symlink changes including safe directory subtrees, reviewed filesystem source drift can be source-applied after revalidation only when the latest source and target payloads still match the revalidation record, and WordPress upload files still referenced by the merged attachment metadata are protected from source-side deletes as file conflicts before semantic validation runs. WordPress E2E links attachment rows to original and generated-size upload files, plugin-shaped E2E checks branch-owned file contents, and PHP coverage uses a discovered validator to cross-check attachment metadata against merged upload files, missing required attachment metadata rows, invalid serialized attachment metadata, malformed `image_meta`, invalid original/generated/backup dimensions, original/generated/backup filesize drift, attachment `post_mime_type`, byte-signature MIME drift, and generated-size/backup `mime-type` drift against known upload file extensions including AVIF and PDF, missing or empty metadata-side original file fields, generated-size, `original_image`, and backup filename drift, upload paths that exist as non-file entries, missing original/generated upload files including metadata-side original, `original_image`, and backup image files, attached-file metadata drift, duplicate original/generated/backup upload ownership, and unsafe primary/metadata/generated/`original_image`/backup upload paths, including root-escaping, dot or empty path segments, URL-like primary/metadata/generated, and Windows drive-letter primary/metadata upload metadata. `tests/cow/merge.php` covers file adds/deletes/conflicts, binary hash comparisons, symlink safety, directory/file and file/directory replacement review, rollback artifacts, upload-file validators, generated and backup attachment file checks, original/generated dimension drift, malformed `image_meta`, generated-size filename drift, featured-image/image-block/media metadata drift, and unsafe metadata paths. `tests/cow/e2e.sh` verifies real merged upload originals and generated thumbnails. | Add stricter uploads-specific validators for more drift shapes and implement a deterministic media repair driver only if WordPress can prove exact regeneration. | | 5. Crash consistency across DB/files/metadata/Git | `docs/merge-crash-consistency.md` lists the covered boundaries. `tests/cow/merge.php` covers target DB, metadata, file, rollback-failure, ID-band, and whole-branch rollback paths. `tests/cow/plugin_validator.php` covers a plugin-driver failpoint after a mutating driver returns but before `plugin-driver` resolution metadata is written, proving target DB/files are restored and no resolution is recorded for ordinary pre-resolution failures. It also kills the plugin-driver runner at the same boundary, verifies the target DB/files may be partially mutated before recovery, verifies the pending `plugin-driver-resolution` crash artifact blocks retries, exposes that artifact through `merge-audit --records crash-recovery`, and restores target DB/files through `recover-crash --restore-target-db --restore-files`. `crates/forkpress-storage/src/lib.rs` and `scripts/cow/git_server.php` publish post-merge and Git-created-branch DB/filesystem merge-base snapshots by atomic replacement instead of remove-before-rename, with focused storage and Git-server tests proving failed publication keeps the previous snapshot. `tests/cow/e2e.sh` drives public merge/create/reset/recover crash/retry flows for DB, metadata, before-file, after-file, recovery-cleanup, branch-birth, branch-reset publication failpoints, restores a pending public merge crash through the WordPress branch UI restore action, and covers actual smart-HTTP Git-created branch pushes interrupted before branch-birth metadata, after branch-birth metadata but before branch tree publication, after branch storage publication, before branch-list publication, and after branch-list publication, each verified after a fresh server restart. `tests/cow/git_server.php` covers Git-created branch birth, Git update/delete, stale cleanup, object-prune interruption, and atomic merge-base file publication. | Broaden external kill harness coverage across the remaining platform-specific Git publication and APFS/cleanup checkpoints, then verify post-crash state from a fresh process. | | 6. Branch birth always captures merge bases | `crates/forkpress-storage/src/lib.rs` requires branch birth metadata for branch reuse/merge and blocks pending reset states. `tests/cow/branch_birth.php` fast-gates required ID bands, keyless row identities, filesystem merge-base capture as a frozen pre-write snapshot with managed DB/config/Git exclusions, cleanup of rollback metadata, and cleanup isolation for unrelated branch metadata. `tests/cow/git_server.php` covers Git-created branch DB/file base, ID-band, row identity, decision/run metadata, branch-birth decision cleanup, DB merge-base sidecar cleanup, file-base cleanup, and cleanup/rollback paths. `tests/cow/e2e.sh` covers public create retry after interrupted birth metadata, public reset retry after interrupted reset publication, and remote-cache branch creation followed by AUTOINCREMENT-band insertion and mergeback to `main`. | Keep every new creation/reuse/reset path under the same invariant and add regressions whenever a new branch publication path is introduced. | diff --git a/tests/cow/schema_review.php b/tests/cow/schema_review.php index cd52a1ac..796b2d4f 100644 --- a/tests/cow/schema_review.php +++ b/tests/cow/schema_review.php @@ -1826,6 +1826,67 @@ function create_schema_trigger_order_db(string $path): void { 'source-added trigger can use source-added body and WHEN dependency tables after merge' ); + $trigger_source_view_base = $tmp . '/trigger-source-view-base.sqlite'; + $trigger_source_view_source = $tmp . '/trigger-source-view-source.sqlite'; + $trigger_source_view_target = $tmp . '/trigger-source-view-target.sqlite'; + $trigger_source_view_metadata = $tmp . '/.forkpress/cow/merge/schema-trigger-source-view-metadata.sqlite'; + + $db = open_db($trigger_source_view_base); + $db->exec('CREATE TABLE plugin_trigger_source_view_items (item_id TEXT PRIMARY KEY, label TEXT NOT NULL)'); + $db->close(); + copy($trigger_source_view_base, $trigger_source_view_source); + copy($trigger_source_view_base, $trigger_source_view_target); + + $source_db = open_db($trigger_source_view_source); + $source_db->exec('CREATE TABLE plugin_trigger_source_view_audit (item_id TEXT, label TEXT)'); + $source_db->exec('CREATE TABLE plugin_trigger_source_view_suffixes (suffix_key TEXT PRIMARY KEY, suffix TEXT NOT NULL)'); + $source_db->exec("INSERT INTO plugin_trigger_source_view_suffixes (suffix_key, suffix) VALUES ('default', ':source-view')"); + $source_db->exec('CREATE TABLE plugin_trigger_source_view_gate (enabled INTEGER NOT NULL)'); + $source_db->exec('INSERT INTO plugin_trigger_source_view_gate (enabled) VALUES (1)'); + $source_db->exec("CREATE VIEW plugin_trigger_source_view_suffix_view AS SELECT suffix FROM plugin_trigger_source_view_suffixes WHERE suffix_key = 'default'"); + $source_db->exec('CREATE VIEW plugin_trigger_source_view_gate_view AS SELECT enabled FROM plugin_trigger_source_view_gate WHERE enabled = 1'); + $source_db->exec("CREATE TRIGGER plugin_trigger_source_view_items_after AFTER INSERT ON plugin_trigger_source_view_items WHEN EXISTS (SELECT 1 FROM plugin_trigger_source_view_gate_view) BEGIN INSERT INTO plugin_trigger_source_view_audit (item_id, label) SELECT NEW.item_id, NEW.label || suffix FROM plugin_trigger_source_view_suffix_view; END"); + $source_db->close(); + + $trigger_source_view_result = cow_merge_databases( + $trigger_source_view_base, + $trigger_source_view_source, + $trigger_source_view_target, + $trigger_source_view_metadata, + 'feature-schema-trigger-source-view', + 'main' + ); + $trigger_source_view_run_id = (int)$trigger_source_view_result['run_id']; + assert_same($trigger_source_view_result['status'], 'completed', 'source-added triggers depending on source-added views merge automatically'); + assert_same( + (int)scalar($trigger_source_view_target, "SELECT COUNT(*) FROM sqlite_master WHERE type = 'view' AND name IN ('plugin_trigger_source_view_suffix_view', 'plugin_trigger_source_view_gate_view')"), + 2, + 'source-added trigger body and WHEN dependency views install before trigger validation' + ); + assert_same( + (int)scalar($trigger_source_view_target, "SELECT COUNT(*) FROM sqlite_master WHERE type = 'trigger' AND name = 'plugin_trigger_source_view_items_after'"), + 1, + 'source-added trigger depending on source-added views installs' + ); + assert_same( + (int)scalar($trigger_source_view_metadata, "SELECT COUNT(*) FROM merge_conflicts WHERE run_id = $trigger_source_view_run_id AND conflict_type IN ('schema-source-added-view', 'schema-source-added-trigger')"), + 0, + 'source-added trigger dependencies on source-added views create no review-only schema conflicts' + ); + assert_same( + (int)scalar($trigger_source_view_metadata, "SELECT COUNT(*) FROM merge_decisions WHERE run_id = $trigger_source_view_run_id AND column_name IN ('plugin_trigger_source_view_suffix_view', 'plugin_trigger_source_view_gate_view', 'plugin_trigger_source_view_items_after') AND decision = 'source-applied'"), + 3, + 'source-added views and dependent trigger creation are auditable' + ); + $target_db = open_db($trigger_source_view_target); + $target_db->exec("INSERT INTO plugin_trigger_source_view_items (item_id, label) VALUES ('source-view-trigger', 'Source View Trigger')"); + $target_db->close(); + assert_same( + scalar($trigger_source_view_target, "SELECT label FROM plugin_trigger_source_view_audit WHERE item_id = 'source-view-trigger'"), + 'Source View Trigger:source-view', + 'source-added trigger can use source-added body and WHEN dependency views after merge' + ); + $trigger_dependency_base = $tmp . '/trigger-dependency-base.sqlite'; $trigger_dependency_source = $tmp . '/trigger-dependency-source.sqlite'; $trigger_dependency_target = $tmp . '/trigger-dependency-target.sqlite';