From ac56643aa5871fba4b40b9364d44ef1f281d938d Mon Sep 17 00:00:00 2001 From: sunwoo162 Date: Thu, 27 Aug 2026 11:41:03 +0900 Subject: [PATCH 01/19] feat: add Bloom specialist agent roles --- bloom-runtime/ts/types.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bloom-runtime/ts/types.ts b/bloom-runtime/ts/types.ts index f3619c31..f208fa9d 100644 --- a/bloom-runtime/ts/types.ts +++ b/bloom-runtime/ts/types.ts @@ -9,6 +9,10 @@ export type AgentRole = | "designer" | "frontend" | "backend" + | "database" + | "security" + | "devops" + | "accessibility" | "data-marketing" | "code-review" | "reviewer" From e56a537bfdfa0682d67f6bd2581054a835e66dd6 Mon Sep 17 00:00:00 2001 From: sunwoo162 Date: Thu, 27 Aug 2026 11:41:28 +0900 Subject: [PATCH 02/19] feat: expand Bloom team roster --- bloom-runtime/ts/catalog.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/bloom-runtime/ts/catalog.ts b/bloom-runtime/ts/catalog.ts index 194f658c..1e8f7f3f 100644 --- a/bloom-runtime/ts/catalog.ts +++ b/bloom-runtime/ts/catalog.ts @@ -16,7 +16,7 @@ export const BOUQUET_AUTH_POLICY = { export const EXECUTION_POLICY = { id: "iseol-workflow" as const, - version: "1.4.0", + version: "1.5.0", summary: "모든 Agent는 이설 작업 방식처럼 실제 저장소를 기준으로 독립적으로 작업하며 10년 이상 시니어 수준의 품질 기준을 적용합니다.", rules: [ "모든 Agent는 자신의 전문 역할에서 최소 10년 이상 실무를 수행한 시니어 수준의 판단, 품질 기준, 리스크 감각을 적용합니다.", @@ -27,6 +27,7 @@ export const EXECUTION_POLICY = { "검증하지 못한 항목은 성공했다고 기록하지 않고 정확한 blocker를 남깁니다.", "커밋은 작은 작업 단위로 나누고 영어 커밋 메시지를 사용합니다.", "작업한 Agent가 직접 branch push와 PR 생성/업데이트를 수행합니다.", + "Database, Security, DevOps, Accessibility 전문 Agent는 관련 위험이나 구현 범위가 있을 때 별도 Task와 PR로 독립 검증합니다.", "Code Review Agent가 코드 품질을 독립 검토하고 Reviewer Agent가 기능/요구사항/구조를 별도로 검토합니다.", "QA Agent는 실제 build/test/사용 흐름을 검증하고 Documentation Agent는 검증된 사실을 기준으로 사용자·개발·운영·마케팅 문서를 맞춥니다.", "Data & Marketing Agent는 제품, 사용자, 채널, 퍼널, 지표, SEO/콘텐츠, 출시 실험을 근거 중심으로 분석하고 Documentation Agent가 검증·정리할 수 있는 마케팅 산출물을 남깁니다.", @@ -42,6 +43,7 @@ export const WORKFLOW_STAGES = [ "Design System", "Designer", "Frontend / Backend", + "Database / Security / DevOps / Accessibility", "Code Review", "Reviewer", "QA", @@ -63,7 +65,11 @@ const agentCatalog: Array<{ { role: "design-system", label: "Design System Agent", description: "Figma와 실제 제품 근거로 디자인 규칙을 결정" }, { role: "designer", label: "Designer Agent", description: "제품 화면과 상호작용을 설계" }, { role: "frontend", label: "Frontend Agent", description: "실제 프론트엔드 저장소를 구현" }, - { role: "backend", label: "Backend Agent", description: "API, DB, 서버 영역을 구현" }, + { role: "backend", label: "Backend Agent", description: "API, 서버 도메인과 애플리케이션 로직을 구현" }, + { role: "database", label: "Database Agent", description: "스키마, 마이그레이션, 쿼리, 트랜잭션, 데이터 무결성과 저장 성능을 구현·검증" }, + { role: "security", label: "Security Agent", description: "인증·인가, 입력 검증, secret, 의존성, 민감정보와 공격 표면을 독립 검증" }, + { role: "devops", label: "DevOps Agent", description: "CI/CD, 실행 환경, 배포 설정, 컨테이너, 관측 가능성과 재현 가능한 운영 경로를 구현·검증" }, + { role: "accessibility", label: "Accessibility Agent", description: "시맨틱 UI, 키보드·포커스, 스크린리더, 폼 오류와 접근성 사용 흐름을 구현·검증" }, { role: "data-marketing", label: "Data & Marketing Agent", description: "제품 데이터, 사용자 세그먼트, 퍼널, 채널, SEO/콘텐츠, 출시 실험을 분석하고 마케팅 전략 문서를 작성" }, { role: "code-review", label: "Code Review Agent", description: "PR diff의 코드 품질, 버그, 보안, 성능, 테스트 누락을 독립 검토" }, { role: "reviewer", label: "Reviewer Agent", description: "기능, 요구사항, 구조와 제품 완성도를 독립 검토" }, From 41eff189abc21c827d38fed60fe93ecc91914648 Mon Sep 17 00:00:00 2001 From: sunwoo162 Date: Thu, 27 Aug 2026 11:42:05 +0900 Subject: [PATCH 03/19] feat: add specialist task routing --- bloom-runtime/ts/specialistPlanning.ts | 167 +++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 bloom-runtime/ts/specialistPlanning.ts diff --git a/bloom-runtime/ts/specialistPlanning.ts b/bloom-runtime/ts/specialistPlanning.ts new file mode 100644 index 00000000..56c49168 --- /dev/null +++ b/bloom-runtime/ts/specialistPlanning.ts @@ -0,0 +1,167 @@ +import type { ExecutableAgentRole, ProjectPlan, ProjectTaskPlan } from "./types"; + +export const SPECIALIST_AGENT_ROLES = [ + "database", + "security", + "devops", + "accessibility", +] as const satisfies readonly ExecutableAgentRole[]; + +const MAX_TASKS_BEFORE_MANDATORY_MARKETING = 35; + +type SpecialistRole = (typeof SPECIALIST_AGENT_ROLES)[number]; + +type SpecialistDefinition = { + role: SpecialistRole; + prefix: string; + slug: string; + title: string; + summary: string; + acceptanceCriteria: string[]; + shouldActivate: (plan: ProjectPlan, text: string) => boolean; + dependencyRoles: ExecutableAgentRole[]; +}; + +function normalizedPlanText(plan: ProjectPlan) { + return [ + plan.productSummary, + plan.architectureSummary, + ...plan.technologyDecisions.flatMap((decision) => [decision.area, decision.choice, decision.reason]), + ...plan.tasks.flatMap((task) => [task.title, task.summary, ...task.acceptanceCriteria]), + ].join(" ").toLowerCase(); +} + +function includesAny(text: string, values: string[]) { + return values.some((value) => text.includes(value)); +} + +function hasAnyRole(plan: ProjectPlan, roles: ExecutableAgentRole[]) { + return plan.tasks.some((task) => roles.includes(task.role)); +} + +function nextTaskId(tasks: ProjectTaskPlan[], prefix: string) { + const used = new Set(tasks.map((task) => task.id)); + for (let index = 1; index <= 999; index += 1) { + const candidate = `${prefix}-${String(index).padStart(3, "0")}`; + if (!used.has(candidate)) return candidate; + } + throw new Error(`${prefix} specialist Task ID를 생성할 수 없습니다.`); +} + +function nextTaskSlug(tasks: ProjectTaskPlan[], base: string) { + const used = new Set(tasks.map((task) => task.taskSlug)); + if (!used.has(base)) return base; + for (let index = 2; index <= 99; index += 1) { + const candidate = `${base}-${index}`; + if (!used.has(candidate)) return candidate; + } + throw new Error(`${base} specialist taskSlug를 생성할 수 없습니다.`); +} + +function terminalTaskIds(tasks: ProjectTaskPlan[]) { + const referenced = new Set(tasks.flatMap((task) => task.dependsOn)); + return tasks.filter((task) => !referenced.has(task.id)).map((task) => task.id); +} + +function dependenciesForRoles(tasks: ProjectTaskPlan[], roles: ExecutableAgentRole[]) { + const roleTaskIds = tasks.filter((task) => roles.includes(task.role)).map((task) => task.id); + return roleTaskIds.length > 0 ? roleTaskIds : terminalTaskIds(tasks); +} + +const definitions: SpecialistDefinition[] = [ + { + role: "database", + prefix: "DB", + slug: "database-integrity", + title: "데이터 모델 및 저장 계층 검증", + summary: "Database Agent가 실제 구현을 기준으로 스키마, 마이그레이션, 쿼리, 트랜잭션, 인덱스와 데이터 무결성을 검토하고 필요한 저장 계층 변경을 별도 branch/PR로 구현합니다. 근거 없는 성능 수치는 만들지 않고 재현 가능한 테스트와 migration evidence를 남깁니다.", + acceptanceCriteria: [ + "실제 도메인 요구사항과 데이터 모델, 제약조건, 관계가 일치한다.", + "스키마 변경이 있다면 재현 가능한 migration 또는 동등한 버전 관리 경로가 존재한다.", + "핵심 읽기/쓰기 경로의 트랜잭션 경계, 무결성, 오류 처리가 검증된다.", + "쿼리 또는 저장 계층 변경에 가능한 자동 테스트나 실행 evidence가 남는다.", + ], + shouldActivate: (plan, text) => hasAnyRole(plan, ["backend"]) + || includesAny(text, ["database", " db ", "sql", "postgres", "mysql", "sqlite", "schema", "migration", "persistence", "storage"]), + dependencyRoles: ["backend"], + }, + { + role: "security", + prefix: "SEC", + slug: "security-hardening", + title: "보안 경계 및 위협 표면 검증", + summary: "Security Agent가 인증·인가, 입력 검증, 민감정보, secret, 의존성, 권한 경계와 주요 공격 표면을 실제 코드와 설정에서 독립 검증하고 필요한 보안 수정을 별도 branch/PR로 구현합니다. 다른 Agent의 보안 주장을 그대로 신뢰하지 않고 evidence를 직접 확인합니다.", + acceptanceCriteria: [ + "인증·인가가 필요한 경로의 권한 경계와 실패 동작이 실제 코드에서 검증된다.", + "외부 입력과 민감 데이터 처리 경로에서 검증·노출·보존 위험을 확인한다.", + "secret 값이 repository, 로그, 사용자 응답에 하드코딩되지 않는다.", + "발견한 위험과 수정 사항에 재현 가능한 테스트 또는 명확한 검증 evidence를 남긴다.", + ], + shouldActivate: (plan, text) => plan.needsAuth + || includesAny(text, ["auth", "login", "permission", "security", "secret", "token", "payment", "admin", "sensitive"]), + dependencyRoles: ["frontend", "backend", "database"], + }, + { + role: "accessibility", + prefix: "A11Y", + slug: "accessibility-validation", + title: "접근성 사용자 흐름 검증", + summary: "Accessibility Agent가 실제 사용자 화면을 기준으로 시맨틱 구조, 키보드·포커스, 스크린리더 이름, 폼/오류 전달과 주요 접근성 흐름을 독립 검증하고 필요한 수정을 별도 branch/PR로 구현합니다. 시각적 추정만으로 통과시키지 않고 가능한 자동·수동 evidence를 남깁니다.", + acceptanceCriteria: [ + "핵심 사용자 흐름이 키보드만으로 진행 가능하고 포커스 이동이 예측 가능하다.", + "상호작용 요소에 적절한 시맨틱 요소와 접근 가능한 이름이 제공된다.", + "폼 상태, 오류, 로딩 등 중요한 상태가 시각 정보에만 의존하지 않는다.", + "가능한 접근성 검사 또는 재현 가능한 수동 검증 결과가 evidence로 남는다.", + ], + shouldActivate: (plan, text) => hasAnyRole(plan, ["design-system", "designer", "frontend"]) + || includesAny(text, ["accessibility", "accessible", "a11y", "screen reader", "keyboard", "responsive ui"]), + dependencyRoles: ["design-system", "designer", "frontend"], + }, + { + role: "devops", + prefix: "OPS", + slug: "delivery-runtime-readiness", + title: "빌드·배포·운영 재현성 검증", + summary: "DevOps Agent가 CI/CD, 환경 변수 계약, 실행·배포 설정, 컨테이너 또는 호스팅 경로, 로그와 기본 관측 가능성을 실제 repository 기준으로 검증하고 필요한 운영 변경을 별도 branch/PR로 구현합니다. 실제로 검증하지 않은 배포 성공이나 인프라 존재를 주장하지 않습니다.", + acceptanceCriteria: [ + "깨끗한 환경에서 의존성 설치, build/test, 실행에 필요한 명령과 설정이 재현 가능하다.", + "환경 변수와 secret 경계가 문서/설정에 명확하며 실제 secret 값은 저장소에 포함되지 않는다.", + "CI 또는 동등한 자동 검증 경로가 현재 프로젝트의 핵심 build/test를 실행한다.", + "배포 또는 운영 경로에서 확인하지 못한 항목은 성공으로 표시하지 않고 blocker/evidence를 남긴다.", + ], + shouldActivate: (plan, text) => hasAnyRole(plan, ["frontend", "backend"]) + || includesAny(text, ["deploy", "deployment", "ci/cd", "docker", "container", "infrastructure", "production", "runtime", "hosting"]), + dependencyRoles: ["frontend", "backend", "database", "security"], + }, +]; + +export function ensureSpecialistAgentPlan(plan: ProjectPlan): ProjectPlan { + const existingSpecialists = new Set( + plan.tasks + .filter((task) => SPECIALIST_AGENT_ROLES.includes(task.role as SpecialistRole)) + .map((task) => task.role as SpecialistRole), + ); + const text = normalizedPlanText(plan); + const tasks = [...plan.tasks]; + let remainingSlots = Math.max(0, MAX_TASKS_BEFORE_MANDATORY_MARKETING - tasks.length); + + for (const definition of definitions) { + if (remainingSlots === 0) break; + if (existingSpecialists.has(definition.role) || !definition.shouldActivate(plan, text)) continue; + + const task: ProjectTaskPlan = { + id: nextTaskId(tasks, definition.prefix), + title: definition.title, + role: definition.role, + taskSlug: nextTaskSlug(tasks, definition.slug), + summary: definition.summary, + dependsOn: dependenciesForRoles(tasks, definition.dependencyRoles), + acceptanceCriteria: definition.acceptanceCriteria, + }; + tasks.push(task); + existingSpecialists.add(definition.role); + remainingSlots -= 1; + } + + return tasks.length === plan.tasks.length ? plan : { ...plan, tasks }; +} From f903f2263fffbcf4a9fec6a7b0914663ad7da2a4 Mon Sep 17 00:00:00 2001 From: sunwoo162 Date: Thu, 27 Aug 2026 11:42:24 +0900 Subject: [PATCH 04/19] feat: connect specialist agents to orchestration --- bloom-runtime/ts/orchestrationCore.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bloom-runtime/ts/orchestrationCore.ts b/bloom-runtime/ts/orchestrationCore.ts index d487bb4e..dd966346 100644 --- a/bloom-runtime/ts/orchestrationCore.ts +++ b/bloom-runtime/ts/orchestrationCore.ts @@ -1,6 +1,7 @@ import { ensureBouquetAuthPlan } from "./bouquetAuth"; import { ensureMarketingDocumentationPlan } from "./dataMarketing"; import { validateProjectPlanReviewTopology } from "./planTopology"; +import { ensureSpecialistAgentPlan } from "./specialistPlanning"; import type { ExecutableAgentRole, ProjectPlan, @@ -23,7 +24,8 @@ export type TaskRunSummary = { export function prepareOrchestrationPlan(plan: ProjectPlan): ProjectPlan { const authPlan = ensureBouquetAuthPlan(plan); - const prepared = ensureMarketingDocumentationPlan(authPlan); + const specialistPlan = ensureSpecialistAgentPlan(authPlan); + const prepared = ensureMarketingDocumentationPlan(specialistPlan); validateProjectPlanReviewTopology(prepared); return prepared; } @@ -119,7 +121,7 @@ export function projectStatusForActiveRoles( { roles: ["user-a", "user-b"], status: "user-test" }, { roles: ["qa"], status: "qa" }, { roles: ["code-review", "reviewer", "documentation", "data-marketing"], status: "review" }, - { roles: ["frontend", "backend", "debug-router"], status: "development" }, + { roles: ["frontend", "backend", "database", "security", "devops", "accessibility", "debug-router"], status: "development" }, { roles: ["design-system", "designer"], status: "design" }, { roles: ["idea"], status: "planning" }, ]; From 0cbcff79901cd6990041c7837fa347a9cdf3688b Mon Sep 17 00:00:00 2001 From: sunwoo162 Date: Thu, 27 Aug 2026 11:42:38 +0900 Subject: [PATCH 05/19] feat: require review evidence for specialist agents --- bloom-runtime/ts/planTopology.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bloom-runtime/ts/planTopology.ts b/bloom-runtime/ts/planTopology.ts index 15f5b10b..9b135aa0 100644 --- a/bloom-runtime/ts/planTopology.ts +++ b/bloom-runtime/ts/planTopology.ts @@ -6,6 +6,10 @@ export const REPOSITORY_WRITER_ROLES: Array> = [ "designer", "frontend", "backend", + "database", + "security", + "devops", + "accessibility", "data-marketing", "documentation", "debug-router", From bfb20a3ad5c3d4e0ba318f7f98fd48755b89b9e0 Mon Sep 17 00:00:00 2001 From: sunwoo162 Date: Thu, 27 Aug 2026 11:43:10 +0900 Subject: [PATCH 06/19] test: cover Bloom specialist task routing --- .../ts/specialistPlanning.policy-test.ts | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 bloom-runtime/ts/specialistPlanning.policy-test.ts diff --git a/bloom-runtime/ts/specialistPlanning.policy-test.ts b/bloom-runtime/ts/specialistPlanning.policy-test.ts new file mode 100644 index 00000000..e20a1ced --- /dev/null +++ b/bloom-runtime/ts/specialistPlanning.policy-test.ts @@ -0,0 +1,125 @@ +import { ensureSpecialistAgentPlan, SPECIALIST_AGENT_ROLES } from "./specialistPlanning"; +import type { ProjectPlan, ProjectTaskPlan } from "./types"; + +function assert(condition: unknown, message: string): asserts condition { + if (!condition) throw new Error(message); +} + +function fullStackPlan(): ProjectPlan { + return { + projectName: "Specialist Fixture", + repositoryName: "specialist-fixture", + productSummary: "A user-facing full-stack web application with authentication and SQLite persistence.", + architectureSummary: "React frontend, API backend, SQLite database, CI build and production deployment path.", + needsAuth: true, + technologyDecisions: [ + { area: "database", choice: "SQLite", reason: "small durable relational store" }, + { area: "delivery", choice: "CI", reason: "reproducible build and test gate" }, + ], + tasks: [ + { + id: "FE-001", + title: "Frontend", + role: "frontend", + taskSlug: "frontend-app", + summary: "Implement the accessible user interface.", + dependsOn: [], + acceptanceCriteria: ["frontend build passes"], + }, + { + id: "BE-001", + title: "Backend", + role: "backend", + taskSlug: "backend-api", + summary: "Implement authenticated API and persistence.", + dependsOn: [], + acceptanceCriteria: ["API tests pass"], + }, + ], + }; +} + +function testFullStackSpecialists() { + const prepared = ensureSpecialistAgentPlan(fullStackPlan()); + const roles = new Set(prepared.tasks.map((task) => task.role)); + + for (const role of SPECIALIST_AGENT_ROLES) { + assert(roles.has(role), `full-stack plan must activate ${role} Agent`); + } + + const database = prepared.tasks.find((task) => task.role === "database"); + const security = prepared.tasks.find((task) => task.role === "security"); + const accessibility = prepared.tasks.find((task) => task.role === "accessibility"); + const devops = prepared.tasks.find((task) => task.role === "devops"); + + assert(database?.dependsOn.includes("BE-001"), "Database Agent must wait for backend implementation evidence"); + assert(security?.dependsOn.includes(database.id), "Security Agent must inspect the database specialist result when present"); + assert(accessibility?.dependsOn.includes("FE-001"), "Accessibility Agent must inspect frontend implementation evidence"); + assert(devops?.dependsOn.includes(security.id), "DevOps Agent must consume security hardening evidence when present"); +} + +function testSpecialistRoutingIsIdempotent() { + const once = ensureSpecialistAgentPlan(fullStackPlan()); + const twice = ensureSpecialistAgentPlan(once); + + for (const role of SPECIALIST_AGENT_ROLES) { + assert( + twice.tasks.filter((task) => task.role === role).length === 1, + `${role} specialist routing must not duplicate an existing specialist task`, + ); + } +} + +function testSmallNonImplementationPlanStaysSmall() { + const plan: ProjectPlan = { + projectName: "Research Note", + repositoryName: "research-note", + productSummary: "Clarify a product idea without implementation.", + architectureSummary: "No application implementation is in scope.", + needsAuth: false, + technologyDecisions: [], + tasks: [ + { + id: "IDEA-001", + title: "Clarify scope", + role: "idea", + taskSlug: "clarify-scope", + summary: "Clarify the product scope.", + dependsOn: [], + acceptanceCriteria: ["scope is documented"], + }, + ], + }; + + const prepared = ensureSpecialistAgentPlan(plan); + assert(prepared.tasks.length === 1, "specialists must not be forced into a non-implementation plan without relevant signals"); +} + +function testMarketingTaskBudgetIsProtected() { + const tasks: ProjectTaskPlan[] = Array.from({ length: 35 }, (_, index) => ({ + id: `IDEA-${String(index + 1).padStart(3, "0")}`, + title: `Task ${index + 1}`, + role: "idea", + taskSlug: `task-${index + 1}`, + summary: index === 0 ? "database security deployment accessibility frontend backend" : "planning task", + dependsOn: [], + acceptanceCriteria: ["complete"], + })); + const plan: ProjectPlan = { + ...fullStackPlan(), + tasks, + }; + + const prepared = ensureSpecialistAgentPlan(plan); + assert(prepared.tasks.length === 35, "specialist routing must preserve the mandatory marketing task budget"); +} + +function run() { + testFullStackSpecialists(); + testSpecialistRoutingIsIdempotent(); + testSmallNonImplementationPlanStaysSmall(); + testMarketingTaskBudgetIsProtected(); + console.log("specialistPlanning policy tests passed"); +} + +run(); From 0a701f015807053dc2387d24a3bf10aab5fb0fb4 Mon Sep 17 00:00:00 2001 From: sunwoo162 Date: Thu, 27 Aug 2026 11:43:36 +0900 Subject: [PATCH 07/19] test: cover expanded Bloom team roster --- bloom-runtime/ts/catalog.policy-test.ts | 31 +++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 bloom-runtime/ts/catalog.policy-test.ts diff --git a/bloom-runtime/ts/catalog.policy-test.ts b/bloom-runtime/ts/catalog.policy-test.ts new file mode 100644 index 00000000..6d2044d1 --- /dev/null +++ b/bloom-runtime/ts/catalog.policy-test.ts @@ -0,0 +1,31 @@ +import { createInitialProjectTeamsState } from "./catalog"; +import { SPECIALIST_AGENT_ROLES } from "./specialistPlanning"; + +function assert(condition: unknown, message: string): asserts condition { + if (!condition) throw new Error(message); +} + +function testEveryTeamHasExpandedRoster() { + const state = createInitialProjectTeamsState(); + + assert(state.teams.length === 5, "Bloom must keep the five peer delivery teams"); + for (const team of state.teams) { + assert(team.agents.length === 19, `${team.id} must contain 19 Agents after specialist expansion`); + assert(new Set(team.agents.map((agent) => agent.id)).size === 19, `${team.id} Agent IDs must remain unique`); + + for (const role of SPECIALIST_AGENT_ROLES) { + const agent = team.agents.find((candidate) => candidate.role === role); + assert(agent, `${team.id} must include a ${role} specialist 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(); From 6f7cec83a64b9d5db5e4aa9a17e9e8b153db41f3 Mon Sep 17 00:00:00 2001 From: sunwoo162 Date: Thu, 27 Aug 2026 11:43:47 +0900 Subject: [PATCH 08/19] test: include Bloom specialist policies --- bloom-runtime/tsconfig.policy-tests.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bloom-runtime/tsconfig.policy-tests.json b/bloom-runtime/tsconfig.policy-tests.json index b0b1192a..71c23fc1 100644 --- a/bloom-runtime/tsconfig.policy-tests.json +++ b/bloom-runtime/tsconfig.policy-tests.json @@ -10,6 +10,10 @@ }, "include": [ "ts/types.ts", + "ts/policies.ts", + "ts/permissions.ts", + "ts/catalog.ts", + "ts/catalog.policy-test.ts", "ts/teamAllocation.ts", "ts/teamAllocation.policy-test.ts", "ts/intakeClarification.ts", @@ -18,6 +22,8 @@ "ts/dataMarketing.policy-test.ts", "ts/seniorAgent.ts", "ts/seniorAgent.policy-test.ts", + "ts/specialistPlanning.ts", + "ts/specialistPlanning.policy-test.ts", "ts/planTopology.ts", "ts/e2eSmoke.ts", "ts/e2eSmoke.policy-test.ts", From 5e0b3fbd398b742ae4d2f8d7d112fbe8b39b8496 Mon Sep 17 00:00:00 2001 From: sunwoo162 Date: Thu, 27 Aug 2026 11:47:19 +0900 Subject: [PATCH 09/19] feat: add implementation accelerator agent roles --- bloom-runtime/ts/types.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/bloom-runtime/ts/types.ts b/bloom-runtime/ts/types.ts index f208fa9d..91227af7 100644 --- a/bloom-runtime/ts/types.ts +++ b/bloom-runtime/ts/types.ts @@ -8,7 +8,15 @@ export type AgentRole = | "design-system" | "designer" | "frontend" + | "frontend-ui" + | "frontend-state" | "backend" + | "backend-api" + | "backend-domain" + | "integration" + | "test-automation" + | "performance" + | "observability" | "database" | "security" | "devops" From bd18428342999b95292222b9f5abb27f1df6d7ed Mon Sep 17 00:00:00 2001 From: sunwoo162 Date: Thu, 27 Aug 2026 11:47:51 +0900 Subject: [PATCH 10/19] feat: expand Bloom implementation team to 27 agents --- bloom-runtime/ts/catalog.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/bloom-runtime/ts/catalog.ts b/bloom-runtime/ts/catalog.ts index 1e8f7f3f..c9793221 100644 --- a/bloom-runtime/ts/catalog.ts +++ b/bloom-runtime/ts/catalog.ts @@ -16,7 +16,7 @@ export const BOUQUET_AUTH_POLICY = { export const EXECUTION_POLICY = { id: "iseol-workflow" as const, - version: "1.5.0", + version: "1.6.0", summary: "모든 Agent는 이설 작업 방식처럼 실제 저장소를 기준으로 독립적으로 작업하며 10년 이상 시니어 수준의 품질 기준을 적용합니다.", rules: [ "모든 Agent는 자신의 전문 역할에서 최소 10년 이상 실무를 수행한 시니어 수준의 판단, 품질 기준, 리스크 감각을 적용합니다.", @@ -27,6 +27,7 @@ export const EXECUTION_POLICY = { "검증하지 못한 항목은 성공했다고 기록하지 않고 정확한 blocker를 남깁니다.", "커밋은 작은 작업 단위로 나누고 영어 커밋 메시지를 사용합니다.", "작업한 Agent가 직접 branch push와 PR 생성/업데이트를 수행합니다.", + "PM은 큰 Frontend/Backend 일을 UI, 상태·데이터, API, 도메인, 외부 연동, 테스트 자동화, 성능, 관측 가능성처럼 독립 가능한 구현 경계로 분리해 여러 Agent가 병렬로 처리할 수 있게 계획합니다.", "Database, Security, DevOps, Accessibility 전문 Agent는 관련 위험이나 구현 범위가 있을 때 별도 Task와 PR로 독립 검증합니다.", "Code Review Agent가 코드 품질을 독립 검토하고 Reviewer Agent가 기능/요구사항/구조를 별도로 검토합니다.", "QA Agent는 실제 build/test/사용 흐름을 검증하고 Documentation Agent는 검증된 사실을 기준으로 사용자·개발·운영·마케팅 문서를 맞춥니다.", @@ -42,7 +43,7 @@ export const WORKFLOW_STAGES = [ "Planning", "Design System", "Designer", - "Frontend / Backend", + "Frontend / Backend Implementation Swarm", "Database / Security / DevOps / Accessibility", "Code Review", "Reviewer", @@ -64,8 +65,16 @@ const agentCatalog: Array<{ { role: "pm", label: "PM Agent", description: "요구사항, Task, Agent 실행 순서를 관리" }, { role: "design-system", label: "Design System Agent", description: "Figma와 실제 제품 근거로 디자인 규칙을 결정" }, { role: "designer", label: "Designer Agent", description: "제품 화면과 상호작용을 설계" }, - { role: "frontend", label: "Frontend Agent", description: "실제 프론트엔드 저장소를 구현" }, - { role: "backend", label: "Backend Agent", description: "API, 서버 도메인과 애플리케이션 로직을 구현" }, + { role: "frontend", label: "Frontend Agent", description: "프론트엔드 전체 구조와 최종 통합을 구현" }, + { role: "frontend-ui", label: "Frontend UI Agent", description: "페이지, 컴포넌트, 레이아웃, 스타일과 사용자 인터랙션 구현" }, + { role: "frontend-state", label: "Frontend State & Data Agent", description: "상태 관리, 폼, 데이터 패칭, 캐시, 클라이언트 데이터 흐름 구현" }, + { role: "backend", label: "Backend Agent", description: "백엔드 전체 구조와 서버 통합을 구현" }, + { role: "backend-api", label: "API Agent", description: "HTTP/RPC endpoint, contract, validation, controller와 transport 계층 구현" }, + { role: "backend-domain", label: "Domain Logic Agent", description: "서비스, 유스케이스, 비즈니스 규칙과 도메인 로직 구현" }, + { role: "integration", label: "Integration Agent", description: "외부 API, webhook, realtime, third-party adapter와 내부 서비스 연결 구현" }, + { role: "test-automation", label: "Test Automation Agent", description: "unit, integration, E2E 자동화 테스트와 fixture/harness 구현" }, + { role: "performance", label: "Performance Agent", description: "렌더링, API, 쿼리, 캐시, 번들 및 병목 구간을 측정하고 개선" }, + { role: "observability", label: "Observability Agent", description: "로그, health check, metric, tracing, 오류 진단과 운영 가시성 구현" }, { role: "database", label: "Database Agent", description: "스키마, 마이그레이션, 쿼리, 트랜잭션, 데이터 무결성과 저장 성능을 구현·검증" }, { role: "security", label: "Security Agent", description: "인증·인가, 입력 검증, secret, 의존성, 민감정보와 공격 표면을 독립 검증" }, { role: "devops", label: "DevOps Agent", description: "CI/CD, 실행 환경, 배포 설정, 컨테이너, 관측 가능성과 재현 가능한 운영 경로를 구현·검증" }, From 82886b7da611430fe13bc606db928496966c4550 Mon Sep 17 00:00:00 2001 From: sunwoo162 Date: Thu, 27 Aug 2026 11:48:13 +0900 Subject: [PATCH 11/19] feat: classify implementation swarm roles --- bloom-runtime/ts/orchestrationCore.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bloom-runtime/ts/orchestrationCore.ts b/bloom-runtime/ts/orchestrationCore.ts index dd966346..53adf962 100644 --- a/bloom-runtime/ts/orchestrationCore.ts +++ b/bloom-runtime/ts/orchestrationCore.ts @@ -121,7 +121,7 @@ export function projectStatusForActiveRoles( { roles: ["user-a", "user-b"], status: "user-test" }, { roles: ["qa"], status: "qa" }, { roles: ["code-review", "reviewer", "documentation", "data-marketing"], status: "review" }, - { roles: ["frontend", "backend", "database", "security", "devops", "accessibility", "debug-router"], status: "development" }, + { roles: ["frontend", "frontend-ui", "frontend-state", "backend", "backend-api", "backend-domain", "integration", "test-automation", "performance", "observability", "database", "security", "devops", "accessibility", "debug-router"], status: "development" }, { roles: ["design-system", "designer"], status: "design" }, { roles: ["idea"], status: "planning" }, ]; From 0897dc8dc3f177f9f05308fb7cfabb271bab3c1e Mon Sep 17 00:00:00 2001 From: sunwoo162 Date: Thu, 27 Aug 2026 11:48:32 +0900 Subject: [PATCH 12/19] feat: include implementation swarm in review topology --- bloom-runtime/ts/planTopology.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/bloom-runtime/ts/planTopology.ts b/bloom-runtime/ts/planTopology.ts index 9b135aa0..a3ea320d 100644 --- a/bloom-runtime/ts/planTopology.ts +++ b/bloom-runtime/ts/planTopology.ts @@ -5,7 +5,15 @@ export const REPOSITORY_WRITER_ROLES: Array> = [ "design-system", "designer", "frontend", + "frontend-ui", + "frontend-state", "backend", + "backend-api", + "backend-domain", + "integration", + "test-automation", + "performance", + "observability", "database", "security", "devops", From ebf4b5ff65f3dede5d973348051f94b55b565a0e Mon Sep 17 00:00:00 2001 From: sunwoo162 Date: Thu, 27 Aug 2026 11:49:00 +0900 Subject: [PATCH 13/19] feat: align specialist routing with implementation swarm --- bloom-runtime/ts/specialistPlanning.ts | 33 ++++++++++++++++++++------ 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/bloom-runtime/ts/specialistPlanning.ts b/bloom-runtime/ts/specialistPlanning.ts index 56c49168..a6d186a5 100644 --- a/bloom-runtime/ts/specialistPlanning.ts +++ b/bloom-runtime/ts/specialistPlanning.ts @@ -68,6 +68,25 @@ function dependenciesForRoles(tasks: ProjectTaskPlan[], roles: ExecutableAgentRo return roleTaskIds.length > 0 ? roleTaskIds : terminalTaskIds(tasks); } +const frontendImplementationRoles: ExecutableAgentRole[] = [ + "frontend", + "frontend-ui", + "frontend-state", +]; +const backendImplementationRoles: ExecutableAgentRole[] = [ + "backend", + "backend-api", + "backend-domain", + "integration", +]; +const allImplementationRoles: ExecutableAgentRole[] = [ + ...frontendImplementationRoles, + ...backendImplementationRoles, + "test-automation", + "performance", + "observability", +]; + const definitions: SpecialistDefinition[] = [ { role: "database", @@ -81,9 +100,9 @@ const definitions: SpecialistDefinition[] = [ "핵심 읽기/쓰기 경로의 트랜잭션 경계, 무결성, 오류 처리가 검증된다.", "쿼리 또는 저장 계층 변경에 가능한 자동 테스트나 실행 evidence가 남는다.", ], - shouldActivate: (plan, text) => hasAnyRole(plan, ["backend"]) + shouldActivate: (plan, text) => hasAnyRole(plan, backendImplementationRoles) || includesAny(text, ["database", " db ", "sql", "postgres", "mysql", "sqlite", "schema", "migration", "persistence", "storage"]), - dependencyRoles: ["backend"], + dependencyRoles: backendImplementationRoles, }, { role: "security", @@ -99,7 +118,7 @@ const definitions: SpecialistDefinition[] = [ ], shouldActivate: (plan, text) => plan.needsAuth || includesAny(text, ["auth", "login", "permission", "security", "secret", "token", "payment", "admin", "sensitive"]), - dependencyRoles: ["frontend", "backend", "database"], + dependencyRoles: [...frontendImplementationRoles, ...backendImplementationRoles, "database"], }, { role: "accessibility", @@ -113,9 +132,9 @@ const definitions: SpecialistDefinition[] = [ "폼 상태, 오류, 로딩 등 중요한 상태가 시각 정보에만 의존하지 않는다.", "가능한 접근성 검사 또는 재현 가능한 수동 검증 결과가 evidence로 남는다.", ], - shouldActivate: (plan, text) => hasAnyRole(plan, ["design-system", "designer", "frontend"]) + shouldActivate: (plan, text) => hasAnyRole(plan, ["design-system", "designer", ...frontendImplementationRoles]) || includesAny(text, ["accessibility", "accessible", "a11y", "screen reader", "keyboard", "responsive ui"]), - dependencyRoles: ["design-system", "designer", "frontend"], + dependencyRoles: ["design-system", "designer", ...frontendImplementationRoles], }, { role: "devops", @@ -129,9 +148,9 @@ const definitions: SpecialistDefinition[] = [ "CI 또는 동등한 자동 검증 경로가 현재 프로젝트의 핵심 build/test를 실행한다.", "배포 또는 운영 경로에서 확인하지 못한 항목은 성공으로 표시하지 않고 blocker/evidence를 남긴다.", ], - shouldActivate: (plan, text) => hasAnyRole(plan, ["frontend", "backend"]) + shouldActivate: (plan, text) => hasAnyRole(plan, allImplementationRoles) || includesAny(text, ["deploy", "deployment", "ci/cd", "docker", "container", "infrastructure", "production", "runtime", "hosting"]), - dependencyRoles: ["frontend", "backend", "database", "security"], + dependencyRoles: [...allImplementationRoles, "database", "security"], }, ]; From e4d43778e5e1f8dcd90908a9774aa626667711b7 Mon Sep 17 00:00:00 2001 From: sunwoo162 Date: Thu, 27 Aug 2026 11:50:58 +0900 Subject: [PATCH 14/19] feat: let PM assign implementation swarm roles --- bloom-runtime/src/project_runtime.rs | 38 +++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/bloom-runtime/src/project_runtime.rs b/bloom-runtime/src/project_runtime.rs index 253ff80f..49660a19 100644 --- a/bloom-runtime/src/project_runtime.rs +++ b/bloom-runtime/src/project_runtime.rs @@ -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", @@ -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", @@ -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. @@ -666,7 +690,13 @@ Operating contract: - Branch convention is agent///. 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. @@ -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) From 333dca83b7c7c2bceecbe2083d64f5c3c50234f1 Mon Sep 17 00:00:00 2001 From: sunwoo162 Date: Thu, 27 Aug 2026 11:51:16 +0900 Subject: [PATCH 15/19] test: cover 27-agent Bloom teams --- bloom-runtime/ts/catalog.policy-test.ts | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/bloom-runtime/ts/catalog.policy-test.ts b/bloom-runtime/ts/catalog.policy-test.ts index 6d2044d1..ad227d22 100644 --- a/bloom-runtime/ts/catalog.policy-test.ts +++ b/bloom-runtime/ts/catalog.policy-test.ts @@ -1,5 +1,17 @@ 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); @@ -7,15 +19,16 @@ function assert(condition: unknown, message: string): asserts condition { 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 === 19, `${team.id} must contain 19 Agents after specialist expansion`); - assert(new Set(team.agents.map((agent) => agent.id)).size === 19, `${team.id} Agent IDs must remain unique`); + 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 SPECIALIST_AGENT_ROLES) { + for (const role of expandedRoles) { const agent = team.agents.find((candidate) => candidate.role === role); - assert(agent, `${team.id} must include a ${role} specialist Agent`); + 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`); From 152145c3959621ba65126ecd7dad344e6824dabc Mon Sep 17 00:00:00 2001 From: sunwoo162 Date: Thu, 27 Aug 2026 11:53:15 +0900 Subject: [PATCH 16/19] test: fix specialist task narrowing --- bloom-runtime/ts/specialistPlanning.policy-test.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/bloom-runtime/ts/specialistPlanning.policy-test.ts b/bloom-runtime/ts/specialistPlanning.policy-test.ts index e20a1ced..8a69481c 100644 --- a/bloom-runtime/ts/specialistPlanning.policy-test.ts +++ b/bloom-runtime/ts/specialistPlanning.policy-test.ts @@ -52,10 +52,14 @@ function testFullStackSpecialists() { const accessibility = prepared.tasks.find((task) => task.role === "accessibility"); const devops = prepared.tasks.find((task) => task.role === "devops"); - assert(database?.dependsOn.includes("BE-001"), "Database Agent must wait for backend implementation evidence"); - assert(security?.dependsOn.includes(database.id), "Security Agent must inspect the database specialist result when present"); - assert(accessibility?.dependsOn.includes("FE-001"), "Accessibility Agent must inspect frontend implementation evidence"); - assert(devops?.dependsOn.includes(security.id), "DevOps Agent must consume security hardening evidence when present"); + assert(database, "Database Agent task must exist"); + assert(security, "Security Agent task must exist"); + assert(accessibility, "Accessibility Agent task must exist"); + assert(devops, "DevOps Agent task must exist"); + assert(database.dependsOn.includes("BE-001"), "Database Agent must wait for backend implementation evidence"); + assert(security.dependsOn.includes(database.id), "Security Agent must inspect the database specialist result when present"); + assert(accessibility.dependsOn.includes("FE-001"), "Accessibility Agent must inspect frontend implementation evidence"); + assert(devops.dependsOn.includes(security.id), "DevOps Agent must consume security hardening evidence when present"); } function testSpecialistRoutingIsIdempotent() { From 6ad1cee6ed01bde983a641980078ca94fdf26090 Mon Sep 17 00:00:00 2001 From: sunwoo162 Date: Thu, 27 Aug 2026 11:55:14 +0900 Subject: [PATCH 17/19] feat: support implementation swarm in replanning --- bloom-runtime/src/replan_runtime.rs | 49 ++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/bloom-runtime/src/replan_runtime.rs b/bloom-runtime/src/replan_runtime.rs index 7c2ff4cd..1669c134 100644 --- a/bloom-runtime/src/replan_runtime.rs +++ b/bloom-runtime/src/replan_runtime.rs @@ -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", @@ -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", ]; @@ -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", @@ -510,7 +547,7 @@ fn prompt(input: &ReplanProjectInput) -> Result { 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} @@ -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. From 71ae52ffb8bfa8c25eb298a0e927498173dcc20d Mon Sep 17 00:00:00 2001 From: sunwoo162 Date: Thu, 27 Aug 2026 11:58:03 +0900 Subject: [PATCH 18/19] feat: support implementation swarm in agent runtime --- bloom-runtime/src/agent_runtime.rs | 36 +++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/bloom-runtime/src/agent_runtime.rs b/bloom-runtime/src/agent_runtime.rs index 4daafccc..98f7b4b3 100644 --- a/bloom-runtime/src/agent_runtime.rs +++ b/bloom-runtime/src/agent_runtime.rs @@ -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", @@ -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" @@ -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, @@ -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": {} @@ -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" } }), )?; @@ -832,4 +856,4 @@ pub async fn dispatch_agent_task(input: AgentTaskRuntimeInput) -> Result Date: Thu, 27 Aug 2026 11:59:10 +0900 Subject: [PATCH 19/19] feat: reconcile implementation swarm writers --- bloom-runtime/src/agent_reconciliation.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/bloom-runtime/src/agent_reconciliation.rs b/bloom-runtime/src/agent_reconciliation.rs index c84cba6b..8aac305b 100644 --- a/bloom-runtime/src/agent_reconciliation.rs +++ b/bloom-runtime/src/agent_reconciliation.rs @@ -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",