diff --git a/src/main/prompt-builder/messages/operating-en.ts b/src/main/prompt-builder/messages/operating-en.ts index bcefd237..11e31cd2 100644 --- a/src/main/prompt-builder/messages/operating-en.ts +++ b/src/main/prompt-builder/messages/operating-en.ts @@ -164,6 +164,7 @@ When your task is set back to "in_progress" with feedback: - When completing a significant milestone, post a progress comment even if not required - If you discover something unexpected, comment immediately — don't wait - When creating subtasks for yourself, comment on the parent explaining your breakdown +- You are authorised to DM any peer directly — including leads in other departments listed in the Company Department Map — when their expertise unblocks your work. Do not funnel every cross-department question through your manager. - If you notice a task could benefit from another agent's input, send them a message: POST /api/companies/${companyId}/messages with channel "direct" - After solving a tricky problem, add it to the knowledge base: @@ -218,7 +219,7 @@ export const authorityRules: Record = { "Submit work for review (in_review status)", "Post comments on tasks you're involved with", "Request approvals when blocked (secret_access, etc.)", - "Send messages to your manager and teammates", + "Send direct messages to your manager, teammates, and any cross-department lead listed in the Company Department Map", "Create knowledge base entries from your learnings", "Create documents relevant to your work (specs, post-mortems)", "Report blockers and escalate to your manager", diff --git a/src/main/prompt-builder/system-prompt.ts b/src/main/prompt-builder/system-prompt.ts index 382cab94..89ac596d 100644 --- a/src/main/prompt-builder/system-prompt.ts +++ b/src/main/prompt-builder/system-prompt.ts @@ -142,20 +142,29 @@ export function buildSystemPrompt(ctx: PromptContext): string { sections.push(m.departmentMembersSection(deptLabel, memberLines)); } - if (isCEO || ctx.directReports.length > 0) { - const deptMap = new Map(); - for (const a of ctx.allAgents.filter((a) => a.status !== "terminated")) { - const dept = a.department ?? "unassigned"; - if (!deptMap.has(dept)) deptMap.set(dept, []); - deptMap.get(dept)!.push(a); - } - if (deptMap.size > 1) { - const deptLines = Array.from(deptMap.entries()).map(([dept, members]) => { - const label = DEPARTMENT_LABELS[dept as DepartmentId] ?? dept; + // Every agent needs to see where the other departments' entry points + // are so DMs can cross the org without going through the CEO. Leads + // (CEO + managers) get the full member roll call; individual + // contributors get the single lead per department so their prompt + // stays lean while still enabling direct cross-dept reach-out. + const deptMap = new Map(); + for (const a of ctx.allAgents.filter((a) => a.status !== "terminated")) { + const dept = a.department ?? "unassigned"; + if (!deptMap.has(dept)) deptMap.set(dept, []); + deptMap.get(dept)!.push(a); + } + if (deptMap.size > 1) { + const showAllMembers = isCEO || isManager; + const deptLines = Array.from(deptMap.entries()).map(([dept, members]) => { + const label = DEPARTMENT_LABELS[dept as DepartmentId] ?? dept; + if (showAllMembers) { return m.companyDeptMapLine(label, members.length, members.map((x) => x.name).join(", ")); - }).join("\n"); - sections.push(m.companyDepartmentMapSection(deptLines)); - } + } + const leads = members.filter((x) => !x.reportsTo || ctx.directReports.every((r) => r.id !== x.id)); + const leadNames = (leads.length > 0 ? leads : members).slice(0, 3).map((x) => x.name).join(", "); + return m.companyDeptMapLine(label, members.length, leadNames); + }).join("\n"); + sections.push(m.companyDepartmentMapSection(deptLines)); } const activeGoals = ctx.goals.filter((g) => g.status === "active" || g.status === "planned");