feat(2meet-data-optimizer): report flat-vs-native drift with wp tmdo drift-check

- 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
This commit is contained in:
2026-09-26 23:56:23 +08:00
parent 382786cb9d
commit 02045c6f07
7 changed files with 543 additions and 1 deletions
+75
View File
@@ -186,6 +186,79 @@ class TMDO_CLI_V2 {
WP_CLI::success( "{$entity}: {$from} → {$mode}" );
}
/**
* Compare an entity's flat tables with native meta (read-only).
*
* Reports per key how many values are missing from flat (miss), differ
* (diff) or exist only in flat (extra), and records the outcome per group
* in the wpdo_drift_status option.
*
* ## OPTIONS
*
* <entity>
* : post | user | term | comment
*
* [--group=<group>]
* : Only this group. Default: every backfillable group.
*
* [--sample=<n>]
* : Check n random entities per group instead of all of them.
*
* [--format=<format>]
* : table or json. Default: table.
*
* ## EXAMPLES
*
* wp tmdo drift-check post
* wp tmdo drift-check user --group=activity
* wp tmdo drift-check post --sample=500
*
* @param array<int, string> $args Entity type.
* @param array<string, string> $assoc_args Options.
*/
public function drift_check( $args, $assoc_args ): void {
$entity = (string) ( $args[0] ?? '' );
$sample = max( 0, (int) ( $assoc_args['sample'] ?? 0 ) );
try {
$report = TMDO_Drift_Check::run( $entity, (string) ( $assoc_args['group'] ?? '' ), $sample );
} catch ( InvalidArgumentException $e ) {
WP_CLI::error( $e->getMessage() );
return;
}
if ( 'json' === ( $assoc_args['format'] ?? 'table' ) ) {
WP_CLI::line( (string) wp_json_encode( $report ) );
return;
}
$rows = array();
foreach ( $report['groups'] as $group => $result ) {
foreach ( $result['keys'] as $key => $counts ) {
if ( $counts['miss'] + $counts['diff'] + $counts['extra'] > 0 ) {
$rows[] = array_merge(
array(
'group' => $group,
'key' => $key,
),
$counts
);
}
}
}
if ( $rows ) {
\WP_CLI\Utils\format_items( 'table', $rows, array( 'group', 'key', 'miss', 'diff', 'extra' ) );
}
$total = $report['total'];
$summary = sprintf( '%s: %d group(s)%s — miss=%d diff=%d extra=%d', $entity, count( $report['groups'] ), $sample ? " (sample {$sample})" : '', $total['miss'], $total['diff'], $total['extra'] );
if ( $total['miss'] + $total['diff'] + $total['extra'] > 0 ) {
WP_CLI::warning( $summary );
return;
}
WP_CLI::success( $summary );
}
/**
* Write an entity's flat-table values back to native meta — the way out of
* aeav_only. Resumable; re-run after an interruption.
@@ -954,6 +1027,8 @@ WP_CLI::add_command( 'wpdo bridge-set', array( 'TMDO_CLI_V2', 'bridge_set' ) );
WP_CLI::add_command( 'tmdo bridge-set', array( 'TMDO_CLI_V2', 'bridge_set' ) );
WP_CLI::add_command( 'wpdo bridge-mode', array( new TMDO_CLI_V2(), 'bridge_mode' ) );
WP_CLI::add_command( 'tmdo bridge-mode', array( new TMDO_CLI_V2(), 'bridge_mode' ) );
WP_CLI::add_command( 'wpdo drift-check', array( new TMDO_CLI_V2(), 'drift_check' ) );
WP_CLI::add_command( 'tmdo drift-check', array( new TMDO_CLI_V2(), 'drift_check' ) );
WP_CLI::add_command( 'wpdo bridge-restore-native', array( new TMDO_CLI_V2(), 'bridge_restore_native' ) );
WP_CLI::add_command( 'tmdo bridge-restore-native', array( new TMDO_CLI_V2(), 'bridge_restore_native' ) );
WP_CLI::add_command( 'wpdo repair-array-literals', array( new TMDO_CLI_V2(), 'repair_array_literals' ) );