Files
fusion/plugins/fusion-plugin-quality/src/index.ts
gsxdsm 446c969968 fix(FN-2127): harden Quality Postgres store (project scope, races) (#2230)
## Summary

Follow-up after **#2164** and main’s **FN-8103 / FN-8104**
(Postgres-only data access / SQLite retirement).

Main already routes Quality through `AsyncQualityStore` +
`getAsyncLayer()`. This PR keeps the **review hardening** that was still
missing:

- **Project binding** — reject request `projectId` mismatches vs bound
AsyncDataLayer; all SQL uses bound project
- **`createRunIfNoActive`** — advisory lock so concurrent starts cannot
double-queue
- **Cancel-safe runner** — cancel slot registered before the running
write; pre-spawn cancel skips process
- **`finalizeRun`** — never overwrites a `cancelled` terminal status
- **Detached execute** — catch only execution failures; prune fail-soft
in `finally`
- Guardrail tests + Quality v2 plan doc

## Test plan

- [ ] Task QA loads under PostgreSQL (no SQLite/backend-mode error)
- [ ] Concurrent start for same task → 409 second start
- [ ] Cancel during start does not leave a live orphan process
- [ ] Cancelled run stays cancelled after process exit
- [ ] `pnpm --filter @fusion-plugin-examples/quality test` (37 tests)
2026-07-16 11:38:34 -07:00

60 lines
1.9 KiB
TypeScript

import { definePlugin } from "@fusion/plugin-sdk";
import { ensureQualitySchema, qualityPostgresSchema } from "./quality-schema.js";
import { createQualityRoutes } from "./routes/create-routes.js";
import { settingsSchema } from "./settings.js";
/*
FNXC:Quality 2026-07-14-21:45:
Bundled Quality plugin: hub dashboard view + Task QA tab metadata.
Server entry must not re-export React/CSS views (fusion/no-plugin-view-reexport).
*/
const plugin = definePlugin({
manifest: {
id: "fusion-plugin-quality",
name: "Quality",
version: "0.1.0",
description:
"Task QA tab and Quality hub: preview servers, test runs, reports, screenshots, and suggested cases",
author: "Fusion Team",
fusionVersion: ">=0.1.0",
settingsSchema,
},
state: "installed",
hooks: {
onSchemaInit: ensureQualitySchema,
onPostgresSchemaInit: () => qualityPostgresSchema,
},
routes: createQualityRoutes(),
dashboardViews: [
{
viewId: "quality",
label: "Quality",
componentPath: "./dashboard-view",
icon: "ShieldCheck",
placement: "primary",
order: 32,
description: "Project quality: runs, plans, and CI",
},
],
uiSlots: [
{
slotId: "task-detail-tab",
label: "QA",
icon: "FlaskConical",
componentPath: "./qa-tab",
order: 20,
},
],
});
export default plugin;
export { ensureQualitySchema, qualityPostgresSchema } from "./quality-schema.js";
export { QualityStore } from "./store/quality-store.js";
export { AsyncQualityStore } from "./store/async-quality-store.js";
export { getQualityStore } from "./store/quality-store-provider.js";
export type { QualityStoreApi } from "./store/quality-store-api.js";
export { resolvePresetCommand, isQualityPresetId, listPresetCatalog } from "./runner/command-presets.js";
export { buildHeuristicSuggestedCases } from "./suggestions/heuristic-cases.js";