Commit Graph

85 Commits

Author SHA1 Message Date
wpdev 10671e7b78 perf(2meet-data-optimizer): cache found-rows counts for routed queries
- 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
2026-09-26 01:44:45 +08:00
wpdev 754f904e3b perf(2meet-data-optimizer): route status and date ordering to entity tables
- TMDO_Post_Query_Router: when a group's post_status / post_date copies
  are ready and none is NULL (one index probe per group per request), a
  single-post-type search with explicit statuses and date ordering INNER
  JOINs the flat table, filters the copied status, orders by the copied
  post_date and skips GROUP BY. Otherwise the query keeps its old shape.
- New posts_orderby filter; bench gains an `entity+copy` variant.
- Bench (1M, with SQL_CALC_FOUND_ROWS): scenario 1 p95 3,739 -> 773 ms
  (p50 670 -> 770); scenario 2 p50 356 -> 448 ms. Without found-rows the
  same shape runs in 8 ms (PR-030).
- Verify: core unit 659 / integration 471 green, phpcs clean
- PR: PR-029
2026-09-26 01:41:07 +08:00
wpdev aeca12b41d feat(2meet-data-optimizer): store post_status and post_date copies in post entity tables
- 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
2026-09-26 01:25:20 +08:00
wpdev f4eee2338b feat(2meet-data-optimizer): flag new Hot zone registrations as deprecated
- ADR-003 step 4 (core side): TMDO_Schema_Registry::register() raises
  _doing_it_wrong() the first time each provider registers a zone=hot
  field in a request (zone omitted defaults to hot). Fields still
  register, so existing hot data stays readable.
- Cold / warm / archive registrations are unaffected.
- Test bootstrap's _doing_it_wrong stub now records calls.
- Verify: core unit 658 / integration 459, HP 142/49, WC 10/22 green,
  phpcs clean
- PR: PR-038
2026-09-26 01:16:05 +08:00
wpdev 65c75dfc8d feat(2meet-data-optimizer): support composite indexes on entity groups
- register_group() accepts $args['indexes'] (name => ordered keys); indexes on
  unknown keys are dropped. Kept outside the field definition, so schema
  hashes are unchanged.
- Schema_Manager::ensure_group_indexes() adds missing cidx_* indexes to the
  existing flat table in place; the applied definition is remembered in
  tmdo_entity_indexes so later requests skip SHOW INDEX.
- Bench (1M listings, scenario 2): Entity p50 337 ms -> 278 ms with
  (hp_verified, hp_price), on par with Hot (269 ms).
