fix: resume distributed task ID counter past existing IDs; extract plugin view types

The dashboard task-create route now uses the distributed task ID allocator
(FN-3450). On projects whose tasks had been allocated through the legacy
config.nextId counter, the allocator's `ensureStateRow` was seeding a fresh
prefix at sequence 1, so new tasks restarted at FN-001 even when FN-3700
already existed. ensureStateRow now seeds past:

- the legacy config.nextId counter (when configured taskPrefix matches), and
- one past the highest numeric suffix on any existing tasks/archivedTasks row
  for the prefix.

A regression test seeds FN-3700 in a fresh DB and asserts the next reservation
returns FN-3701, not FN-001.

Plugin dashboard view contracts are now exposed via a slim type-only module
(`@fusion/dashboard/app/plugins/types`). External plugin tsc builds previously
imported `pluginViewRegistry`, transitively pulling in dashboard runtime
sources (React components, CSS, lucide-react). The dependency-graph plugin's
import + path mapping is updated to use the new module.

Schema housekeeping: drop unused scaffolding tables left over from an earlier
migration via a new idempotent migration (v67). Fresh DBs see no change;
existing DBs that ran the older migration get the orphan tables dropped on
next init.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-05-07 09:41:42 -07:00
parent e7deab16b8
commit e3f2e4c3be
21 changed files with 201 additions and 453 deletions

View File

