Skip to content

fix(audit): keep campaign history in write order within the same second - #166

Merged
TheAggressive merged 1 commit into
masterfrom
fix/history-order
Oct 8, 2026
Merged

TheAggressive merged 1 commit into
masterfrom
fix/history-order

Conversation

@TheAggressive

Copy link
Copy Markdown
Owner

Closes #164. Part of #65.

Problem

Audit events store whole-second created_at values and random IDs, and history was ordered created_at DESC, id ASC. Events written in the same second therefore showed in arbitrary order. For example, a scripted create → snapshot → submit → approve listed "Approved" below "Created".

Change

  • Schema 6 adds sequence_number to {prefix}campaignbridge_audit_events. It's a unique AUTO_INCREMENT insertion order.
  • History is now ordered created_at DESC, sequence_number DESC. The target_created index still serves the filter.
  • Migration: dbDelta adds a column and its index in separate statements, and MySQL refuses an AUTO_INCREMENT column that isn't a key. So Schema_Manager adds the column and its key in one ALTER before dbDelta runs, and dbDelta then sees nothing to change. Fresh installs get the column from the CREATE TABLE.
  • Existing events are numbered in the order the database reads them, because their order within a second was never recorded. No existing value changes.
  • Docs:
    • api.md: the history order.
    • campaign-persistence.md: the new column.
    • runbook.md: how to roll back to a schema-5 release. That code never reads the column, so lowering the stamped version is safe.

Evidence

  • Migration from schema 5 on a populated table (Campaign_Schema_Migration_Test):
    • existing events get distinct, non-zero numbers;
    • a re-run changes nothing;
    • resuming after an interruption (column present, version still 5) changes nothing;
    • the next insert numbers above the existing rows;
    • the index list includes sequence_number.
  • Same-second ordering (Campaign_Operation_Repository_Test): four events in one second, with IDs that sort against write order, read back newest write first, including across pages. A later second still sorts first even when written earlier.
    • Restoring the old id ASC ordering makes this test fail.
  • SQLite (WordPress Studio): the migration on the local site kept all 281 audit events. Each got its own number, the column is auto_increment, and a second run was a no-op.

Verification

  • pnpm qa:fast passes.
  • The unit (1126), integration (431), security (117), accessibility (25), and performance (7) suites all pass.

🤖 Generated with Claude Code

Audit events store whole-second timestamps and random IDs, and history was
ordered created_at DESC, id ASC, so events written in one second (a scripted
create, snapshot, submit, approve) appeared in arbitrary order.

Schema 6 adds sequence_number, a unique auto-increment insertion order, to
the audit events table, and history is now ordered created_at DESC,
sequence_number DESC. dbDelta adds a column and its index in separate
statements, which MySQL refuses for an AUTO_INCREMENT column, so the
migration adds the column and its key in one ALTER before dbDelta runs;
the CREATE TABLE carries the same definition for fresh installs. Existing
events are numbered as the database reads them, since their order within a
second was never recorded. Re-running, or resuming before the version is
stamped, changes nothing. Verified on MySQL in CI's harness and on the
Studio SQLite site (281 events, every one numbered).

The runbook documents rolling a release back to schema 5: that code ignores
the new column, so lowering the stamped version is safe.

Closes #164

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added area:php PHP or Composer code area:tests Automated tests or test configuration risk:high Security or release sensitive; never auto-merged type:fix Corrects a defect needs-attention Automation stopped; maintainer action required labels Oct 8, 2026
@TheAggressive
TheAggressive merged commit f618aa7 into master Oct 8, 2026
21 checks passed
@TheAggressive
TheAggressive deleted the fix/history-order branch October 8, 2026 07:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:php PHP or Composer code area:tests Automated tests or test configuration needs-attention Automation stopped; maintainer action required risk:high Security or release sensitive; never auto-merged type:fix Corrects a defect

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(audit): order campaign history deterministically within the same second

1 participant