- Verify: core unit 656 / integration 459 green, phpcs clean
- PR: PR-027
2026-09-26 01:05:23 +08:00
wpdev d86c0a7bac feat(2meet-data-optimizer): write flat-table data back to native meta
- TMDO_Native_Restorer copies an entity's flat-table values into its native meta table so the value each key reads today is what native returns afterwards: the owning group (by post type for posts) wins, values are stored as WP stores them, NULL removes the native row
- Resumable per-group id cursor, catch-up pass for rows updated during the run, optional demote to dual_write; CLI wp tmdo bridge-restore-native <entity> [--dry-run] [--batch=<n>] [--demote]
- Verify: NativeRestorerTest (5, user) + NativeRestorerPostTest (2, scoped vs legacy routing); core unit 652 / integration 456, HP 148, WC 10 OK; scale estimate dev21 user ~5.1M / post ~4.0M flat rows
- PR: PR-031
2026-09-26 00:44:53 +08:00
wpdev c84d2409b7 feat(2meet-data-optimizer): warn when HivePress / WooCommerce run without their AddOn
- TMDO_Compatibility::missing_integration_addons() reports active HivePress / WooCommerce whose AddOn bootstrap class is absent
- Surface it as Site Health test wpdo_missing_integration_addons (also on the admin Doctor tab) and an administrator notice (TMDO_Missing_Addons_Notice)
- Verify: MissingIntegrationAddonsTest (5, class stand-ins in separate processes); core unit 651 / integration 449, HP 148, WC 10 OK; read-only check: dev20/21 HivePress+WooCommerce, dev22 HivePress, others clean
- PR: PR-035
2026-09-26 00:36:02 +08:00
wpdev 3d650eb848 docs(2meet-data-optimizer): partner self-registration guide and deprecated AddOn notice
- Add docs/PARTNER-REGISTRATION.md: TMDO_Anti_EAV_Aware bridge setup (plugins_loaded:3), table and entity-group registration with post_types scopes, frozen definitions, and the AddOn → self-registration migration checked with [COLLISION]/[ORPHAN]
- TMDO_Deprecated_Addons_Notice warns administrators while any of the 8 in-house partner AddOns is active (site or network)
- Trait register_entity_fields() forwards an optional $args (post_types) to the registry
- Verify: DeprecatedAddonsNoticeTest (4); core unit 645 / integration 449, HP 148, WC 10 OK
- PR: PR-021
2026-09-26 00:31:34 +08:00
wpdev 0603537f0a refactor(2meet-data-optimizer): resolve post_type/group/table mappings through the registry
- TMDO_Post_Fields::post_type_for_group() / groups_for_post_type() derive HivePress-style groups from their registry post_type scope (core keeps its own attachment / wc_product / nav_menu_item map); the three copy-pasted switches and the query router's POST_TYPE_GROUP_MAP use them
- Term/comment backfill, shadow verifier and CLI take their groups from the registry (TMDO_Term_Comment_Backfill::registered_groups()); installer drops the hp_* entries already covered by its table patterns; REST listings default post type is filterable (tmdo_rest_default_post_type, default 'post')
- Verify: NoHpMappingsTest guard + mapping/default tests; core unit 640 / integration 449, HP 146/49, WC 10/22 OK
- PR: PR-020e
2026-09-26 00:22:20 +08:00
wpdev 8a1c9c0353 refactor(2meet-data-optimizer): remove HPCT legacy from core
- Feature flags keep only the live AddOn modules (wc_orders, latepoint) as ADDON_MODULES / addon_modules(); drop map_hpct_status() and the 7 HivePress HPCT modules whose interceptors were removed in PR-014
- Remove HPCT detection/notice (Compatibility, core), the admin HPCT import tab and status row, CLI import-hpct, the 7 advisor rules for dead modules, and the removed interceptors from COEXIST_WHITELIST
- Verify: NoHpctLegacyTest guard; core unit 635 / integration 449, HP 145/49, WC 10/22 OK; all sites have every module idle and never imported HPCT
- PR: PR-020c
2026-09-26 00:14:18 +08:00
wpdev 1eedaa9b3c refactor(2meet-data-optimizer): stop registering HivePress entity groups in core
- Drop hp_listing_core / hp_request_core / hp_vendor_core from TMDO_Post_Fields and hp_user from TMDO_Member_Fields; the HivePress AddOn registers identical definitions (PR-019/020a/033b)
- Tests: core asserts it registers none of them; HP-specific core tests removed (covered by the AddOn); SyncBridgeEntityGuardTest registers its own hp_price group
- Verify: core unit 633 / integration 449, HP 145/49, WC 10/22 OK
- Deploy: activate the HivePress AddOn on dev20/21/22 first
- PR: PR-020b
2026-09-26 00:07:31 +08:00
wpdev e0683a19b1 feat(2meet-data-optimizer): reconcile post rows written to the wrong group table
- TMDO_Post_Group_Reconciler copies non-NULL values that legacy routing stored in another group's table into the post type's own group (NULL/missing legacy values keep the scoped value), resumable via a cursor, with a catch-up pass for rows updated during the run, then switches on tmdo_post_scoped_routing
- CLI: wp tmdo post-reconcile-groups [--dry-run] [--batch=<n>]; registry exposes get_group_post_types()
- Verify: PostGroupReconcilerPlanTest (4), PostGroupReconcilerTest (6); core unit 635 / integration 449, HP 145/49, WC 10/22 OK; read-only dry-run: dev20 320 values / 0 overwrites, dev21/22 none
- PR: PR-034
2026-09-26 00:04:38 +08:00
wpdev 3f4fe52e1b fix(2meet-data-optimizer): route post entity keys by post_type
- 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
2026-09-25 23:55:49 +08:00
wpdev 1653011f4f feat(2meet-data-optimizer): guard orphaned flat-table groups
- On init:1, re-register aeav_only groups whose flat table exists and whose definition is in wpdo_registry_meta but no active plugin registers them, so their data stays readable (routing only, never DDL)
- Keys owned by a live group, or with rows in native meta (newer data written while orphaned), are left alone; the native-meta check is cached for a day
- Surface restored groups via Site Health (wpdo_orphaned_entity_groups), the admin Doctor tab, and wp wpdo doctor
- Verify: EntityRegistryRestoreTest (9), OrphanGroupRestoreTest (7); core unit 622 / integration 439, HP 141/49, WC 10/22 OK; dry-run on dev20–22 restores 16/17/4 groups, keeps hp_taxonomy hp_sort_order/hp_icon on native
- PR: PR-018
2026-09-25 23:38:12 +08:00
wpdev e9d8696c93 fix(2meet-data-optimizer): report entity group name collisions instead of silently dropping
- register_group() keeps the first definition but records a field diff (added/removed/changed) when the same entity/group is re-registered differently; identical re-registration stays silent
- Surface collisions via Site Health (wpdo_entity_group_collisions), the admin Doctor tab, and wp wpdo doctor; warning log throttled to once a day
- Verify: EntityRegistryCollisionTest (7); core unit 613 / integration 432, HP 141/49, WC 10/22 OK
- PR: PR-017
2026-09-25 23:27:05 +08:00
wpdev 07db7734cf refactor(2meet-data-optimizer): delete dead HivePress defaults and hardcoded partner list
- Remove never-called register_hivepress_defaults() and core boot probes for AddOn classes that load at plugins_loaded:3/:6 and self-register (8 partner list entries, WC orders interceptor, HP transient filter)
- Keep core-owned TMDO_Member_Fields / TMDO_Post_Fields registration unchanged
- Verify: CoreBoundaryTest guard added; core unit 606 / integration 432, HP 141/49, WC 10/22 OK
- PR: PR-016
2026-09-25 23:19:22 +08:00
wpdev 9dc4516ca0 fix(2meet-data-optimizer): filter REST listings collection by post_status and post_type
- GET /wpdo/v1/listings rejects unregistered or non-viewable post types (400) and joins posts so Zone A only lists published posts of that type
- WP_Query fallback now returns only show_in_rest hot columns, matching the Zone A path
- Verify: core unit 605 / integration 432 OK (new RestApi tests); 1M bench: page 7ms, total COUNT 0.12s → 1.7s (tracked in PR-030)
- PR: PR-015
2026-09-25 23:10:46 +08:00
wpdev b407f235ac fix(2meet-data-optimizer): refuse uninstall while data lives only in flat tables
- uninstall.php checks every site first and wp_die()s (keeping tables,
  options and plugin files) on aeav_only modes, misc rows, or zone
  modules past cutover; a failed bootstrap also refuses
