| @@ -442,9 +442,10 @@ | ||
| 442 | 442 | * refreshes its own copy, but nothing rewrites the STORED digest, so the |
| 443 | 443 | * bucket mismatches forever and the till shows a permanent "records need |
| 444 | 444 | * attention" for data that is already correct. Observed on dev-pro |
| 445 | 445 | * 2026-08-19: 138 products drifted by a hookless bulk edit, re-escalated |
| 446 | - * every sweep, local copies byte-identical to the server. | |
| 446 | + * every sweep, local copies byte-identical to the server. Missing stored | |
| 447 | + * digests in a partially populated table likewise need rebuilding. | |
| 447 | 448 | * |
| 448 | 449 | * Why a STREAK and not the first sight of drift: for a hookless write the |
| 449 | 450 | * integrity scan is the ONLY signal — such a write bypasses the sequence |
| 450 | 451 | * log too — so re-baselining on first detection would erase the one thing |
| @@ -461,9 +462,9 @@ | ||
| 461 | 462 | */ |
| 462 | 463 | private function maybe_schedule_stale_digest_rebuild( int $bucket, int $bucket_size, array $changes ): void { |
| 463 | 464 | $stale = 0; |
| 464 | 465 | foreach ( $changes as $change ) { |
| 465 | - if ( 'changed' === ( $change['status'] ?? '' ) ) { | |
| 466 | + if ( \in_array( $change['status'] ?? '', array( 'changed', 'missing_stored' ), true ) ) { | |
| 466 | 467 | ++$stale; |
| 467 | 468 | } |
| 468 | 469 | } |
| 469 | 470 | |
| @@ -474,9 +475,9 @@ | ||
| 474 | 475 | |
| 475 | 476 | $streak_key = $bucket_size . ':' . $bucket; |
| 476 | 477 | |
| 477 | 478 | if ( 0 === $stale ) { |
| 478 | - // Reconciled (or only deletions/missing_stored left) — forget it. | |
| 479 | + // Reconciled (or only deletions left) — forget it. | |
| 479 | 480 | if ( isset( $streaks[ $streak_key ] ) ) { |
| 480 | 481 | unset( $streaks[ $streak_key ] ); |
| 481 | 482 | $this->save_drift_streaks( $streaks ); |
| 482 | 483 | } |