Skip to content

metadata-protocol: the package commit timeline sorts by weekday NAME on Postgres/MySQL — rollbackToPackageCommit therefore plans the wrong set of reverts #13995

Description

@zhuangjianguo

Found by the driver-materialisation consumer census on #13973 (class (c) — genuinely wrong on one side). That sweep is not addressed by this card and does not close it.

The two sites, one root cause

packages/metadata-protocol/src/protocol.ts.

1. listCommits ordering — line 18262:

// Newest-first; tolerate drivers that don't order by returning
// insertion order, then sort by the ISO timestamp.
mapped.sort((a, b) => String(b.createdAt ?? '').localeCompare(String(a.createdAt ?? '')));

The comment states the assumption in as many words: "sort by the ISO timestamp". createdAt is r.created_at off this.engine.find('sys_metadata_commit', …) — a record read door.

2. rollbackToPackageCommit planning — lines 18843-18845:

const targetCreatedAt = String(target.created_at ?? '');
const toRevert = all.filter(
  (c) => String(c.createdAt ?? '') > targetCreatedAt && c.operation === 'apply',
);

Lexicographic > over the same values, to decide which commits get reverted.

Why it is wrong on the production default driver

created_at is a builtin audit column: not in datetimeFields, and SqlDriver#formatOutput repairs it only inside if (this.isSqlite). Pinned in packages/drivers/driver-sql/src/sql-driver-13567-audit-stamp-materialisation.test.ts — the live dialects hand it out of the record read door as a JS Date, SQLite as canonical ISO-Z text.

So on Postgres/MySQL both sites compare strings of the form

Sun Aug 30 2026 18:19:25 GMT+0800 (China Standard Time)
Mon Aug 31 2026 09:02:11 GMT+0800 (China Standard Time)

whose leading token is the weekday name. Lexicographic order over that set is Fri < Mon < Sat < Sun < Thu < Tue < Wed — unrelated to chronology, and stable across the whole file, so the failure is systematic rather than intermittent.

Consequences:

  • listCommits returns the package timeline in an arbitrary weekday-name order while claiming newest-first.
  • rollbackToPackageCommit consumes that ordering and re-derives the same comparison itself, so it reverts a set of apply commits chosen by weekday name: commits older than the target get reverted, commits newer than it get skipped. That is a destructive operation planning off a wrong predicate.

On SQLite both sides are canonical ISO-Z text, lexicographic order equals chronological order, and both sites are correct. Every test they have drives SQLite or memory.

Why this is not a ?? fallback

Per #13973's standing prohibition, the question is which side owes the canonical spelling:

A regression pin needs a Date on the read side. @objectstack/metadata-protocol has no driver dependency and must not grow one (the layering runs the other way), so the pin drives a hand-made Date here — the same split sql-driver-13567-audit-stamp-materialisation.test.ts documents for the OCC seam.

Re-run

rg -n 'String\((b?\.)?(target\.)?created_?[Aa]t' packages/metadata-protocol/src/protocol.ts

Backlink: #13973 (census), #13382 (the OCC seam, the same shape, same file). Neither is addressed here.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdomain:enginepriority:p1High: required for production / M2

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions