import type { DatabaseSync } from "node:sqlite"; import type { CreateAgentInput } from "../../shared/commands"; import { RISK_LEVEL, ROUTE_TO_TYPE, SLO_SIGNAL_KEY, type RiskLevel } from "../../shared/domain"; import { createId } from "../../shared/ids"; import { AuditService } from "./audit-service"; import { OperationsService } from "./operations-service"; export const ORGANIZATION_DEPARTMENTS = ["Executive", "Engineering", "Product", "Design", "Marketing", "Sales", "Finance", "Operations", "Support"] as const; const RISK_RANK: Record = { low: 0, medium: 1, high: 2 }; export class OrgGovernanceService { private readonly audit: AuditService; private readonly operations: OperationsService; constructor(private readonly db: DatabaseSync) { this.audit = new AuditService(db); this.operations = new OperationsService(db); } seedOrganizationGovernance(orgId: string, boardEmployeeId: string, now: string): void { for (const department of ORGANIZATION_DEPARTMENTS) this.seedDepartmentPolicy(orgId, department, now); this.db.prepare(` insert into approval_routing_rules(id, org_id, kind, minimum_risk, route_to_type, route_to_id, created_at) values (?, ?, ?, ?, ?, ?, ?) on conflict(org_id, kind, minimum_risk) do nothing `).run(createId("arr"), orgId, "*", RISK_LEVEL.LOW, ROUTE_TO_TYPE.EMPLOYEE, boardEmployeeId, now); } /** Resolves the canonical department name (case-insensitive), or throws naming the valid choices. */ requireCanonicalDepartment(orgId: string, department: string): string { const requested = department.trim(); if (!requested) throw new Error("Department is required."); const row = this.db.prepare(` select department_name from hiring_policies where org_id = ? and lower(department_name) = lower(?) `).get(orgId, requested) as { department_name: string } | undefined; if (!row) { const known = this.db.prepare("select department_name from hiring_policies where org_id = ? order by department_name asc") .all(orgId) as Array<{ department_name: string }>; throw new Error(`Unknown department "${requested}". Departments: ${known.map((entry) => entry.department_name).join(", ")}.`); } return row.department_name; } enforceHiringPolicy(input: CreateAgentInput): HiringPolicyDecision { const departmentName = this.requireCanonicalDepartment(input.orgId, input.department); const policy = this.db.prepare(` select max_headcount, monthly_budget_limit_cents, require_approval_above_cents from hiring_policies where org_id = ? and department_name = ? `).get(input.orgId, departmentName) as HiringPolicyRow | undefined; if (!policy) throw new Error("Hiring policy is required for department."); const usage = this.db.prepare(` select count(*) as headcount, coalesce(sum(budget_cents), 0) as budget_cents from agents where org_id = ? and department = ? and lifecycle = ? `).get(input.orgId, departmentName, "active") as { headcount: number; budget_cents: number }; if (usage.headcount >= policy.max_headcount) throw new Error("Department headcount limit has been reached."); if (usage.budget_cents + input.budgetCents > policy.monthly_budget_limit_cents) { throw new Error("Department monthly budget limit has been reached."); } return { reviewRequired: input.budgetCents > policy.require_approval_above_cents, departmentName }; } routeApproval(input: { orgId: string; approvalId: string; kind: string; risk: RiskLevel }): void { const rule = this.resolveRoutingRule(input.orgId, input.kind, input.risk); this.assertRouteTarget(input.orgId, rule.route_to_type, rule.route_to_id); this.db.prepare(` insert into approval_assignments(id, org_id, approval_id, assignee_type, assignee_id, routing_rule_id, created_at) values (?, ?, ?, ?, ?, ?, ?) on conflict(approval_id, assignee_type, assignee_id) do nothing `).run(createId("aas"), input.orgId, input.approvalId, rule.route_to_type, rule.route_to_id, rule.id, new Date().toISOString()); this.audit.record({ orgId: input.orgId, entityType: "approval", entityId: input.approvalId, actorType: "system", actorId: null, action: "routed", payload: { routingRuleId: rule.id, routeToType: rule.route_to_type, routeToId: rule.route_to_id }, }); this.operations.recordSignal({ orgId: input.orgId, signalKey: SLO_SIGNAL_KEY.APPROVAL_ROUTE_COVERAGE, source: "approval_routing", success: true, durationMs: null, detail: { approvalId: input.approvalId, routingRuleId: rule.id }, }); } private seedDepartmentPolicy(orgId: string, departmentName: string, now: string): void { this.db.prepare(` insert into org_departments(id, org_id, name, lead_agent_id, headcount_limit, monthly_budget_limit_cents, created_at, updated_at) values (?, ?, ?, null, 12, 2500000, ?, ?) on conflict(org_id, name) do nothing `).run(createId("dep"), orgId, departmentName, now, now); this.db.prepare(` insert into hiring_policies(id, org_id, department_name, max_headcount, monthly_budget_limit_cents, require_approval_above_cents, created_at, updated_at) values (?, ?, ?, 12, 2500000, 500000, ?, ?) on conflict(org_id, department_name) do nothing `).run(createId("hpl"), orgId, departmentName, now, now); } private resolveRoutingRule(orgId: string, kind: string, risk: RiskLevel): ApprovalRoutingRuleRow { const rows = this.db.prepare(` select id, kind, minimum_risk, route_to_type, route_to_id from approval_routing_rules where org_id = ? and (kind = ? or kind = ?) `).all(orgId, kind, "*") as unknown as ApprovalRoutingRuleRow[]; const matching = rows .filter((row) => riskApplies(row.minimum_risk, risk)) .sort((a, b) => ruleRank(kind, b) - ruleRank(kind, a)); const selected = matching[0]; if (!selected) throw new Error("Approval routing rule is required."); return selected; } private assertRouteTarget(orgId: string, routeToType: string, routeToId: string): void { if (routeToType === ROUTE_TO_TYPE.BOARD) { if (routeToId !== orgId) throw new Error("Board approval route is invalid."); return; } if (routeToType === ROUTE_TO_TYPE.EMPLOYEE) { const employee = this.db.prepare("select id from employees where id = ? and org_id = ?").get(routeToId, orgId); if (!employee) throw new Error("Approval route employee was not found."); return; } if (routeToType === ROUTE_TO_TYPE.AGENT) { const agent = this.db.prepare("select id from agents where id = ? and org_id = ?").get(routeToId, orgId); if (!agent) throw new Error("Approval route agent was not found."); return; } throw new Error("Approval route target type is invalid."); } } interface HiringPolicyRow { max_headcount: number; monthly_budget_limit_cents: number; require_approval_above_cents: number; } interface HiringPolicyDecision { reviewRequired: boolean; /** Canonical department name from the policy table (the input may differ in case). */ departmentName: string; } interface ApprovalRoutingRuleRow { id: string; kind: string; minimum_risk: string; route_to_type: string; route_to_id: string; } function riskApplies(minimumRisk: string, risk: RiskLevel): boolean { const required = RISK_RANK[minimumRisk as RiskLevel]; if (required === undefined) throw new Error("Approval routing risk is invalid."); return RISK_RANK[risk] >= required; } function ruleRank(kind: string, rule: ApprovalRoutingRuleRow): number { const exactKind = rule.kind === kind ? 100 : 0; return exactKind + RISK_RANK[rule.minimum_risk as RiskLevel]; }