mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-31 06:04:11 +01:00
test: await settled response recovery across startup batches
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
c4beddad1e
commit
fad35f2d7a
3 files changed
+55
-11
No files matched your search
@@ -2364,3 +2364,14 @@ unproven. Remote failures at `79bf36cbf8` include an early startup-settlement te
|
||||
assertion (repaired), a workspace staging timeout, and an E2E runner shutdown.
|
||||
See the response audit for logs. Fresh-head CI/review, earlier stack findings,
|
||||
master reconciliation, and all remaining original duties stay open. Nothing merged.
|
||||
|
||||
|
||||
## Slice 48 full-history acceptance repair
|
||||
|
||||
Fresh review of `c4beddad1e` is 5/5 with its scope finding resolved. Remote CI
|
||||
exposed a second early startup assertion. Full local chat verification finished
|
||||
1,595 passed / seven failed; all seven were new worker tests observing a transient
|
||||
null deadline or unfinished startup batch. Their settlement predicate now includes
|
||||
zero active work. All 18 affected cases pass against the full suite's retained
|
||||
history. Fresh remote acceptance remains pending; these are recorded test repairs,
|
||||
not a waiver of failed gates or a change to production batch behavior.
|
||||
@@ -376,3 +376,25 @@ hit a five-second sandbox staging test timeout; E2E shard five received a runner
|
||||
shutdown signal. These failures remain recorded in `/tmp/sleep48-ci-chat3.log`,
|
||||
`/tmp/sleep48-ci-workspaces-b.log`, and `/tmp/sleep48-ci-e2e5.log`. Fresh-head CI and
|
||||
review are required after the repair; no failing gate is waived.
|
||||
|
||||
|
||||
## Full-history test repair, 2026-10-26
|
||||
|
||||
At `c4beddad1e`, fresh Greptile review is 5/5 and the scope thread is resolved.
|
||||
Remote CI passed 51 contexts but failed the chat shard and its aggregate gate:
|
||||
a second startup assertion assumed `worker.ready` drained every fair batch.
|
||||
The full local chat file finished with 1,595 passing and seven failing tests
|
||||
(`/tmp/sleep48-chat-full.log`); all seven were in the newly added worker tests.
|
||||
|
||||
The larger run exposed two related observation errors: startup can have another
|
||||
batch scheduled, and `nextWakeAt() === null` can be transient while a batch runs.
|
||||
The latter caused query instrumentation to count unfinished startup checks and
|
||||
an outcome assertion to run before its response had been materialized. An early
|
||||
future-deadline test failure then left its fixture's real deadline for later tests.
|
||||
|
||||
Worker startup/settlement assertions now require both zero active work and the
|
||||
expected next deadline. This changes test observation, not scheduler behavior or
|
||||
batch limits. All 18 affected cases pass against the retained history database
|
||||
from the full 1,602-case run (`/tmp/sleep48-settlement-history.log`), including
|
||||
single-query unrelated wakes, future deadlines, and coordinator retry release.
|
||||
The full-run failure remains recorded; fresh remote CI is still required.
|
||||
@@ -72641,7 +72641,8 @@ describeEmbeddedPostgres("chat channel control-plane integration", () => {
|
||||
const worker = work.register(coordinator, () => true);
|
||||
try {
|
||||
await worker.ready;
|
||||
await expect.poll(() => coordinator.nextWakeAt(), { timeout: 5000 }).toBeNull();
|
||||
await expect.poll(() => ({ active: idleWorkSnapshot().active, next: coordinator.nextWakeAt() }),
|
||||
{ timeout: 10_000 }).toEqual({ active: 0, next: null });
|
||||
const comments = await db.select().from(issueComments).where(eq(issueComments.createdByRunId, context.runId));
|
||||
expect(comments).toHaveLength(1);
|
||||
expect(comments[0].body).toBe(context.result.summary);
|
||||
@@ -72666,11 +72667,13 @@ describeEmbeddedPostgres("chat channel control-plane integration", () => {
|
||||
let replacement: ReturnType<typeof createService> | undefined;
|
||||
try {
|
||||
await worker.ready;
|
||||
await expect.poll(() => coordinator.nextWakeAt(), { timeout: 5000 }).toBeNull();
|
||||
await expect.poll(() => ({ active: idleWorkSnapshot().active, next: coordinator.nextWakeAt() }),
|
||||
{ timeout: 10_000 }).toEqual({ active: 0, next: null });
|
||||
expect(await readChatPublicationsByEndpointId(context.endpoint.id)).toHaveLength(0);
|
||||
replacement = createService(new FakeChatSdkRuntime(), fakeTelegramFetch() as typeof fetch);
|
||||
await expect.poll(async () => (await readChatPublicationsByEndpointId(context.endpoint.id)).length, { timeout: 5000 }).toBe(1);
|
||||
await expect.poll(() => coordinator.nextWakeAt(), { timeout: 5000 }).toBeNull();
|
||||
await expect.poll(() => ({ active: idleWorkSnapshot().active, next: coordinator.nextWakeAt() }),
|
||||
{ timeout: 10_000 }).toEqual({ active: 0, next: null });
|
||||
expect(errors).toEqual([]);
|
||||
} finally { worker.stop(); await coordinator.stop(); await replacement?.service.shutdown(); }
|
||||
});
|
||||
@@ -72684,7 +72687,8 @@ describeEmbeddedPostgres("chat channel control-plane integration", () => {
|
||||
const worker = work.register(coordinator, () => true);
|
||||
try {
|
||||
await worker.ready;
|
||||
await expect.poll(() => coordinator.nextWakeAt(), { timeout: 10_000 }).toBeNull();
|
||||
await expect.poll(() => ({ active: idleWorkSnapshot().active, next: coordinator.nextWakeAt() }),
|
||||
{ timeout: 10_000 }).toEqual({ active: 0, next: null });
|
||||
expect(await work.load({ companyId: context.fixture.companyId, runId: context.runId })).toHaveLength(1);
|
||||
const previousDebug = db.$client.options.debug;
|
||||
const queries: string[] = [];
|
||||
@@ -72711,7 +72715,8 @@ describeEmbeddedPostgres("chat channel control-plane integration", () => {
|
||||
const worker = nativeResponsePresentationWork(db).register(coordinator, () => true);
|
||||
try {
|
||||
await worker.ready;
|
||||
await expect.poll(() => coordinator.nextWakeAt(), { timeout: 5000 }).toBeNull();
|
||||
await expect.poll(() => ({ active: idleWorkSnapshot().active, next: coordinator.nextWakeAt() }),
|
||||
{ timeout: 10_000 }).toEqual({ active: 0, next: null });
|
||||
const transaction = db.transaction(async tx => {
|
||||
await tx.transaction(async nested => {
|
||||
await notifyChatRunMilestones(nested, { companyId: context.fixture.companyId, issueId: context.issue.id, runId: randomUUID() });
|
||||
@@ -72742,7 +72747,8 @@ describeEmbeddedPostgres("chat channel control-plane integration", () => {
|
||||
const worker = nativeResponsePresentationWork(db).register(coordinator, () => canRun);
|
||||
try {
|
||||
await worker.ready;
|
||||
await expect.poll(() => coordinator.nextWakeAt(), { timeout: 5000 }).toBeNull();
|
||||
await expect.poll(() => ({ active: idleWorkSnapshot().active, next: coordinator.nextWakeAt() }),
|
||||
{ timeout: 10_000 }).toEqual({ active: 0, next: null });
|
||||
canRun = false;
|
||||
await db.transaction(async tx => {
|
||||
await notifyChatRunMilestones(tx, { companyId: context.fixture.companyId, issueId: context.issue.id });
|
||||
@@ -72818,7 +72824,8 @@ describeEmbeddedPostgres("chat channel control-plane integration", () => {
|
||||
const worker = nativeResponsePresentationWork(db).register(coordinator, () => true);
|
||||
try {
|
||||
await worker.ready;
|
||||
expect(coordinator.nextWakeAt()).toBe(at.getTime());
|
||||
await expect.poll(() => ({ active: idleWorkSnapshot().active, next: coordinator.nextWakeAt() }),
|
||||
{ timeout: 10_000 }).toEqual({ active: 0, next: at.getTime() });
|
||||
expect(await readChatPublicationsByEndpointId(context.endpoint.id)).toHaveLength(0);
|
||||
await vi.advanceTimersByTimeAsync(Math.max(0, at.getTime() - Date.now()));
|
||||
await vi.waitFor(async () => {
|
||||
@@ -72869,14 +72876,16 @@ describeEmbeddedPostgres("chat channel control-plane integration", () => {
|
||||
});
|
||||
try {
|
||||
await worker.ready;
|
||||
expect(coordinator.nextWakeAt()).toBeNull();
|
||||
await expect.poll(() => ({ active: idleWorkSnapshot().active, next: coordinator.nextWakeAt() }),
|
||||
{ timeout: 10_000 }).toEqual({ active: 0, next: null });
|
||||
expect(await work.load({ companyId: context.fixture.companyId, agentId: randomUUID() })).toEqual([]);
|
||||
expect(await readChatPublicationsByEndpointId(context.endpoint.id)).toHaveLength(0);
|
||||
await createAgentLifecycle(db).resumeAgent(context.binding.agentId);
|
||||
await processAgentLifecycle(db, context.binding.agentId);
|
||||
await expect.poll(async () => (await db.select().from(agents).where(eq(agents.id, context.binding.agentId)))[0].status).toBe("idle");
|
||||
await expect.poll(async () => (await readChatPublicationsByEndpointId(context.endpoint.id)).length, { timeout: 5000 }).toBe(1);
|
||||
await expect.poll(() => coordinator.nextWakeAt(), { timeout: 5000 }).toBeNull();
|
||||
await expect.poll(() => ({ active: idleWorkSnapshot().active, next: coordinator.nextWakeAt() }),
|
||||
{ timeout: 10_000 }).toEqual({ active: 0, next: null });
|
||||
expect(errors).toEqual([]);
|
||||
} finally { await lifecycle.stop(); worker.stop(); await coordinator.stop(); await context.service.shutdown(); }
|
||||
});
|
||||
@@ -72891,7 +72900,8 @@ describeEmbeddedPostgres("chat channel control-plane integration", () => {
|
||||
const worker = nativeResponsePresentationWork(db).register(coordinator, () => true);
|
||||
try {
|
||||
await worker.ready;
|
||||
expect(coordinator.nextWakeAt()).toBeNull();
|
||||
await expect.poll(() => ({ active: idleWorkSnapshot().active, next: coordinator.nextWakeAt() }),
|
||||
{ timeout: 10_000 }).toEqual({ active: 0, next: null });
|
||||
await vi.advanceTimersByTimeAsync(2000);
|
||||
expect(await readChatPublicationsByEndpointId(context.endpoint.id)).toHaveLength(0);
|
||||
expect(coordinator.nextWakeAt()).toBeNull();
|
||||
@@ -72914,7 +72924,8 @@ describeEmbeddedPostgres("chat channel control-plane integration", () => {
|
||||
const worker = nativeResponsePresentationWork(db).register(coordinator, () => true);
|
||||
try {
|
||||
await worker.ready;
|
||||
await expect.poll(() => coordinator.nextWakeAt(), { timeout: 10_000 }).toBeNull();
|
||||
await expect.poll(() => ({ active: idleWorkSnapshot().active, next: coordinator.nextWakeAt() }),
|
||||
{ timeout: 10_000 }).toEqual({ active: 0, next: null });
|
||||
expect(await readChatPublicationsByEndpointId(context.endpoint.id)).toHaveLength(0);
|
||||
const port = new PaperclipControlPlanePort(db, {
|
||||
companyId: context.binding.companyId, issueId: context.binding.issueId,
|
||||
|
||||
Reference in new issue
Block a user