- When an entity goes from disabled to dual_write / shadow_read, or the Hook Bus flag is switched on, a single WP-Cron event (tmdo_drift_resync, one per entity, 60 s later) runs drift-check --fix for the entities that keep native meta, so values written while flat was bypassed land in flat. aeav_only is never touched.
- A full fix instead of the planned post_modified_gmt increment (decision 2026-09-27): meta-only writes such as WooCommerce's direct stock SQL do not bump post_modified. No wpdo_bridge_off_since.
- The resync hook is cleared on deactivate / uninstall (literal name: uninstall.php does not load TMDO_Drift_Check).
- Verify: DriftCheckTest resync cases (RED before); dev36 end to end with real WP-Cron: write while user disabled → miss 1 → bridge-mode user dual_write schedules tmdo_drift_resync [user] → run → drift 0.
- PR: PR-074
- In aeav_only flat is the source of truth and native meta is stale by design, so drift there means nothing: drift-check --fix refuses (it would overwrite current flat values with stale native ones), the daily sample skips such entities, and the router keeps routing a drifting group instead of falling back to stale native meta.
- Found before PR-074 on hardening/2.2 (PR-071–073 not released; dev21 still runs aeav_only).
- Verify: DriftCheckTest / PostQueryRouterDriftTest aeav_only cases (RED before); dev36 fix() in a simulated aeav_only refused.
- PR: PR-073b
- TMDO_Drift_Check::cron_tick() runs on the existing wpdo_daily_health_check event (no new schedule to add or clear): every entity type whose flat tables are written (dual_write or higher) is checked on 500 random entities per group, and the outcome replaces the recorded status, so a group found drifting leaves the router that day.
- Verify: DriftCheckTest cron cases (RED: method / hook missing); dev36 with all four entities in dual_write: one tick over 22 groups in 10 s, 0 drift, status option restored.
- PR: PR-073
- TMDO_Drift_Check::fix() deletes flat rows of deleted entities and, for post groups, of posts outside the group's post types; rebuilds every other drifted entity's row from native through the new TMDO_Entity_Migration_Engine::migrate_entity() (keys without native rows become NULL, PR-066); keeps a row whose entity has no native value for the group but clears its columns (no NULL-only inserts, no deleted rows the router may join on); invalidates the row cache of each touched entity; then checks again. Native meta is never written.
- `wp tmdo drift-check <entity> --fix` (not combinable with --sample).
- Verify: DriftCheckTest fix cases (RED: method missing); dev36 --fix: post 42 rows rebuilt, 1 stray row (post 312, hp_booking in hp_draft_flags) deleted, all four entities 0 drift after, checker miss/extra 0.
- PR: PR-071
- New TMDO_Drift_Check compares every backfillable group of post / user / term / comment with native meta by value (first native row → safe unserialize → Type_Caster::to_db): miss / diff / extra per key, set-based SQL with BINARY exact matches, PHP comparison only for the remaining candidates. Serialized-vs-JSON and 50 vs 50.000000 are no longer drift; over-long text stored as NULL is not a miss; extra covers rows of other post types. Outcome per group goes to the wpdo_drift_status option; nothing else is written.
- `wp tmdo drift-check <entity> [--group] [--sample=<n>] [--format]`. A new class instead of extending the post-only shadow verifier, which queries row by row and writes shadow_diffs.
- Verify: DriftCheckTest; live suite drift; dev36 post miss=533 diff=2 extra=1 in 23 s (checker's 45 json false diffs gone), user/term/comment 0.
- PR: PR-070