## 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)
60 lines
1.9 KiB
TypeScript
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";
|