dreaming: stabilize waiting-entry recency sort

This commit is contained in:
Dave Morin
2026-04-09 22:26:14 -10:00
committed by Vignesh
parent 7d342374ce
commit c519f5abe1
2 changed files with 75 additions and 6 deletions

View File

@@ -409,5 +409,68 @@ describe("dreaming view", () => {
setDreamSubTab("scene");
});
it("treats malformed waiting-entry timestamps as oldest in both sort modes", () => {
setDreamSubTab("advanced");
const shortTermEntries = [
{
key: "memory:valid-recent",
path: "memory/2026-04-06.md",
startLine: 1,
endLine: 1,
snippet: "Valid recent timestamp",
recallCount: 1,
dailyCount: 0,
groundedCount: 0,
totalSignalCount: 3,
lightHits: 1,
remHits: 0,
phaseHitCount: 1,
lastRecalledAt: "2026-04-06T12:00:00.000Z",
},
{
key: "memory:malformed-time",
path: "memory/2026-04-05.md",
startLine: 1,
endLine: 1,
snippet: "Malformed timestamp",
recallCount: 1,
dailyCount: 0,
groundedCount: 0,
totalSignalCount: 3,
lightHits: 1,
remHits: 0,
phaseHitCount: 1,
lastRecalledAt: "not-a-timestamp",
},
];
setDreamAdvancedWaitingSort("recent");
let container = renderInto(
buildProps({
shortTermEntries,
promotedEntries: [],
}),
);
const recentOrder = [...container.querySelectorAll("[data-entry-key]")].map((node) =>
node.getAttribute("data-entry-key"),
);
expect(recentOrder).toEqual(["memory:valid-recent", "memory:malformed-time"]);
setDreamAdvancedWaitingSort("signals");
container = renderInto(
buildProps({
shortTermEntries,
promotedEntries: [],
}),
);
const signalOrder = [...container.querySelectorAll("[data-entry-key]")].map((node) =>
node.getAttribute("data-entry-key"),
);
expect(signalOrder).toEqual(["memory:valid-recent", "memory:malformed-time"]);
setDreamAdvancedWaitingSort("recent");
setDreamSubTab("scene");
});
// Toggle lives in the page header (app-render.ts), not inside the dreaming view.
});

View File

@@ -430,13 +430,19 @@ function formatCompactDateTime(value: string): string {
});
}
function parseSortableTimestamp(value?: string): number {
if (!value) {
return Number.NEGATIVE_INFINITY;
}
const parsed = Date.parse(value);
return Number.isFinite(parsed) ? parsed : Number.NEGATIVE_INFINITY;
}
function compareWaitingEntryByRecency(a: DreamingEntry, b: DreamingEntry): number {
const aMs = a.lastRecalledAt ? Date.parse(a.lastRecalledAt) : Number.NEGATIVE_INFINITY;
const bMs = b.lastRecalledAt ? Date.parse(b.lastRecalledAt) : Number.NEGATIVE_INFINITY;
if (Number.isFinite(aMs) || Number.isFinite(bMs)) {
if (bMs !== aMs) {
return bMs - aMs;
}
const aMs = parseSortableTimestamp(a.lastRecalledAt);
const bMs = parseSortableTimestamp(b.lastRecalledAt);
if (bMs !== aMs) {
return bMs - aMs;
}
if (b.totalSignalCount !== a.totalSignalCount) {
return b.totalSignalCount - a.totalSignalCount;