From e993c9b2fd94a085f76694596b413bca0e8b1d7b Mon Sep 17 00:00:00 2001 From: Dotta Date: Tue, 3 Nov 2026 13:03:27 -0600 Subject: [PATCH] test: account for native cursor projection metadata --- doc/plans/2026-11-02-sleep-blocker-inventory.md | 3 ++- packages/db/src/client.test.ts | 8 ++++++-- .../src/__tests__/idle-sleep-diagnostics-postgres.test.ts | 2 +- server/src/__tests__/idle-sleep-diagnostics.test.ts | 2 +- .../src/__tests__/native-settled-process-release.test.ts | 3 ++- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/doc/plans/2026-11-02-sleep-blocker-inventory.md b/doc/plans/2026-11-02-sleep-blocker-inventory.md index e9b1de4ce71..73b37d41788 100644 --- a/doc/plans/2026-11-02-sleep-blocker-inventory.md +++ b/doc/plans/2026-11-02-sleep-blocker-inventory.md @@ -20,7 +20,7 @@ The opt-in DB SELECT evaluates every existing predicate as a named boolean colum ## Complete durable reason catalog -At this baseline there are 36 explicit checks, 32 retained-table checks, and four appended checks: 72 durable predicates. Keys identify domain families independently of SQL position or status. A later completion/ingress/deadline improvement retains the family key; a new independent predicate requires its own key and inventory row. This count is a baseline observation, not a permanent protocol limit. +At this baseline there are 36 explicit checks, 32 retained-table checks, and four appended checks: 72 durable predicates. Keys identify domain families independently of SQL position or status. A later completion/ingress/deadline improvement retains the family key; a new independent predicate requires its own key and inventory row. This count is a baseline observation, not a permanent protocol limit. Native event cursor rebuilding adds the independent `native_event_cursor_rebuild` predicate, bringing the current total to 73. For every row below, the diagnostic fixture is the named durable trigger and its negative/completion counterpart. Existing owner suites establish domain contracts; this PR's real PostgreSQL representative fixtures establish simultaneous reporting and the original eligibility behavior, not new exemptions for every domain. The reason-column test compiles the actual full policy query, checks key uniqueness, and evaluates its reporting contract. It is not substituted for real database fixtures or deployed proof. @@ -32,6 +32,7 @@ Remedies below mean completing the owning protocol or supplying a separately tes | --- | --- | --- | --- | | `agents.execution_or_timer` | [agents](../../packages/db/src/schema/agents.ts); [heartbeat/scheduling.ts](../../server/src/services/heartbeat/scheduling.ts). active / future. | Running agent, or enabled positive timer on an executable agent. Paused/terminated/pending approval timers do not require dispatch. Fixture: Enable a positive timer; disable it for the negative fixture. | Future-work alarm coverage; preserve the owning evidence and records. | | `heartbeat_runs` | [heartbeat_runs](../../packages/db/src/schema/heartbeat_runs.ts); [heartbeat/run-completion.ts](../../server/src/services/heartbeat/run-completion.ts). active / recovery. | Nonterminal run, retry later than finish or missing finish, or pending accounting. Terminal consumed retries alone are history. Fixture: Queued run versus finished ordinary run, consumed retry, live retry child. | Domain settlement; preserve the owning evidence and records. | +| `native_event_cursor_rebuild` | [heartbeat_runs](../../packages/db/src/schema/heartbeat_runs.ts); [heartbeat/native-event-cursor-work.ts](../../server/src/services/heartbeat/native-event-cursor-work.ts). projection recovery. | An explicitly pending native source cursor rebuild blocks sleep, including on a terminal run. Indexed pending state drains through bounded metadata pages. Fixture: Pending rebuild versus settled projection. | Complete the owned cursor rebuild; preserve event history and resume pending work after restart. | | `agent_instruction_working_copies` | [agent_instruction_working_copies](../../packages/db/src/schema/agent_instruction_revisions.ts); [agent-instruction-working-copies.ts](../../server/src/services/agent-instruction-working-copies.ts). cleanup. | Requires known settled state, stopped process, no next attempt, no baseline or cleanup-pending receipt. Fixture: Stopped saved copy versus deferred cleanup. | Domain cleanup; preserve the owning evidence and records. | | `sandbox_creation_operations` | [sandbox_creation_operations](../../packages/db/src/schema/sandbox_creation_operations.ts); [sandbox-creation-operations.ts](../../server/src/services/sandbox-creation-operations.ts). recovery. | Pending creation operation retains uncertain/provider work. Fixture: Pending operation versus settled operation. | Domain settlement; preserve the owning evidence and records. | | `budget_reservations` | [budget_reservations](../../packages/db/src/schema/accounting.ts); [budget-reservations.ts](../../server/src/services/budget-reservations.ts). accounting. | Anything outside settled/released remains owed. Fixture: Unsettled reservation versus settled charge. | Domain accounting; preserve the owning evidence and records. | diff --git a/packages/db/src/client.test.ts b/packages/db/src/client.test.ts index 5d0f6a60167..be6ae0984c9 100644 --- a/packages/db/src/client.test.ts +++ b/packages/db/src/client.test.ts @@ -294,6 +294,10 @@ describeEmbeddedPostgres("merged Muse migration histories", () => { const beforeHistory = baseline === "fresh" ? [] : await historyRows(sql); const retainedRows = baseline === "fresh" ? null : await seedRetainedRows(sql, baseline === "muse0348"); const beforeRows = await retainedRows?.(); + const expectedRows = beforeRows && { + ...beforeRows, + run: { ...beforeRows.run, event_cursor_projection_seq: 0, event_cursor_rebuild_pending: false }, + }; const state = await inspectMigrations(connectionString); expect(state.status).toBe("needsMigrations"); const laterMigrations = journal.entries.filter(entry => entry.idx > 353).map(entry => `${entry.tag}.sql`); @@ -319,7 +323,7 @@ describeEmbeddedPostgres("merged Muse migration histories", () => { const currentHash = await migrationHash(entry.file); expect(afterHistory.filter(row => row.hash === currentHash)).toHaveLength(1); } - if (retainedRows) expect(await retainedRows()).toEqual(beforeRows); + if (retainedRows) expect(await retainedRows()).toEqual(expectedRows); const indexes = await sql` SELECT c.relname, i.indisvalid, i.indisready, pg_get_indexdef(c.oid) AS definition FROM pg_index i JOIN pg_class c ON c.oid = i.indexrelid JOIN pg_namespace n ON n.oid = c.relnamespace @@ -336,7 +340,7 @@ describeEmbeddedPostgres("merged Muse migration histories", () => { expect((await inspectMigrations(connectionString)).status).toBe("upToDate"); expect(await historyRows(sql)).toEqual(afterHistory); expect(await schemaShape(sql)).toEqual(finalShape); - if (retainedRows) expect(await retainedRows()).toEqual(beforeRows); + if (retainedRows) expect(await retainedRows()).toEqual(expectedRows); } finally { await Promise.all([sql.end(), reference.end()]); } diff --git a/server/src/__tests__/idle-sleep-diagnostics-postgres.test.ts b/server/src/__tests__/idle-sleep-diagnostics-postgres.test.ts index 1f36be6bb81..dbae1706f00 100644 --- a/server/src/__tests__/idle-sleep-diagnostics-postgres.test.ts +++ b/server/src/__tests__/idle-sleep-diagnostics-postgres.test.ts @@ -61,7 +61,7 @@ if (!support.supported) console.warn(`Skipping idle sleep diagnostics Postgres t expect(blockers(report)).toEqual([]); expect(report.diagnostics?.checks.filter(check => !check.reason.includes(".") || ["agents.execution_or_timer", "agents.lifecycle", "agents.company_hold", "heartbeat_runs.budget_cancellation"].includes(check.reason))) - .toHaveLength(72); + .toHaveLength(73); expect(await db.select().from(heartbeatRuns).where(eq(heartbeatRuns.id, f.runId))).toHaveLength(1); }); diff --git a/server/src/__tests__/idle-sleep-diagnostics.test.ts b/server/src/__tests__/idle-sleep-diagnostics.test.ts index 58c6cc3bdba..6cdbbeebb61 100644 --- a/server/src/__tests__/idle-sleep-diagnostics.test.ts +++ b/server/src/__tests__/idle-sleep-diagnostics.test.ts @@ -40,7 +40,7 @@ describe("owned idle sleep diagnostics", () => { expect(result.diagnostics?.complete).toBe(true); expect(result.diagnostics?.checks.filter(check => check.state === "present").map(check => check.reason)) .toEqual(["agent_api_keys", "chat_deliveries", "heartbeat_runs", "muse_mailbox_items"]); - expect(f.keys()).toHaveLength(72); + expect(f.keys()).toHaveLength(73); expect(new Set(f.keys()).size).toBe(f.keys().length); const inventory = readFileSync(new URL("../../../doc/plans/2026-11-02-sleep-blocker-inventory.md", import.meta.url), "utf8"); const documented = [...inventory.matchAll(/^\| `([a-z0-9_.]+)` \|/gm)].map(match => match[1]); diff --git a/server/src/__tests__/native-settled-process-release.test.ts b/server/src/__tests__/native-settled-process-release.test.ts index a6805c61582..9293ab8e122 100644 --- a/server/src/__tests__/native-settled-process-release.test.ts +++ b/server/src/__tests__/native-settled-process-release.test.ts @@ -230,7 +230,8 @@ suite("settled native run process identity", () => { expect(summary.responseValueBytes).toBeLessThan(8192); const after = await readRun(runId); expect(after).toEqual({ ...saved, processPid: null, processGroupId: null, processStartedAt: null, - nextEventSeq: saved.nextEventSeq + 1, updatedAt: after.updatedAt }); + nextEventSeq: saved.nextEventSeq + 1, eventCursorProjectionSeq: saved.eventCursorProjectionSeq + 1, + updatedAt: after.updatedAt }); expect(await hasNativeLocalProcessStop(db, companyId, runId)).toBe(true); expect(JSON.stringify(events)).not.toContain(bulk.slice(0, 64)); });