- In shadow_read, TMDO_Hook_Bus::intercept_get() returned the flat value.
update_metadata() reads the old value through that filter after the Hook
Bus has upserted the new one, so WP saw old === new and never wrote native
meta: every update in shadow_read was lost on demotion (dev36 2026-09-26).
shadow_read now returns $check (native) and compares flat on a sample of
reads (filter tmdo_shadow_compare_sample_rate, default 0.1). aeav_only is
unchanged; the post query router still routes in shadow_read.
- Live suite shadow_write: xfail markers removed, HivePress listing case
added (new int key hp_rating_count); fixtures gain post() and a
post_type-scoped field()/flat().
- Verify: live shadow_write + serialized 8/8 PASS (old code: 4 FAIL);
unit 685 OK, integration 484 OK, phpcs (phpcs.xml) 0, stan OK
- PR: PR-056
- CI PHPStan (level 6, baseline for pre-existing debt) failed on 35 errors in
code added on hardening/2.0: missing iterable value types (PHPDoc only), a
redundant method_exists() in uninstall.php, and the new WP-CLI commands
registered as array('Class', 'instanceMethod') — now array(new Class(), ...)
- Local runs had been masked by a PHPStan tmpDir shared with the act CI runner
(cached container pointing at a removed phar); use a private tmpDir
- Verify: phpstan (private tmpDir) no errors, phpcs clean on changed files,
phpunit Unit 658 OK; dev22 (running this branch) CLI post-status-sync /
bridge-restore-native dry runs OK
- TMDO_Post_Query_Router drops SQL_CALC_FOUND_ROWS from routed searches
(posts_request) and answers found_posts_query from a COUNT(*) of the
same query, cached in the object cache under posts' last_changed.
- Hook Bus flat post writes and deletes move posts' last_changed: in
aeav_only they skip update_metadata()'s own hooks, so query caches
would otherwise outlive the write.
- Bench (1M): cached page 8 ms (was 694 / 363 ms for scenarios 1 / 2);
after invalidation 8 ms + COUNT 608 / 344 ms, on par with before.
- REST X-WP-Total COUNT is on the frozen Hot zone path (ADR-003): not
changed.
- Verify: core unit 659 / integration 475, HP 142/49, WC 10/22 green,
phpcs clean
- PR: PR-030
- TMDO_Post_Status_Sync: post group tables get post_status / post_date
columns (posts' collation) and an (post_status, post_date) index.
Brand-new empty tables get them on creation; existing tables only via
`wp tmdo post-status-sync [--dry-run] [--batch]`, never as DDL on a
populated table in a web request. Per-group state in
tmdo_post_status_columns ('columns' | 'ready').
- transition_post_status refreshes the copies; Hook Bus upserts that
create a post row fill them in (existing rows keep theirs).
- Bench (1M rows): add columns 0.05 s, index 5.3 s, batched backfill 32 s,
0 mismatches afterwards.
- Verify: core unit 659 / integration 466 green, phpcs clean
- PR: PR-028
- Entity groups can declare post_types; get_field() resolves a post's key against its own type's groups first, then unscoped ones (legacy last-wins lookup kept for callers without a post type)
- Hook Bus and the post query router pass the post type, so a listing's hp_rating / hp_status / hp_verified map to hp_listing_core instead of the vendor/request tables; core HP groups declare their post types (schema hashes unchanged)
- Behind tmdo_post_scoped_routing: on for sites without post flat rows, off where rows may sit in the wrong table until PR-034 reconciles them (dry-run: dev20–22 off, dev23/24/30/31/40 on)
- Verify: EntityRegistryScopeTest (8), PostScopedRoutingTest (4); core unit 630 / integration 443, HP 144/49, WC 10/22 OK
- PR: PR-033