fix(core): restore PostgreSQL lifecycle activity logging (#2344)
## Summary PostgreSQL-backed projects now record automatic TaskStore lifecycle activity, including ordinary task creation and movement. Backend initialization previously returned before registering the lifecycle listeners, while features that called `recordActivity()` directly could make the log appear only partially affected. The fix wires the backend-agnostic listeners without changing SQLite initialization behavior. ## Validation - PostgreSQL activity parity test verifies all six listener surfaces and persisted `task:created` and `task:moved` entries. - `pnpm lint` - `pnpm --filter @fusion/core typecheck` - `pnpm check:changesets --strict` - `pnpm verify:fast` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Restored automatic task lifecycle entries in PostgreSQL activity logs by ensuring task lifecycle activity listeners are wired during PostgreSQL backend initialization. * Activity logs now reliably include task creation and movement events (with transition details) immediately after backend startup. * **Tests** * Added a PostgreSQL activity-log parity test to verify listeners are connected and that activity logs reflect `task:created` and `task:moved` as expected. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
7
.changeset/fix-postgres-activity-lifecycle.md
Normal file
7
.changeset/fix-postgres-activity-lifecycle.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
summary: Restore automatic task lifecycle entries in PostgreSQL activity logs.
|
||||
category: fix
|
||||
dev: Registers TaskStore activity listeners during PostgreSQL backend initialization.
|
||||
@@ -23,6 +23,44 @@ pgDescribe("activity log parity (PostgreSQL)", () => {
|
||||
afterEach(h.afterEach);
|
||||
afterAll(h.afterAll);
|
||||
|
||||
it("records TaskStore lifecycle events after backend initialization", async () => {
|
||||
const store = h.store();
|
||||
|
||||
expect(store.activityListenersWired).toBe(true);
|
||||
for (const event of [
|
||||
"task:created",
|
||||
"task:moved",
|
||||
"task:merged",
|
||||
"task:updated",
|
||||
"task:deleted",
|
||||
"settings:updated",
|
||||
] as const) {
|
||||
expect(store.listenerCount(event), `${event} activity listener`).toBeGreaterThan(0);
|
||||
}
|
||||
|
||||
const task = await store.createTask({
|
||||
title: "Backend lifecycle activity",
|
||||
description: "Verify PostgreSQL lifecycle activity logging",
|
||||
});
|
||||
await store.moveTask(task.id, "todo", { moveSource: "user" });
|
||||
|
||||
await vi.waitFor(async () => {
|
||||
const entries = await store.getActivityLog({ limit: 10 });
|
||||
expect(entries).toEqual(expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
type: "task:created",
|
||||
taskId: task.id,
|
||||
taskTitle: task.title,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
type: "task:moved",
|
||||
taskId: task.id,
|
||||
metadata: { from: "triage", to: "todo" },
|
||||
}),
|
||||
]));
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps failed writes best-effort so audit storage cannot break product operations", async () => {
|
||||
const layer = h.layer();
|
||||
const insert = vi.spyOn(layer.db, "insert").mockImplementation(() => {
|
||||
|
||||
@@ -47,12 +47,9 @@ export async function initImpl(store: TaskStore): Promise<void> {
|
||||
// In backend mode (an AsyncDataLayer was injected), TaskStore skips ALL
|
||||
// SQLite construction and the SQLite-specific startup reconciliations
|
||||
// (corruption guard, legacy file migration, agent-log file migration,
|
||||
// schema-version re-init, orphaned task-dir reconcile, activity-log
|
||||
// listener wiring that reads from SQLite, etc.). The PostgreSQL schema
|
||||
// baseline is applied by the startup factory before constructing the
|
||||
// store, and the async equivalents of these reconciliations are wired by
|
||||
// the runtime-*-async features. init() in backend mode performs only the
|
||||
// backend-agnostic setup (mkdir, trait-hook registration) above and returns.
|
||||
// schema-version re-init, orphaned task-dir reconcile, etc.). The PostgreSQL
|
||||
// schema baseline is applied by the startup factory before constructing the
|
||||
// store. Backend-safe startup work is performed explicitly in the branch below.
|
||||
//
|
||||
// When the async layer is ABSENT, the entire block below runs exactly as
|
||||
// before — byte-identical to the pre-migration SQLite path.
|
||||
@@ -81,6 +78,9 @@ export async function initImpl(store: TaskStore): Promise<void> {
|
||||
async store API (listTasks/updateTask), so it is PG-safe.
|
||||
*/
|
||||
await adoptLegacyTaskRowsOnOpen(store);
|
||||
// Lifecycle listeners are backend-agnostic: recordActivity() routes their
|
||||
// best-effort writes through the injected PostgreSQL data layer.
|
||||
store.setupActivityLogListeners();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user