Founder-CEO direct conversation is a structural invariant

Every org now keeps one direct chat between the primary employee and the
active c_suite agent: created at onboarding bootstrap (hard failure if
missing), repaired for existing orgs at startup. The existing
wakeDirectCounterparts path already wakes the CEO on every founder
message, so this space is the pinned "talk to your AI CEO" entry the
Messages page will surface next.
This commit is contained in:
ZacharyZhang-NY committed 2026-07-14 00:07:30 -04:00
1 parent 74195c6cdd
commit 62ce1570cd
4 files changed
+172

No files matched your search

+29
View File
@@ -0,0 +1,29 @@
import type { DatabaseSync } from "node:sqlite";
import { ChatService } from "./chat-service";
/**
* The founder's direct line to the CEO: every organization keeps one direct
* conversation between its primary employee (the human operator) and its CEO
* (the c_suite agent — the same predicate the grant system enforces). The
* Messages page pins this conversation at the top, so it exists as a
* structural invariant instead of appearing lazily on first use.
*
* Returns the space id, or null while the org has no employee or no active
* CEO yet (mid-bootstrap) — the next ensure pass picks it up.
*/
export function ensureCeoDirectSpace(db: DatabaseSync, orgId: string): string | null {
const employee = db.prepare("select id from employees where org_id = ? order by created_at asc limit 1")
.get(orgId) as { id: string } | undefined;
const ceo = db.prepare(`
select id from agents
where org_id = ? and role_level = 'c_suite' and lifecycle = 'active'
order by created_at asc
limit 1
`).get(orgId) as { id: string } | undefined;
if (!employee || !ceo) return null;
return new ChatService(db).ensureDirectSpace(
orgId,
{ type: "employee", id: employee.id },
{ type: "agent", id: ceo.id },
);
}
+6
View File
@@ -8,6 +8,7 @@ import { PROVIDER_AUTH_TYPE, RISK_LEVEL, TASK_STATUS } from "../../shared/domain
import { PROVIDER_CATALOG } from "../../shared/provider-catalog";
import { createId } from "../../shared/ids";
import { AuditService } from "./audit-service";
import { ensureCeoDirectSpace } from "./ceo-direct-space";
import { goalKickoffDescription, goalKickoffTitle } from "./goal-kickoff";
import { OrganizationService } from "./organization-service";
import { ChatService } from "./chat-service";
@@ -70,6 +71,11 @@ export class OnboardingService {
const goalId = this.createGoal(orgId, ceoAgentId, input, now);
const projectId = this.createProject(orgId, goalId, ceoAgentId, input, now);
this.createProjectChatSpace(orgId, projectName(input));
// The founder⇄CEO conversation is a bootstrap deliverable, not an option:
// the Messages page pins it, so its absence here is a hard failure.
if (!ensureCeoDirectSpace(this.deps.db, orgId)) {
throw new Error("The founder⇄CEO conversation was not created during bootstrap.");
}
if (workspaceId) this.bindWorkspaceToProject(workspaceId, projectId, now);
const taskId = this.organization.createTask({
orgId,