- Multisite now checks and cleans every site: network activation is always
  false at uninstall, so only the main site was being cleaned before
- Drop the inline fallback DROP loop, unreachable now that a missing
  helper refuses instead

Verify: UninstallGuardTest (8 tests incl. running uninstall.php single and
  multisite); unit 602 OK, integration 429 OK
PR: PR-008
2026-09-25 22:16:02 +08:00
wpdev fc0f018a94 fix(2meet-data-optimizer): stop auto-activating entity bridge on install
- install() no longer switches the Hook Bus on or puts user/term/comment
  into dual_write; the now-unused maybe_autoactivate_entity_bridge() is gone
- Hook Bus and Mode_Manager defaults are off/disabled; sites with stored
  modes (incl. aeav_only) keep them since only unset keys use defaults
- Guard run_dbdelta()'s upgrade.php require like install_v2_tables() so
  install() can run under the integration test stub

Verify: InstallerV2Test::test_install_does_not_enable_entity_bridge,
  ModeManagerDefaultsTest; unit 602 OK, integration 421 OK
PR: PR-007
2026-09-25 21:48:23 +08:00
wpdev 151aadd661 fix(2meet-data-optimizer): default misc bucket and garbage filter to off
- Both term/comment filters now default to disabled for new installs, so
  activating the plugin no longer reroutes or drops other plugins' meta
