Skip to main content

Development

Department Scope Pattern

Every new authenticated route must apply department isolation via forUser() from src/lib/department-scope.ts. forDepartment() still works as a compatibility alias, but forUser() exposes the permission and multi-department scope helpers.

Use the smallest scope helper that matches the data shape:

const scope = forUser(request.user);

// Model has departmentId directly.
await prisma.knowledgeBase.findMany({
where: { ...scope.directWhere() },
});

// Model is scoped through Namespace.
await prisma.agentTask.findMany({
where: { ...scope.nestedWhere('namespace') },
});

Global-scope users see all departments. Everyone else is restricted to scope.deptIds. Don't let a departmentId query parameter override scope.canSeeDept() or scope.directWhere().

For raw SQL that only supports a single department filter, derive filters from scope.departmentId and pass values as Prisma SQL parameters:

const departmentFilter = scope.departmentId
? Prisma.sql`AND kb."departmentId" = ${scope.departmentId}`
: Prisma.empty;

For multi-department scopes, use scope.deptIds and an IN (...) filter instead of assuming scope.departmentId is present.

Routes can also read request.departmentScopesrc/plugins/department-scope.ts decorates the request after auth.

Route Checklist

  • Add fastify.authenticate to every non-public route.
  • Add RBAC middleware like requirePermission() on routes that mutate privileged data.
  • Apply forDepartment() or request.departmentScope before querying department-scoped records.
  • Use sendError() for explicit failures so responses use { error, message, statusCode, details? }.
  • Add route schemas so /docs stays aligned with runtime behavior.
  • Extend department-isolation tests when a new endpoint exposes department-scoped data.

Agent-Tasks Pipeline

Inbound channel handlers should not own RAG, trust, or HITL decisions. For a new channel:

  1. parse the transport payload;
  2. find or create the user, conversation, and inbound message;
  3. create an AgentTask;
  4. enqueue the agent-tasks queue;
  5. provide an outbound dependency so the agent runner can send approved or bypassed replies.

The agent runner worker owns inject_profile, rag_search, generate, and confidence_check. See ADR-001.