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.
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.
listCommitsordering — line 18262:The comment states the assumption in as many words: "sort by the ISO timestamp".
createdAtisr.created_atoffthis.engine.find('sys_metadata_commit', …)— a record read door.2.
rollbackToPackageCommitplanning — lines 18843-18845:Lexicographic
>over the same values, to decide which commits get reverted.Why it is wrong on the production default driver
created_atis a builtin audit column: not indatetimeFields, andSqlDriver#formatOutputrepairs it only insideif (this.isSqlite). Pinned inpackages/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 JSDate, SQLite as canonical ISO-Z text.So on Postgres/MySQL both sites compare strings of the form
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:
listCommitsreturns the package timeline in an arbitrary weekday-name order while claiming newest-first.rollbackToPackageCommitconsumes that ordering and re-derives the same comparison itself, so it reverts a set ofapplycommits 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
??fallbackPer #13973's standing prohibition, the question is which side owes the canonical spelling:
canonicalVersionInstant/versionTokensAgree). Local, and this file already has the helper.withPostgresCalendarDayAsText) — a maintainer call.A regression pin needs a
Dateon the read side.@objectstack/metadata-protocolhas no driver dependency and must not grow one (the layering runs the other way), so the pin drives a hand-madeDatehere — the same splitsql-driver-13567-audit-stamp-materialisation.test.tsdocuments for the OCC seam.Re-run
Backlink: #13973 (census), #13382 (the OCC seam, the same shape, same file). Neither is addressed here.