- One-time maybe_upgrade() migration keeps the misc bucket on for sites
  that already hold misc rows and never set the toggle (data stays readable)
- Admin toggles show the same defaults as the filters

Verify: phpunit-integration --filter 'defaults_false|preserve_default';
  unit 600 OK, integration 420 OK
PR: PR-006
2026-09-25 21:39:38 +08:00
wpdev d3b72bd1af test(2meet-data-optimizer): add scale benchmark report (native vs hot vs entity)
- Benchmark 7 scenarios at 100k vendors / 1M listings with SQL shaped like
  WP_Query, TMDO_Query_Router and TMDO_Post_Query_Router
- Native meta_query takes 14-17s; flat tables bring searches to 0.3-0.6s
- Hot table lacks an hp_vendor index (3.4s); entity lacks composite indexes

Verify: TMDO_TEST_DB_NAME=wp_wpdo_bench php tests/bench/run.php --iterations=20
PR: PR-005
2026-09-25 21:29:58 +08:00
wpdev 8fc1dc1f1c test(2meet-data-optimizer): add scale seeder for 100k vendors / 1M listings
- Seed a HivePress-shaped dataset three ways (native meta, entity tables,
  hot table) with flat tables built by TMDO's own schema code
- Refuse any database not named *_bench before the bootstrap connects;
  bootstrap now honours a pre-defined ABSPATH for the wp-admin stub
- Full scale seeds 9.5M postmeta rows in 7m25s; 0 value mismatches

Verify: TMDO_TEST_DB_NAME=wp_wpdo_bench php tests/bench/seed.php
  --vendors=100000 --listings=1000000; unit 600 OK, integration 416 OK
PR: PR-004
2026-09-25 20:55:25 +08:00
wpdev 9fa84845be fix: NinjaFirewall 相容性 — 保護 WAF 設定選項不被重導向
評估 NinjaFirewall (WP Edition) 4.9 的相容性,結論是兩者可共存且
不需要開發 AddOn(該外掛全 codebase 零個 apply_filters('nfw_*') /
do_action('nfw_*'),官方相容手段全在部署層)。但核心有一個隱患必須補。

TMDO_Options_Manager::register_settings_group() 以 pre_update_option_{key}
回傳 $old_value,讓選項不再落地 wp_options、改存專屬設定表。而
NinjaFirewall 的 Full WAF 走 auto_prepend_file,在 WordPress 載入前就以
原生 mysqli 直查 wp_options 取 nfw_options / nfw_rules。一旦這些鍵被
重導向,WAF 會讀不到設定而靜默停止防護 —— 不報錯、不寫 log。

nfw_rules 約 77KB 且 autoload=auto,正是 autoload 瘦身最誘人的目標,
因此這條路徑並非理論風險。

Added
- PROTECTED_OPTIONS 常數與註冊守衛(nfw_options / nfw_rules / nfw_checked),
  命中時發出 _doing_it_wrong()。守衛置於方法開頭,全部鍵都被擋時提前返回,
  不再建立空的設定表。
- tests/unit/OptionsManagerProtectedTest.php(4 tests / 10 assertions),
  鎖住「受保護鍵絕不會被掛上 pre_option_* / pre_update_option_* 攔截」。
- docs/WAF-COMPATIBILITY.md:模式差異、symlink 多租戶部署、WP SaaS 開站
  流程與驗證清單、三條開發約束、實測風險矩陣。

Changed
- Migration Wizard 輪詢 500ms → 2s,與四個 stress-test 面板一致。
  原本 2 req/s 打同一 REST endpoint,易觸發 WAF rate-limit 與 bot 偵測。

autoload 最佳化不受影響:optimize_autoload() 只改 autoload 欄位、不刪列,
而 Full WAF 的 SELECT * 不看 autoload。

驗證:591 tests / 1166 assertions 通過,PHPCS 零違規,版本一致性 1.0.2。
dev30 於 Full WAF 與 WP WAF 兩種模式下實測,firewall log 中 TMDO 相關
攔截 0 筆。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdjXAU473eekjPBB8vPVRS
2026-08-15 20:38:09 +08:00
wpdev e33ae4e626 test(boundary): 新增 CoreBoundaryTest,並補完 PR-I 的兩個缺漏測試
Anti-EAV Lint + Quality Gate / anti-eav-lint (push) Successful in 9s
Tests / Unit Tests (push) Successful in 9s
Tests / Integration Tests (push) Successful in 31s
Tests / PHP Lint (push) Successful in 8s
Tests / PHPCS (push) Successful in 20s
Tests / PHPStan (push) Successful in 24s
CoreBoundaryTest 靜態掃描核心 126 個生產檔,斷言每一處對 AddOn 類別的
static 呼叫都在同一個函式內有 class_exists() 守衛。上一個 commit 修的
TMDO_Listing_Stats fatal 就是這類缺陷,這個測試讓它不會再回來。

它當場又抓到 3 處同類違規(都是實際會 fatal 的路徑),一併修掉:
- admin render_hpct_import():改印 admin notice 並 return
- wp tmdo import-hpct:改 WP_CLI::error 明示需要 hivepress-addon
- cli-post cleanup-hp-transients 其實早有守衛,是測試的行距啟發式太窄;
  判斷範圍改成「同一個函式內」而非固定 12 行

負向驗證:暫時注入一處無守衛呼叫 → 測試如預期失敗;還原後回綠。

同時補完計畫階段 7 PR-I 列的兩個缺漏測試:
- 核心 tests/unit/StandardPostInterceptorTest.php(10 tests)
- HP AddOn tests/unit/ListingStatsTest.php(9 tests)——AddOn 的 unit
  bootstrap 先前刻意不載入真實 TMDO_Listing_Stats,改以
  TMDO_TEST_SKIP_LISTING_STATS_STUB 常數讓它跳過核心的 stub
- 核心 unit bootstrap 補 add_post_meta() stub(flush 路徑用得到)

核心 unit 451 → 587、HP AddOn 145 → 154。
2026-07-31 10:41:13 +08:00
wpdev fbe41e2130 release: v1.0.0 — 取代 wp-data-optimizer v3.4.6
Anti-EAV Lint + Quality Gate / anti-eav-lint (push) Successful in 9s
Tests / Unit Tests (push) Successful in 10s
Tests / Integration Tests (push) Successful in 33s
Tests / PHP Lint (push) Successful in 8s
Tests / PHPCS (push) Successful in 20s
Tests / PHPStan (push) Failing after 25s
Release / Test Gate (lint + phpcs + phpstan + unit + integration) (push) Failing after 36s
Release / release (push) Has been skipped
版本 0.1.0 → 1.0.0(含 TMDO_VERSION / TWO_MEET_DATA_OPTIMIZER_VERSION /
WP_DATA_OPTIMIZER_VERSION 與 unit bootstrap)。姊妹外掛只在診斷 CLI 顯示
WP_DATA_OPTIMIZER_VERSION、無版本比較,故報 1.0.0 安全。

wp-data-optimizer 退休:兩站台已停用,本機三個目錄改名為 *.retired
(wp-data-optimizer / -v3.1.1 / _audit),git 歷史保留在 gitea。

