fix(tests): restore listTasks hot-path contract and PG harness fs isolation

- usage-limit-detector + provider-health-monitor: make three bare listTasks()
  callers explicit with { slim: true }, restoring the architecture-hot-paths
  contract (they only read scalar pause/column/model-provider fields).
- pg-test-harness beforeEach: wipe <rootDir>/.fusion/tasks after TRUNCATE ...
  RESTART IDENTITY so filesystem isolation matches the id reset; stale task
  dirs from prior tests no longer collide with reused IDs (fixes
  store-reservation-atomicity rollback assertions).
This commit is contained in:
gsxdsm
2026-07-23 17:27:59 -07:00
parent 62c52972fc
commit 0307476f35
3 changed files with 19 additions and 3 deletions

View File

@@ -1004,6 +1004,19 @@ export function createSharedPgTaskStoreTestHarness(options?: {
ON CONFLICT (project_id) DO UPDATE SET next_id = 1, next_workflow_step_id = 1, settings = EXCLUDED.settings, workflow_steps = '[]'::jsonb, updated_at = now()`,
),
);
/*
FNXC:PgTestHarnessIsolation 2026-07-22-17:40:
TRUNCATE ... RESTART IDENTITY resets next_id so the next created task reuses
the same ID (KB-001) as prior tests in this describe. The DB reset is not
enough on its own: task creation also materializes an on-disk
`<rootDir>/.fusion/tasks/<ID>/` directory (task.json + PROMPT.md), which the
truncate leaves behind. A later test that reuses that ID then sees a stale
canonical directory it never created, so rollback/atomicity assertions like
store-reservation-atomicity's `existsSync(.fusion/tasks/<ID>)` toBe(false)
fail on leftover files. Wipe the task-directory tree so filesystem isolation
matches the identity reset.
*/
await rm(join(harness.rootDir, ".fusion", "tasks"), { recursive: true, force: true });
// Drop any in-memory caches so the store doesn't serve stale rows.
resetStorePrivateState(store);
// Force allocator reconciliation to re-seed the distributed state row.

View File

@@ -134,7 +134,8 @@ export class ProviderHealthMonitor {
await Promise.all(stores.map(async (store) => {
try {
const tasks = await store.listTasks();
// FNXC:ArchitectureHotPath 2026-07-22-17:20: listTasks() must declare payload shape (architecture-hot-paths contract). Health scan reads only paused/userPaused/pausedReason scalars, so request slim rows.
const tasks = await store.listTasks({ slim: true });
for (const task of tasks) {
if (task.paused !== true || task.userPaused === true) continue;
const providerId = providerIdFromRateLimitReason(task.pausedReason);

View File

@@ -93,7 +93,8 @@ export class UsageLimitPauser {
if (!providerId) return 0;
const pausedReason = `provider-rate-limit:${providerId}`;
const tasks = await this.store.listTasks();
// FNXC:ArchitectureHotPath 2026-07-22-17:20: listTasks() must be explicit about payload shape (architecture-hot-paths contract). Recovery only reads scalar pause fields, so request slim rows to avoid loading heavy log/steps/comments for every task.
const tasks = await this.store.listTasks({ slim: true });
const recoverableTasks = tasks.filter((task) =>
task.paused === true
&& task.userPaused !== true
@@ -166,7 +167,8 @@ export class UsageLimitPauser {
const [settings, tasks] = await Promise.all([
this.store.getSettings(),
this.store.listTasks(),
// FNXC:ArchitectureHotPath 2026-07-22-17:20: slim payload — this scan only reads column/pause/model-provider scalars, never heavy detail fields.
this.store.listTasks({ slim: true }),
]);
const affectedTasks = tasks.filter((task) =>
task.column !== "done"