Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/weekly-use-effect-review.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 21 additions & 5 deletions .github/workflows/weekly-use-effect-review.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,26 @@ Classify the intent as `derive-render`, `handle-event`, `reset-or-adjust-state`,
- Prefer `useSyncExternalStore` for external stores.
- For data fetching kept in an Effect, handle cancellation or stale responses.
- For a genuine external synchronization Effect, keep it only when dependencies
include every reactive value read by setup or cleanup, cleanup mirrors setup,
it tolerates Strict Mode, and it has no dependency suppression.
reflect the values that require resynchronization, cleanup mirrors setup, and
it tolerates Strict Mode. Treat dependency suppressions as investigation leads,
not sufficient evidence of incorrect behavior.

Before changing an Effect, trace its callers and lifecycle. Establish which
values can actually change while the component remains mounted, including plugin
initialization, update hooks, and keyed remounts. Identify a concrete failure or
measurable unnecessary work, its reachable trigger, and how the change resolves
it. A dependency suppression alone does not justify a PR.

Preserve setup/cleanup ownership: cleanup must release the resources and notify
the consumers associated with that setup. Explain any intentional callback
changes. Follow React's ref rules: do not introduce render-time ref assignments
or hide dependencies behind refs solely to satisfy lint.

Only fix high-confidence violations. Each pull request must contain exactly one
independent violation, start from the default branch, and use a branch named
`automation/use-effect/<short-slug>`. Do not stack or combine pull requests.
Stop after five pull requests; leave remaining findings for a later run.
Zero findings is a successful run. Five pull requests is a maximum, not a target;
leave remaining findings for a later run.

Do not change dependency manifests, lockfiles, workflow files, agent
instructions, or other protected files. Do not create an issue or pull request
Expand All @@ -74,7 +87,10 @@ Before proposing each fix, run the narrowest relevant check. Use `bun test` for
logic covered by unit tests, `bun run types` for TypeScript changes, `bun run ci`
for formatting and lint-sensitive changes, and `bun run e2e` only when the
changed Effect alters browser behavior that cannot be covered without a browser.
Run verification that exercises the claimed failure or measures the unnecessary
work. Confirm any cited lint rule is enabled. If required tools or checks are
unavailable, report the limitation and create no PR.

In each draft PR, explain the Effect's location and classification, why the old
Effect was incorrect, the selected refactor, and the verification command and
result.
Effect was incorrect, its reachable trigger, the relevant caller/lifecycle
evidence, the selected refactor, and the verification command and result.
35 changes: 28 additions & 7 deletions apps/server/src/storage/postgres/adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ if (url) {
let storage = new PostgresStorage(url);
let sql = new SQL(url);
let suffix = crypto.randomUUID();
let locked = Promise.withResolvers<void>();
let locked = Promise.withResolvers<number>();
let release = Promise.withResolvers<void>();
try {
await storage.migrate();
Expand All @@ -312,13 +312,15 @@ if (url) {
createdBy: userId,
now,
});
let lease = await storage.leases.acquire(`job-lock-${suffix}`, "old-writer", 50);
let lease = await storage.leases.acquire(`job-lock-${suffix}`, "old-writer", 1_000);
let blocker = sql.begin(async transaction => {
await transaction`SELECT pg_advisory_xact_lock(2043237432)`;
locked.resolve();
let [row] = await transaction<{ pid: number }[]>`
SELECT pg_backend_pid() AS pid, pg_advisory_xact_lock(2043237432)
`;
locked.resolve(row!.pid);
await release.promise;
});
await locked.promise;
let blockerPid = await locked.promise;
let enqueue = storage.jobs.enqueue({
id: `job-lock-job-${suffix}`,
channelId,
Expand All @@ -333,10 +335,29 @@ if (url) {
now,
lease: lease!,
});
await Bun.sleep(75);
let result = enqueue.then(() => undefined, error => error);
let waiting = false;
for (let attempt = 0; attempt < 100; attempt++) {
let [row] = await sql<{ waiting: boolean }[]>`
SELECT EXISTS (
SELECT 1 FROM pg_stat_activity
WHERE ${blockerPid} = ANY(pg_blocking_pids(pid))
) AS waiting
`;
if (row!.waiting) {
waiting = true;
break;
}
await Bun.sleep(10);
}
expect(waiting).toBe(true);
await sql`
SELECT pg_sleep(GREATEST(0, EXTRACT(EPOCH FROM (expires_at - clock_timestamp()))))
FROM storage_leases WHERE name = ${lease!.name}
`;
release.resolve();
await blocker;
await expect(enqueue).rejects.toMatchObject({ failure: "conflict" });
expect(await result).toMatchObject({ failure: "conflict" });
expect((await storage.jobs.list(channelId, 10))!.jobs).toEqual([]);
} finally {
release.resolve();
Expand Down
Loading