docs(events): document durable thread seq watermark (#6532)

* docs(events): document durable thread seq watermark

RUN_EVENT_STREAM.md described seq as monotonic but did not cover the durable per-thread high-water mark that keeps allocations monotonic after a run is deleted. Record that behavior in the Record Envelope section, including that thread deletion drops the mark only when no events remain.

* test(scheduler): pin clock for scheduled-run timezone assertions

---------

Co-authored-by: yu1128-L <296164533+yu1128-L@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
This commit is contained in:
yu1128-L 2026-10-09 08:10:41 +08:00 committed by GitHub
parent 286bb5fe1c
commit 5ecc1c2ac2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 53 additions and 14 deletions

View File

@ -37,6 +37,14 @@ monotonically for their supported deployment modes. JSONL only provides this
guarantee within one process; shared multi-process deployments must use the
database store.
The database store additionally keeps a per-thread high-water mark
(`run_event_thread_seq`) that is independent of the event rows. Deleting a run
therefore never lowers the next allocated `seq`: values are not reused within a
thread, and a consumer whose `after_seq` cursor already advanced past a deleted
run does not miss later events. Thread deletion drops the mark only when no
events remain for the thread across every owner and category; surviving rows
retain the allocation floor.
## Categories
`category="message"` means an event is eligible for a message projection; it

View File

@ -1,4 +1,11 @@
import { afterEach, describe, expect, rs, test } from "@rstest/core";
import {
afterEach,
beforeEach,
describe,
expect,
rs,
test,
} from "@rstest/core";
import { cleanup, fireEvent, render, screen } from "@testing-library/react";
import type { ReactNode } from "react";
@ -135,7 +142,13 @@ describe("ScheduledRunPrompt", () => {
});
describe("run time zone follows the tasks page", () => {
beforeEach(() => {
rs.useFakeTimers({ toFake: ["Date"] });
rs.setSystemTime(new Date("2026-10-07T12:00:00Z"));
});
afterEach(() => {
rs.useRealTimers();
rs.restoreAllMocks();
});
@ -170,21 +183,39 @@ describe("ScheduledRunPrompt", () => {
"en-US",
);
const block = screen.getByTestId("scheduled-run-prompt");
expect(block.textContent).toContain("09:00");
expect(block.textContent).toContain("Today 09:00");
expect(block.textContent).not.toContain("your time");
});
test("a cron task saved in UTC reads in UTC, with the viewer's time beside it", () => {
viewerIn("Asia/Shanghai");
const { origin } = launch();
renderPrompt(
at({ ...origin, schedule_type: "cron", timezone: "UTC" }),
"en-US",
);
const block = screen.getByTestId("scheduled-run-prompt");
// Between 16:00 and 24:00 UTC the two zones are on different days, so
// the viewer's time carries its own day ("Today 09:00 your time").
expect(block.textContent).toMatch(/01:00 · (?:\S+ )?09:00 your time/);
});
test.each([
{
label: "both zones have the same relative day",
now: "2026-10-07T12:00:00Z",
expected: "Today 01:00 · 09:00 your time",
},
{
label: "the viewer is on the next day",
now: "2026-10-07T20:00:00Z",
expected: "Today 01:00 · Yesterday 09:00 your time",
},
{
label: "the viewer needs an absolute date",
now: "2026-10-08T23:00:00Z",
expected: "Yesterday 01:00 · Oct 7 09:00 your time",
},
])(
"a cron task saved in UTC shows the viewer's time when $label",
({ now, expected }) => {
rs.setSystemTime(new Date(now));
viewerIn("Asia/Shanghai");
const { origin } = launch();
renderPrompt(
at({ ...origin, schedule_type: "cron", timezone: "UTC" }),
"en-US",
);
const block = screen.getByTestId("scheduled-run-prompt");
expect(block.textContent).toContain(expected);
},
);
});
});