CHANGELOG 記錄本次 backport 全貌與 4 項 ABI/行為變更。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TbG1keQQ7XBa7qMQY16KCY
2026-07-31 09:45:22 +08:00
wpdev cd03d66151 test: 補 HookBus 與 audit 寫入的 integration 回歸網(PR-I)
HookBusIntegrationTest(14 tests,自 A 移植)
  integration bootstrap 原本只載入 hook-bus-bridge、沒載入 hook-bus 本體,
  所以連 WPDO_Hook_Bus alias 都建不出來 — 一併補 auto-promoter 與 hook-bus。

AuditLoggerIntegrationTest(4 tests,新寫)
  直接鎖住上一個 commit 修掉的 fatal:
  - after_write 必須寫出一列 audit(覆蓋 Logger::trace_id() 缺失)
  - group_name / action 兩欄必須有值(SCHEMA_VERSION 2.1.0 新增)
  - trace_id 必須是 UUIDv4 且同一 request 內共用
  - 未註冊的 key 不得寫入
  tearDownAfterClass 卸掉 listener 而非 drop 表,否則後續測試類的受管寫入
  會打到不存在的表;integration bootstrap 連帶補 remove_action() stub。

註:TermCommentBackfillTest 未移植 — 它 require HP AddOn 的
class-*-hivepress-term-comment-fields.php,屬 HP AddOn 測試套件而非核心。

unit 451 / integration 416 GREEN、PHPCS 0/0

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TbG1keQQ7XBa7qMQY16KCY
2026-07-31 08:55:12 +08:00
wpdev 56d534619e feat(interceptors): 核心加入 Standard_Post_Interceptor 抽象基底(F1)
Template Method 基底(A v3.0.1),收斂 action_delete_post /
filter_update_meta / action_insert_post 三件套。子類只需宣告
public const FIELD_MAP + get_post_type() + get_table_key() +
build_insert_data()。

HP AddOn 的 4 個 interceptor 隨即改為繼承它(573 → 344 行);未來
LatePoint / bookings / quotation 等 post↔自訂表 1:1 鏡射也可直接用。
back-compat 補 WPDO_Standard_Post_Interceptor alias。

unit 451 / integration 398 GREEN

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TbG1keQQ7XBa7qMQY16KCY
2026-07-31 06:07:20 +08:00
wpdev efab023289 fix(back-compat): hook 橋改雙向 + alias 表修補 + 函式 shim(E1/E2/E5)
E1 雙向 hook 橋
  原本只做 tmdo_* → wpdo_* 單向轉發,但核心一律 do_action('wpdo_*')
  (core:69,275、custom-table-registry:110、options-manager:59),
  所以任何 add_action('tmdo_after_write') 之類的新契約永遠收不到事件。
  改為雙向,並以 $GLOBALS['tmdo_hook_relay_active'] 共用旗標防止
  A→B→A 迴圈與 listener 重複觸發。
  連帶:兩個 test bootstrap 補 do_action_ref_array() stub(單向時不會走到)。

E2 alias 表
  - 刪死條目 'TMDO_Notifier'(B 沒有這個類別,此行永不生效)
  - 補 'TMDO_Abstract_Notifier' => 'WPDO_Abstract_Notifier'(自訂 notifier 基底)
  - trait-wpdo-anti-eav-aware-alias.php 移除與 interface-wpdo-entity-adapter-alias.php
    重複的 WPDO_Entity_Adapter_Interface 宣告(composer 曾警告 Ambiguous class resolution)

E5 函式 shim:wpdo_run() / wpdo_load_textdomain() 委派到 tmdo_ 版本

