Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions bloom-runtime/src/agent_reconciliation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,19 @@ const WRITER_ROLES: &[&str] = &[
"design-system",
"designer",
"frontend",
"frontend-ui",
"frontend-state",
"backend",
"backend-api",
"backend-domain",
"integration",
"test-automation",
"performance",
"observability",
"database",
"security",
"devops",
"accessibility",
"data-marketing",
"documentation",
"debug-router",
Expand Down
36 changes: 30 additions & 6 deletions bloom-runtime/src/agent_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,19 @@ const ALLOWED_ROLES: &[&str] = &[
"design-system",
"designer",
"frontend",
"frontend-ui",
"frontend-state",
"backend",
"backend-api",
"backend-domain",
"integration",
"test-automation",
"performance",
"observability",
"database",
"security",
"devops",
"accessibility",
"data-marketing",
"code-review",
"reviewer",
Expand Down Expand Up @@ -256,7 +268,19 @@ fn is_repository_writer(role: &str) -> bool {
"design-system"
| "designer"
| "frontend"
| "frontend-ui"
| "frontend-state"
| "backend"
| "backend-api"
| "backend-domain"
| "integration"
| "test-automation"
| "performance"
| "observability"
| "database"
| "security"
| "devops"
| "accessibility"
| "data-marketing"
| "documentation"
| "debug-router"
Expand Down Expand Up @@ -410,13 +434,13 @@ fn agent_prompt(input: &AgentTaskRuntimeInput, branch: Option<&str>) -> String {
branch.unwrap_or("unknown")
)
} else if is_review_role(&input.role) {
"You are an independent verification/review worker. Do not modify product source files or create a feature branch. Inspect the actual repository and dependency PRs directly. Run the checks appropriate to your role. When reviewing a PR, leave a concise top-level PR comment prefixed with your Luna Agent ID and an evidence-based verdict. Do not pretend GitHub native self-approval is an independent approval when all agents share one GitHub credential.".to_string()
"You are an independent verification/review worker. Do not modify product source files or create a feature branch. Inspect the actual repository and dependency PRs directly. Run the checks appropriate to your role. When reviewing a PR, leave a concise top-level PR comment prefixed with your Bloom Agent ID and an evidence-based verdict. Do not pretend GitHub native self-approval is an independent approval when all agents share one GitHub credential.".to_string()
} else {
"You are an independent analysis worker. Inspect available repository and dependency evidence, produce a concrete task result, and do not modify product source files unless the task contract explicitly requires repository changes.".to_string()
};

format!(
"You are Luna Agent `{agent_id}` ({team_name} / {role}).\n\n{mode}\n\nTask: {task_id} — {title}\n{summary}\n\nAcceptance criteria:\n{criteria}\n\nOriginal Product Owner request:\n{user_request}\n\nProduct summary:\n{product_summary}\n\nArchitecture summary:\n{architecture_summary}\n\nDependency evidence:\n{dependencies}\n\nRules:\n- Inspect real repository evidence before material decisions.\n- Do not blindly trust PM, Reviewer, Code Review, QA, or another Agent; independently verify relevant claims.\n- Every material action must have a defensible reason based on requirements, repository state, tests, runtime evidence, or explicit Product Owner direction.\n- Do not invent test results, metrics, user research, credentials, deployments, or external-service state.\n- If verification cannot be run, record the exact blocker instead of calling it passed.\n- Never expose secrets in logs, commits, PRs, reports, or documentation.\n- Return only the structured JSON report required by Luna.\n",
"You are Bloom Agent `{agent_id}` ({team_name} / {role}).\n\n{mode}\n\nTask: {task_id} — {title}\n{summary}\n\nAcceptance criteria:\n{criteria}\n\nOriginal Product Owner request:\n{user_request}\n\nProduct summary:\n{product_summary}\n\nArchitecture summary:\n{architecture_summary}\n\nDependency evidence:\n{dependencies}\n\nRules:\n- Inspect real repository evidence before material decisions.\n- Do not blindly trust PM, Reviewer, Code Review, QA, or another Agent; independently verify relevant claims.\n- Every material action must have a defensible reason based on requirements, repository state, tests, runtime evidence, or explicit Product Owner direction.\n- Do not invent test results, metrics, user research, credentials, deployments, or external-service state.\n- If verification cannot be run, record the exact blocker instead of calling it passed.\n- Never expose secrets in logs, commits, PRs, reports, or documentation.\n- Return only the structured JSON report required by Bloom.\n",
agent_id = input.agent_id,
team_name = input.team_name,
role = input.role,
Expand Down Expand Up @@ -546,8 +570,8 @@ fn run_app_server_agent(
"id": 0,
"params": {
"clientInfo": {
"name": "luna_project_teams",
"title": "Luna Project Teams",
"name": "bloom_project_teams",
"title": "Bloom Project Teams",
"version": "0.1.0"
},
"capabilities": {}
Expand All @@ -566,7 +590,7 @@ fn run_app_server_agent(
"cwd": worktree.to_string_lossy(),
"approvalPolicy": "never",
"sandbox": "workspace-write",
"serviceName": "luna_project_teams"
"serviceName": "bloom_project_teams"
}
}),
)?;
Expand Down Expand Up @@ -832,4 +856,4 @@ pub async fn dispatch_agent_task(input: AgentTaskRuntimeInput) -> Result<AgentTa
tauri::async_runtime::spawn_blocking(move || dispatch_agent_task_blocking(input))
.await
.map_err(|error| format!("Agent Runtime join 실패: {error}"))?
}
}
38 changes: 34 additions & 4 deletions bloom-runtime/src/project_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,19 @@ const PM_PLAN_SCHEMA: &str = r#"{
"design-system",
"designer",
"frontend",
"frontend-ui",
"frontend-state",
"backend",
"backend-api",
"backend-domain",
"integration",
"test-automation",
"performance",
"observability",
"database",
"security",
"devops",
"accessibility",
"code-review",
"reviewer",
"qa",
Expand Down Expand Up @@ -112,7 +124,19 @@ const ALLOWED_PM_TASK_ROLES: &[&str] = &[
"design-system",
"designer",
"frontend",
"frontend-ui",
"frontend-state",
"backend",
"backend-api",
"backend-domain",
"integration",
"test-automation",
"performance",
"observability",
"database",
"security",
"devops",
"accessibility",
"code-review",
"reviewer",
"qa",
Expand Down Expand Up @@ -647,14 +671,14 @@ fn pm_prompt(
request: &str,
) -> String {
format!(
r#"You are the independent PM Codex Agent for Luna team {team_name} ({team_id}).
r#"You are the independent PM Codex Agent for Bloom team {team_name} ({team_id}).

Project ID: {project_id}
GitHub Organization: {organization}
User request:
{request}

Your job in this turn is planning only. Do not create files, repositories, branches, commits, PRs, or deployments. Return the project plan that Luna will execute after this turn.
Your job in this turn is planning only. Do not create files, repositories, branches, commits, PRs, or deployments. Return the project plan that Bloom will execute after this turn.

Operating contract:
- Treat the project as a real production service, not a demo or mock-only prototype.
Expand All @@ -666,7 +690,13 @@ Operating contract:
- Branch convention is agent/<team>/<role>/<task>. taskSlug must be concise lowercase ASCII kebab-case.
- Agents do not blindly trust PM or reviewers. Every material action must have a defensible, verifiable reason.
- Code Review, higher-level Reviewer, QA, Documentation, User A, User B, and Process Evaluator should be included as independent gates for a normal user-facing production service. Omit a role only when it genuinely does not apply.
- Frontend and Backend tasks may run in parallel when dependencies allow it.
- Split large implementation work across independent roles when ownership boundaries are clear instead of assigning one giant Frontend or Backend task.
- Use frontend-ui for pages/components/layout/style/interactions, frontend-state for forms/state/data-fetching/cache/client data flow, and frontend for app shell or final frontend integration when useful.
- Use backend-api for endpoints/contracts/validation/controllers, backend-domain for services/use-cases/business rules, and backend for server shell or final backend integration when useful.
- Use integration for external APIs/webhooks/realtime/adapters, test-automation for automated unit/integration/E2E harness work, performance for measured bottleneck work, and observability for logs/health/metrics/tracing/error diagnosis.
- Database, Security, DevOps, and Accessibility are specialized implementation roles and should receive their own tasks when the project actually needs those concerns.
- Prefer tasks with minimal shared-file ownership so independent branches can progress safely and later run in parallel. Do not invent parallelism when two tasks inherently need the same implementation boundary.
- Frontend-family and Backend-family tasks may run independently when dependencies allow it.
- Acceptance criteria must be observable and verifiable. Include build/test/browser/error/loading/empty/security requirements where relevant.
- Do not assume external credentials, paid services, or unavailable datasets exist. Model them as explicit implementation blockers or setup tasks when necessary.
- Keep tasks independently reviewable. Avoid one giant frontend or backend task covering the whole project.
Expand Down Expand Up @@ -725,7 +755,7 @@ fn run_pm_codex(
return Err("Codex CLI 로그인이 필요합니다. `codex login`을 실행해 주세요.".to_string());
}
if !preflight.codex_chatgpt_auth {
return Err("Luna는 ChatGPT 로그인 상태의 Codex만 실행합니다.".to_string());
return Err("Bloom은 ChatGPT 로그인 상태의 Codex만 실행합니다.".to_string());
}

let planning_dir = PathBuf::from(workspace_root)
Expand Down
49 changes: 44 additions & 5 deletions bloom-runtime/src/replan_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,19 @@ const ALLOWED_TASK_ROLES: &[&str] = &[
"design-system",
"designer",
"frontend",
"frontend-ui",
"frontend-state",
"backend",
"backend-api",
"backend-domain",
"integration",
"test-automation",
"performance",
"observability",
"database",
"security",
"devops",
"accessibility",
"code-review",
"reviewer",
"qa",
Expand All @@ -27,7 +39,20 @@ const REPOSITORY_WRITER_ROLES: &[&str] = &[
"design-system",
"designer",
"frontend",
"frontend-ui",
"frontend-state",
"backend",
"backend-api",
"backend-domain",
"integration",
"test-automation",
"performance",
"observability",
"database",
"security",
"devops",
"accessibility",
"data-marketing",
"documentation",
"debug-router",
];
Expand Down Expand Up @@ -81,7 +106,19 @@ const REPLAN_SCHEMA: &str = r#"{
"design-system",
"designer",
"frontend",
"frontend-ui",
"frontend-state",
"backend",
"backend-api",
"backend-domain",
"integration",
"test-automation",
"performance",
"observability",
"database",
"security",
"devops",
"accessibility",
"code-review",
"reviewer",
"qa",
Expand Down Expand Up @@ -510,7 +547,7 @@ fn prompt(input: &ReplanProjectInput) -> Result<String, String> {
let reopenable = input.reopenable_task_ids.join(", ");

Ok(format!(
r#"You are the independent PM Codex Agent for Luna team {team_name} ({team_id}).
r#"You are the independent PM Codex Agent for Bloom team {team_name} ({team_id}).

Project: {project_id}
Existing repository: {repository}
Expand All @@ -531,24 +568,26 @@ Failure route evidence:
Current PM tasks and actual runtime status:
{tasks}

Tasks Luna permits you to retire because they have no completed/external Git work:
Tasks Bloom permits you to retire because they have no completed/external Git work:
{retirable}

Tasks Luna permits you to reopen on their existing branch/worktree where applicable:
Tasks Bloom permits you to reopen on their existing branch/worktree where applicable:
{reopenable}

Replan attempt: {replan_attempt}/{max_attempts}

Your job is a repair replan only. You MUST NOT create a repository, rename the repository, modify files, commit, push, merge, deploy, or claim tests passed. Luna will apply only the validated plan operations you return.
Your job is a repair replan only. You MUST NOT create a repository, rename the repository, modify files, commit, push, merge, deploy, or claim tests passed. Bloom will apply only the validated plan operations you return.

Rules:
- Preserve completed/external Git work. You cannot edit existing Task definitions in this response.
- `retireTaskIds` may contain only IDs from the retirable list.
- `reopenTaskIds` may contain only IDs from the reopenable list. Use reopen when the existing Agent/branch should own the fix or its verification must be repeated.
- `newTasks` are appended repair Tasks. Their IDs must be brand-new and must not reuse any current Task ID, even a retired one.
- New repair Tasks may use the implementation swarm roles: frontend-ui, frontend-state, backend-api, backend-domain, integration, test-automation, performance, observability, plus database, security, devops, and accessibility when those are the real repair owners.
- Split a repair into a specialist role only when the ownership boundary is concrete; do not create extra Agents merely to appear parallel.
- Do not create a normal `debug-router` Task; the dedicated Debug Router already ran.
- New dependencies may reference current non-retired Tasks or other new Tasks only.
- Resolve the escalated failed Task: either retire it, reopen it, or reopen one of its actual upstream Tasks so that Luna can rewind and re-run the failed downstream chain.
- Resolve the escalated failed Task: either retire it, reopen it, or reopen one of its actual upstream Tasks so that Bloom can rewind and re-run the failed downstream chain.
- Every repository-changing Task in the resulting DAG must still have a downstream Code Review → Reviewer → QA chain.
- Prefer the smallest repair plan that addresses the evidence. Do not rewrite unrelated product scope.
- If the evidence actually requires a Product Owner choice or unavailable credential instead of PM replanning, do not invent a workaround. Return a minimal safe plan only if one truly exists; otherwise the Runtime validation may reject this proposal and keep the project blocked.
Expand Down
44 changes: 44 additions & 0 deletions bloom-runtime/ts/catalog.policy-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { createInitialProjectTeamsState } from "./catalog";
import { SPECIALIST_AGENT_ROLES } from "./specialistPlanning";
import type { ExecutableAgentRole } from "./types";

const IMPLEMENTATION_ACCELERATOR_ROLES: ExecutableAgentRole[] = [
"frontend-ui",
"frontend-state",
"backend-api",
"backend-domain",
"integration",
"test-automation",
"performance",
"observability",
];

function assert(condition: unknown, message: string): asserts condition {
if (!condition) throw new Error(message);
}

function testEveryTeamHasExpandedRoster() {
const state = createInitialProjectTeamsState();
const expandedRoles = [...IMPLEMENTATION_ACCELERATOR_ROLES, ...SPECIALIST_AGENT_ROLES];

assert(state.teams.length === 5, "Bloom must keep the five peer delivery teams");
for (const team of state.teams) {
assert(team.agents.length === 27, `${team.id} must contain 27 Agents after implementation expansion`);
assert(new Set(team.agents.map((agent) => agent.id)).size === 27, `${team.id} Agent IDs must remain unique`);

for (const role of expandedRoles) {
const agent = team.agents.find((candidate) => candidate.role === role);
assert(agent, `${team.id} must include a ${role} Agent`);
assert(agent.id === `${team.id}:${role}`, `${team.id} ${role} Agent must use the stable team:role identity`);
assert(agent.autonomy === "independent", `${team.id} ${role} Agent must remain independently accountable`);
assert(agent.minimumExperienceYears === 10, `${team.id} ${role} Agent must use the senior operating standard`);
}
}
}

function run() {
testEveryTeamHasExpandedRoster();
console.log("catalog policy tests passed");
}

run();
Loading
Loading