fix(workbench): seal timing to source event time (#2025)

This commit is contained in:
Lyon
2026-06-24 06:47:49 +08:00
committed by GitHub
parent 9e094a6697
commit 1018f1ef37
2 changed files with 6 additions and 3 deletions
@@ -91,7 +91,8 @@ test("workbench projection writer commits terminal owner evidence as sealed dura
test("workbench projection event commit writes trace event and checkpoint facts", async () => { test("workbench projection event commit writes trace event and checkpoint facts", async () => {
const factWrites = []; const factWrites = [];
const baseStore = createCloudRuntimeStore({ now: () => "2026-06-20T11:00:00.000Z" }); let now = "2026-06-20T11:01:05.000Z";
const baseStore = createCloudRuntimeStore({ now: () => now });
const runtimeStore = { const runtimeStore = {
allocateWorkbenchProjectedSeq(params, requestMeta) { allocateWorkbenchProjectedSeq(params, requestMeta) {
return baseStore.allocateWorkbenchProjectedSeq(params, requestMeta); return baseStore.allocateWorkbenchProjectedSeq(params, requestMeta);
@@ -106,6 +107,7 @@ test("workbench projection event commit writes trace event and checkpoint facts"
} }
}; };
now = "2026-06-20T11:03:30.000Z";
await writeWorkbenchProjectionEvent({ await writeWorkbenchProjectionEvent({
runtimeStore, runtimeStore,
event: { event: {
@@ -153,6 +155,7 @@ test("workbench projection event commit writes trace event and checkpoint facts"
assert.equal(factWrites[1].params.facts.checkpoints[0].projectionStatus, "caught_up"); assert.equal(factWrites[1].params.facts.checkpoints[0].projectionStatus, "caught_up");
assert.equal(factWrites[1].params.facts.checkpoints[0].durationMs, 90000); assert.equal(factWrites[1].params.facts.checkpoints[0].durationMs, 90000);
assert.equal(factWrites[1].params.facts.checkpoints[0].finishedAt, "2026-06-20T11:02:00.000Z"); assert.equal(factWrites[1].params.facts.checkpoints[0].finishedAt, "2026-06-20T11:02:00.000Z");
assert.equal(factWrites[1].params.facts.checkpoints[0].lastEventAt, "2026-06-20T11:02:00.000Z");
}); });
test("workbench projection event writer allocates projectedSeq idempotently by source event identity", async () => { test("workbench projection event writer allocates projectedSeq idempotently by source event identity", async () => {
@@ -110,8 +110,8 @@ export async function writeWorkbenchProjectionEvent({ runtimeStore = null, event
const previousTiming = normalizeTimingProjection(previousCheckpoint?.timing) ?? normalizeTimingProjection(previousCheckpoint); const previousTiming = normalizeTimingProjection(previousCheckpoint?.timing) ?? normalizeTimingProjection(previousCheckpoint);
const explicitStartedAt = optionalTimestampValue(event.startedAt ?? event.traceStartedAt ?? event.runnerStartedAt); const explicitStartedAt = optionalTimestampValue(event.startedAt ?? event.traceStartedAt ?? event.runnerStartedAt);
const startedAt = explicitStartedAt ?? previousTiming?.startedAt ?? (sourceSeq <= 1 ? occurredAt : null); const startedAt = explicitStartedAt ?? previousTiming?.startedAt ?? (sourceSeq <= 1 ? occurredAt : null);
const lastEventAt = latestTimestamp(previousTiming?.lastEventAt, occurredAt, projectedAt); const lastEventAt = latestTimestamp(previousTiming?.lastEventAt, occurredAt);
const finishedAt = terminal ? latestTimestamp(previousTiming?.lastEventAt, optionalTimestampValue(event.finishedAt), occurredAt, projectedAt) : null; const finishedAt = terminal ? latestTimestamp(previousTiming?.lastEventAt, optionalTimestampValue(event.finishedAt), occurredAt) : null;
const timing = eventTimingProjection({ startedAt, lastEventAt, finishedAt, terminal }); const timing = eventTimingProjection({ startedAt, lastEventAt, finishedAt, terminal });
const sourceEventId = workbenchSourceEventId(event, { traceId, sourceSeq, occurredAt }); const sourceEventId = workbenchSourceEventId(event, { traceId, sourceSeq, occurredAt });
const allocation = await runtimeStore.allocateWorkbenchProjectedSeq({ const allocation = await runtimeStore.allocateWorkbenchProjectedSeq({