unit 451 / integration 398 GREEN

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TbG1keQQ7XBa7qMQY16KCY
2026-07-31 06:00:17 +08:00
wpdev feac63173b test(migration): PostMigrationTest / PostEntityLifecycleTest 補 flush_table_exists_cache
引入 table_exists request-scoped cache 後,其他測試類建立/drop 的 sibling
flat 表(wpdo_post_attachment)會在快取留下 stale true,diagnose() 於是
去 COUNT 一張已被 drop 的表 → mysqli_sql_exception。

A 在 v3.4.6 踩過同一個坑,修法相同:兩個測試的 setUp 呼叫
Schema_Manager::flush_table_exists_cache()。

unit 451 / integration 398 GREEN

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TbG1keQQ7XBa7qMQY16KCY
2026-07-31 05:56:36 +08:00
wpdev c46814ce83 refactor(migration): 回填 Migration Phase Strategy 體系(PR-D)
A v3.0.1 把 orchestrator 的 11 個 phase 抽成可注入的 Phase 物件,B 仍是
1107 行單體、以 'phase_' . $current 字串魔法分派、completed 甚至 inline
在 tick() 裡。本 commit 對齊:

新增 13 檔
- includes/migration/interface-migration-phase.php
- includes/migration/class-tmdo-migration-phase-base.php(log/get_managed_keys/
  execute_bulk_pivot/values_loose_equal 等共用 helper)
- includes/migration/phases/ 11 個 phase 類別

orchestrator 1107 → 640 行
- tick() 改 make_phase() 工廠 + $phase->execute($job)
- 移除 final、self::ENTITY_TYPE → static::(A v3.3.0 late static binding)
- 保留 B 原有的 '✓ %s (%.2fs)' 耗時 log(改為 tick 自行量測,A 版已簡化掉)
- 公開介面(preflight/start/tick/get_status/cancel/resume/needs_attention/
  cron_tick)經 diff 確認與 A 完全一致,呼叫端零影響

連帶
- Schema_Manager 補 table_exists 的 request-scoped cache 與
  flush_table_exists_cache()(A v3.1.6 + v3.4.6),Phase 測試需要它
- back-compat 補 12 個 Phase 類別的 WPDO_ alias
- 移植 MigrationPhaseTest + MigrationPhaseRemainingTest(527 行)

測試隔離差異(B 的 unit bootstrap 會載入 Member_Fields / Post_Fields,
A 的不會):MigrationPhaseRemainingTest 的 setUp 需額外清空 Entity_Registry
與 wpdo_register_entity_fields listener,否則 install_schema 會真的走進
dbDelta。MigrationPhaseTest 的 interface 斷言改用 TMDO_ 正式名稱
(PHP 無法 class_alias 介面,且該契約是核心內部擴充點)。

unit 451 / integration 398 GREEN

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TbG1keQQ7XBa7qMQY16KCY
2026-07-31 05:54:49 +08:00
wpdev f187ac5401 refactor(zones): 回填 Zone_Router + Routing_Predicate(PR-C)
A v3.0.1/v3.4.0 的架構深化,B 完全缺席:

新增
- includes/zones/class-tmdo-zone-router.php:module_name / read / write /
  delete_field / delete_post 五個 static 分派點
- includes/class-tmdo-routing-predicate.php:entity_bridge_owns(含
  request-level cache,B 原本沒有)+ should_{write,read,query}_from_zone
  + flush_cache

改寫
- Sync_Bridge 改用兩者,移除 5 個 private wrapper(get_zone_module /
  read_from_zone / write_to_zone / delete_from_zone / is_owned_by_entity_bridge)
  與 44 行 inline cleanup_post → Zone_Router::delete_post 一行(400→269 行)
- Query_Router 抽出 extract_hot_clauses() public static,pre_get_posts 變薄殼
- 收斂 5 處重複的 'hot_'/'cold_' . sanitize_key()(rest-api ×3、
  hot/cold-migration ×2)
- back-compat 補 WPDO_Zone_Router / WPDO_Routing_Predicate alias