@@ -88,7 +88,7 @@ export function probeFts5(db: DatabaseSync): boolean {
// ── Schema Definition ────────────────────────────────────────────────
const SCHEMA_VERSION = 66;
const SCHEMA_VERSION = 67;
function normalizeTaskComments(
steeringComments: SteeringComment[] | undefined,
@@ -825,59 +825,6 @@ CREATE TABLE IF NOT EXISTS todo_items (
CREATE INDEX IF NOT EXISTS idxTodoListsProjectId ON todo_lists(projectId);
CREATE INDEX IF NOT EXISTS idxTodoItemsListId ON todo_items(listId);
CREATE INDEX IF NOT EXISTS idxTodoItemsSortOrder ON todo_items(listId, sortOrder);
-- Project-scoped auth domain tables (FN-3515)
CREATE TABLE IF NOT EXISTS project_auth_users (
id TEXT PRIMARY KEY,
email TEXT NOT NULL,
displayName TEXT,
active INTEGER NOT NULL DEFAULT 1,
createdAt TEXT NOT NULL,
updatedAt TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS project_auth_memberships (
id TEXT PRIMARY KEY,
userId TEXT NOT NULL,
role TEXT NOT NULL,
active INTEGER NOT NULL DEFAULT 1,
createdAt TEXT NOT NULL,
updatedAt TEXT NOT NULL,
FOREIGN KEY (userId) REFERENCES project_auth_users(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS project_auth_providers (
id TEXT PRIMARY KEY,
userId TEXT NOT NULL,
provider TEXT NOT NULL,
providerUserId TEXT NOT NULL,
metadata TEXT,
createdAt TEXT NOT NULL,
updatedAt TEXT NOT NULL,
FOREIGN KEY (userId) REFERENCES project_auth_users(id) ON DELETE CASCADE,
UNIQUE(provider, providerUserId)
);
CREATE TABLE IF NOT EXISTS project_auth_sessions (
id TEXT PRIMARY KEY,
userId TEXT NOT NULL,
membershipId TEXT NOT NULL,
sessionToken TEXT NOT NULL UNIQUE,
expiresAt TEXT NOT NULL,
revokedAt TEXT,
createdAt TEXT NOT NULL,
updatedAt TEXT NOT NULL,
FOREIGN KEY (userId) REFERENCES project_auth_users(id) ON DELETE CASCADE,
FOREIGN KEY (membershipId) REFERENCES project_auth_memberships(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idxProjectAuthUsersEmail ON project_auth_users(email);
CREATE INDEX IF NOT EXISTS idxProjectAuthMembershipsUserId ON project_auth_memberships(userId);
CREATE INDEX IF NOT EXISTS idxProjectAuthMembershipsRole ON project_auth_memberships(role);
CREATE INDEX IF NOT EXISTS idxProjectAuthProvidersUserId ON project_auth_providers(userId);
CREATE INDEX IF NOT EXISTS idxProjectAuthSessionsUserId ON project_auth_sessions(userId);
CREATE INDEX IF NOT EXISTS idxProjectAuthSessionsMembershipId ON project_auth_sessions(membershipId);
CREATE INDEX IF NOT EXISTS idxProjectAuthSessionsExpiry ON project_auth_sessions(expiresAt);
`;
// ── Database Class ───────────────────────────────────────────────────
@@ -2674,66 +2621,6 @@ export class Database {
});
}
if (version < 63) {
this.applyMigration(63, () => {
this.db.exec(`
CREATE TABLE IF NOT EXISTS project_auth_users (
id TEXT PRIMARY KEY,
email TEXT NOT NULL,
displayName TEXT,
active INTEGER NOT NULL DEFAULT 1,
createdAt TEXT NOT NULL,
updatedAt TEXT NOT NULL
)
`);
this.db.exec(`
CREATE TABLE IF NOT EXISTS project_auth_memberships (
id TEXT PRIMARY KEY,
userId TEXT NOT NULL,
role TEXT NOT NULL,
active INTEGER NOT NULL DEFAULT 1,
createdAt TEXT NOT NULL,
updatedAt TEXT NOT NULL,
FOREIGN KEY (userId) REFERENCES project_auth_users(id) ON DELETE CASCADE
)
`);
this.db.exec(`
CREATE TABLE IF NOT EXISTS project_auth_providers (
id TEXT PRIMARY KEY,
userId TEXT NOT NULL,
provider TEXT NOT NULL,
providerUserId TEXT NOT NULL,
metadata TEXT,
createdAt TEXT NOT NULL,
updatedAt TEXT NOT NULL,
FOREIGN KEY (userId) REFERENCES project_auth_users(id) ON DELETE CASCADE,
UNIQUE(provider, providerUserId)
)
`);
this.db.exec(`
CREATE TABLE IF NOT EXISTS project_auth_sessions (
id TEXT PRIMARY KEY,
userId TEXT NOT NULL,
membershipId TEXT NOT NULL,
sessionToken TEXT NOT NULL UNIQUE,
expiresAt TEXT NOT NULL,
revokedAt TEXT,
createdAt TEXT NOT NULL,
updatedAt TEXT NOT NULL,
FOREIGN KEY (userId) REFERENCES project_auth_users(id) ON DELETE CASCADE,
FOREIGN KEY (membershipId) REFERENCES project_auth_memberships(id) ON DELETE CASCADE
)
`);
this.db.exec(`CREATE INDEX IF NOT EXISTS idxProjectAuthUsersEmail ON project_auth_users(email)`);
this.db.exec(`CREATE INDEX IF NOT EXISTS idxProjectAuthMembershipsUserId ON project_auth_memberships(userId)`);
this.db.exec(`CREATE INDEX IF NOT EXISTS idxProjectAuthMembershipsRole ON project_auth_memberships(role)`);
this.db.exec(`CREATE INDEX IF NOT EXISTS idxProjectAuthProvidersUserId ON project_auth_providers(userId)`);
this.db.exec(`CREATE INDEX IF NOT EXISTS idxProjectAuthSessionsUserId ON project_auth_sessions(userId)`);
this.db.exec(`CREATE INDEX IF NOT EXISTS idxProjectAuthSessionsMembershipId ON project_auth_sessions(membershipId)`);
this.db.exec(`CREATE INDEX IF NOT EXISTS idxProjectAuthSessionsExpiry ON project_auth_sessions(expiresAt)`);
});
}
if (version < 64) {
this.applyMigration(64, () => {
this.db.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idxEvalTaskResultsRunTaskUnique ON eval_task_results(runId, taskId)`);
@@ -2782,6 +2669,20 @@ export class Database {
});
}
if (version < 67) {
// Drop the project_auth_* tables introduced by the old migration 63
// (FN-3544). The pluggable project-auth feature was removed before any
// production usage; these tables are orphaned on DBs that ran the old
// migration. Drop sessions/providers/memberships before users so the
// foreign-key cascade order is honored.
this.applyMigration(67, () => {
this.db.exec(`DROP TABLE IF EXISTS project_auth_sessions`);
this.db.exec(`DROP TABLE IF EXISTS project_auth_providers`);
this.db.exec(`DROP TABLE IF EXISTS project_auth_memberships`);
this.db.exec(`DROP TABLE IF EXISTS project_auth_users`);
});
}
}
/**