測試
- 移植 ZoneRouterTest(250 行)+ RoutingPredicateTest(85 行)
- SyncBridgeEntityGuardTest 的 setUp/tearDownAfterClass 補 flush_cache(),
  否則 entity_bridge_cache 會跨測試污染(A v3.4.6 踩過同一個坑)

unit 409 / integration 398 GREEN

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TbG1keQQ7XBa7qMQY16KCY
2026-07-31 05:47:08 +08:00
wpdev 59f3b19ce6 fix(zone-cold): JSON_MERGE_PATCH 原子寫入 + JSON_REMOVE 原子刪除(B2)
set()/set_many() 原本 get_blob_raw()→array_merge→save_blob() 三步,並發
寫入會互相覆蓋整個 blob;remove() 同樣是 read-modify-write。改為:
- 新增 private save_patch():INSERT ... ON DUPLICATE KEY UPDATE
  data = JSON_MERGE_PATCH(COALESCE(data,'{}'), patch)
- remove():UPDATE ... SET data = JSON_REMOVE(COALESCE(data,'{}'), '$.key')
- save_blob() 改用 TMDO_DB::upsert()
- SQLite 保留 read-modify-write fallback
- ZoneColdTest stub 補 JSON_REMOVE / JSON_MERGE_PATCH / upsert 三種 SQL 模擬

對應 A v3.3.3 P1-21。unit 379 / integration 398 GREEN

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TbG1keQQ7XBa7qMQY16KCY
2026-07-31 05:34:27 +08:00
wpdev 151a14a7e8 fix(zone-warm): warm 表 UNIQUE KEY + set() 改 upsert + 原子 increment(B1/B3)
- CREATE TABLE wpdo_warm 的 meta_key varchar(255)→varchar(191),
  KEY idx_post_meta → UNIQUE KEY ui_post_meta:沒有這個唯一鍵,
  ON DUPLICATE KEY UPDATE 不會觸發,會不斷 INSERT 重複列。
  (既有站台的 ALTER 遷移原本就在 installer:1329,只有 CREATE 落後)
- Zone_Warm::set() 由 SELECT→update/insert 改為 TMDO_DB::upsert()
- 新增 Zone_Warm::increment():CAST(COALESCE(meta_value,0) AS SIGNED)+N
  原子計數,取代 get()+set() 的 read-modify-write race(A v3.3.3)
- ZoneWarmTest stub 與 ZoneWarmIntegrationTest 建表同步適配

unit 379 / integration 398 GREEN

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TbG1keQQ7XBa7qMQY16KCY
2026-07-31 05:31:34 +08:00
wpdev a0847a27bf fix(audit,sso): audit 表補 group_name/action 欄 + 註冊 Audit_Logger + known_login_ips(A11/A12/A14)
- wpdo_audit CREATE 補 group_name varchar(50) 與 action varchar(20):
  TMDO_Audit_Logger::write_row() 一直在寫這兩個 key,表卻沒有該欄位。
  SCHEMA_VERSION 2.0.0 → 2.1.0 觸發既有站台 dbDelta ALTER;主檔與兩個
  test bootstrap 的 TMDO_DB_VERSION 同步(BackCompatTest 守門要求一致)。
- TMDO_Core::run() 註冊 TMDO_Audit_Logger::init():先前完全沒掛,
  wpdo_after_write / wpdo_after_delete 的稽核監聽器形同不存在。
- register_sso_group() 補 known_login_ips(A v3.4.4):2meet-spoke-sso 的
  TMSO_Login_Notify 以 set_entity 寫此欄,未註冊時降級落 wp_usermeta。
  對應測試改為 test_sso_group_has_eight_fields。

unit 379 / integration 398 GREEN

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TbG1keQQ7XBa7qMQY16KCY
2026-07-31 05:16:33 +08:00
wpdev d36bb954d1 chore: initial snapshot of 2meet-data-optimizer v0.1.0
Baseline before backporting wp-data-optimizer v3.0.1-v3.4.6.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TbG1keQQ7XBa7qMQY16KCY
2026-07-31 05:06:36 +08:00