Skip to main content

Milestone 11 - The Design Stage: Role-Aware Coordination, Deliverable Review & the Bloom Designer

Status: delivered (v0.9.0). Delivers FR-23 (Design Studies) end to end, on top of two generic coordination capabilities that the design workflow needs but that are deliberately not design-specific. All twelve tickets (M11-1..M11-12) merged; the ticket sections below describe the delivered shape.

Theme: Bloom can currently plan work and delegate it to one flat pool of engineers, whose only reviewable deliverable is a pull request, which Bloom merges the moment its own review passes. M11 adds the three things a design stage actually requires - roles (so a UX ticket can go to a designer instead of an engineer), deliverable-kind review plus a client-approval gate (so a Figma/Blender study can be reviewed and then handed to the owner for the final say), and the Bloom Designer itself - and it adds the first two as general capabilities so every future role and deliverable type inherits them.


Why this milestone

The v0.9 PRD promises a design stage (FR-23), but an audit of the running system shows the SDLC graph supports roughly half of the intended flow:

Intended stepToday
PRD approved, milestones/tickets plannedFully supported
Some tickets are "about the UX"Ticket.labels is free-text LLM output, pushed to GitHub but inert - nothing classifies or routes on it
UX tickets delegated to a Designer, not an EngineerMissing. Coordinator(engineers, capacity) is a flat, role-less roster with one eng:<name> namespace; it delegates to the least-loaded engineer regardless of ticket content
Designer posts studies/assets on the issue and marks it for reviewMissing. No producer agent (M7 specialists are advisory reviewers, forbidden from setting eng:*), and review is PR-triggered only - an issue-comment deliverable has no review path
PO reviews, then hands to the client for final approvalHalf. The reject -> changes-requested -> rework -> escalate loop is solid (FR-12), but on approval Bloom merges and closes immediately; there is no client-approval state
Client accepts, or refines by conversing with the POMostly present via FR-19 (triage -> revise_plan -> compute_plan_delta -> approve -> reconcile), but it re-plans the whole plan rather than a targeted issue/deliverable

Two design principles fall out of that audit and shape this milestone:

  1. Generalize, don't specialize. The refinement loop in FR-19 is already generic - nothing in revise_plan/compute_plan_delta knows about design. Building a design-specific review and refinement path would create a second loop that drifts from the first within a milestone or two. Phases A and B below are therefore written as role- and deliverable-kind capabilities, with design as the first consumer.
  2. The signal exists; make it actionable. Tickets already carry labels to GitHub. The gap is that nothing validates or routes on them.

One-liner: The PO plans a "design the onboarding flow" ticket, routes it to the Bloom Designer rather than an engineer, the Designer posts comparable Figma studies on the issue, the PO reviews them against the ticket's acceptance criteria, and the owner picks one - or asks for changes in chat, which updates that specific issue.


Scope

In scope

Phase A - Role-aware coordination (generic)

  1. A validated ticket type taxonomy (type:* labels) so a ticket's nature is machine-readable.
  2. A role-aware collaborator roster (name + role), replacing the flat engineer list while staying backward compatible.
  3. Delegation routing by ticket type to the eligible role, least-loaded within that pool.

Phase B - Deliverable review & client approval (generic) 4. A non-PR deliverable review path: an issue marked for review is reviewed against its acceptance criteria, exactly like a PR is today. 5. A client-approval gate (status:awaiting-client) between Bloom's own approval and close/merge, for ticket types that are client-facing. 6. Targeted change requests: FR-19 refinement scoped to a specific milestone/issue/deliverable rather than always re-planning the whole plan.

Phase C - The Bloom Designer (FR-23) 7. An agent skill registry: versioned, composable knowledge skills wired into agents. 8. A Bloom Designer producer agent that generates comparable design studies. 9. Publishing studies to the issue (and a PR when the deliverable includes repo files). 10. Figma integration for UX studies, behind a seam, degrading gracefully. 11. Blender integration for 3D asset studies, behind the same seam, opt-in. 12. Docs / PRD reconcile / version bump.

Phases A and B are independently valuable and independently shippable - they benefit every future role (QA tester, technical writer) and every non-PR deliverable. If M11 runs long, cut Phase C into its own milestone rather than descoping A or B.

Out of scope (future / deferred)

  • The decision graph / institutional-memory concept - see docs/design/decision-graph.md. Related, much larger, separate.
  • Rendering the design stage as new workflow-spec nodes. M11 follows the existing precedent (M4/M5 implemented their loops in orchestrator methods, not the JSON graph). Whether to move the imperative half into the graph is the LangGraph question, decided separately.
  • Automated design-to-ticket handoff (studies seeding engineering tickets) - listed in the PRD as a future feature.
  • Design tokens / component-library generation, interactive prototypes, render farms.
  • Replacing the engineer swarm simulator; a "designer swarm" simulator is a test harness question, not product runtime.
  • An agentic tool loop / MCP. M11's skills are prompt-layer knowledge only (M11-7); agents stay pure structured() calls and all execution stays in capability adapters. Letting an agent call Figma/Blender itself breaks the one-shot provider contract, makes agents side-effecting and nondeterministic, and complicates the fake provider, the idempotency fence, and the capability policy. That is a real architectural expansion deserving its own milestone and its own flip-readiness treatment - not a side effect of adding a Designer.

Key decisions

Decisions 1-6 are locked - the recommendations below were reviewed and approved before implementation began. Decision 7 was added when the skill registry was folded into Phase C.

  1. Assignment label namespace. Does a designer get eng:<name> or a role-prefixed label? Recommendation: keep the single eng:<name> namespace for all roles, documented as "the assigned collaborator", with the role carried on the roster and the ticket's type:*. Renaming to assignee:<name> means a label migration on live repos for no behavioral gain; the reconciler's capacity and _WORKING logic stay untouched. Revisit only if the naming causes real confusion.
  2. Where the client-approval gate applies. All tickets, or only client-facing types? Recommendation: by ticket type, configurable, defaulting to design/content types only. Gating every engineering merge behind the owner would destroy delivery throughput and contradict FR-10 ("shield users from unnecessary engineering complexity").
  3. How a non-PR deliverable enters review. A label flip or a comment command? Recommendation: the collaborator flips the issue to status:in-review - it reuses the existing label contract rather than inventing a command surface, and it is the same signal the PR path already produces.
  4. Unroutable tickets. A type:design ticket with no designer on the roster: fall back to an engineer, or stall visibly? Recommendation: leave it status:ready and surface it in the digest/notification. Silently misrouting design work to an engineer is worse than a visible, explainable stall.
  5. Figma/Blender as hard dependencies? Recommendation: optional, behind a seam, degrading to text/markdown studies when unconfigured. The milestone must be shippable and testable without external accounts or a Blender install, exactly as the LLM provider seam is.
  6. Two reconcilers. LangGraphCoordinator subclasses Coordinator. Recommendation: implement all routing in Coordinator so the subclass inherits it, and extend the existing parity test matrix to cover role routing. Any divergence here is a silent correctness bug.
  7. What a "skill" is, and how it binds. Bloom's agents are one-shot structured() calls with no tool loop anywhere, so "teach the Designer Figma" splits into two different things. Recommendation: a knowledge skill is prompt-layer only - transferable domain knowledge composed into the system prompt - while the executable half stays a capability adapter (M11-10/M11-11) that the orchestrator invokes with the agent's structured output as its spec. The agent never touches Figma, so agents stay pure, outputs stay deterministic under the fake provider, and side effects stay where the capability policy can gate them. On disk, follow the SKILL.md convention so public skills can be vendored in beside proprietary ones; bind by declaring baseline skills in code (an agent is never silently skill-less) with BLOOM_AGENT_SKILLS able only to add.

Tickets

Ordered by dependency. Each is bite-sized and independently reviewable.

M11-1 - Ticket type taxonomy (type:*)

Give Ticket a validated kind (enum: engineering, design, ux, content, research, defaulting to engineering) and provision matching type:* GitHub labels idempotently alongside the existing status:*/eng:* definitions. Ticket generation classifies each ticket. Existing free-text Ticket.labels are preserved untouched (they remain topical, not structural).

AC: the model exposes a typed kind with an engineering default; ticket_generation sets it; type:* labels are provisioned idempotently and appear on created issues; a ticket with a missing or unknown kind falls back to engineering (so existing projects keep working); free-text labels still flow to GitHub; unit tests cover classification and the back-compat default.

M11-2 - Role-aware collaborator roster

Introduce a CollaboratorRole enum (engineer, designer, extensible) and a roster parsed from BLOOM_COLLABORATORS as <name>[:<role>] (e.g. ada,linus,nova:designer), mirroring the proven BLOOM_SPECIALIST_REVIEWERS parsing pattern - unknown roles raise at startup rather than silently doing nothing. BLOOM_ENGINEERS remains honored as an engineer-only alias so live deployments do not break. Capacity remains per collaborator.

AC: roster parses names and roles; a typo fails fast with an actionable message naming the variable; BLOOM_ENGINEERS still works unchanged (deprecated but supported); an empty roster still means "plan but do not delegate"; config unit tests cover both variables and the failure mode.

M11-3 - Route delegation by ticket type to the eligible role

Teach the reconciler to delegate a ready ticket only to collaborators whose role is eligible for that ticket's type (design/ux -> designer, everything else -> engineer, via an explicit mapping). Least-loaded selection and capacity apply within the eligible pool. A ticket with no eligible collaborator stays status:ready and is reported, never misrouted (decision 4). Implemented in Coordinator so LangGraphCoordinator inherits it (decision 6).

AC: a type:design ticket is never delegated to an engineer and vice versa; with no eligible collaborator the ticket stays ready and is surfaced in the reconcile result; a designer at capacity does not block engineering delegation (and vice versa); single-role rosters behave exactly as today; the LangGraphCoordinator parity test matrix is extended to cover role routing and passes.

M11-4 - Deliverable-kind review path (non-PR deliverables)

Add an issue-deliverable review path beside the PR path: when an assigned collaborator posts a deliverable and flips the issue to status:in-review without a PR, Bloom reviews the issue's deliverable (comment body plus linked artifacts) against the ticket's acceptance criteria and posts the verdict as an issue comment. Approve / request-changes drive the same status transitions and the same bounded review_max_rounds escalation as the PR path. Requires handling the issue_comment / issue-labeled webhook through the existing durable job queue and event_key fence.

AC: an issue moved to status:in-review with no PR is reviewed and gets a verdict comment; request-changes returns it to status:changes-requested for the same collaborator; the round cap still escalates to status:needs-attention; the PR path is unchanged; a redelivered webhook does not double-review (idempotency fence); integration test covers approve and request-changes.

M11-5 - Client-approval gate (status:awaiting-client)

Add a status:awaiting-client state between Bloom's approval and close/merge, applied to client-facing ticket types (decision 2). On approving such a ticket, Bloom does not merge/close: it moves the ticket to status:awaiting-client, notifies the owner with a concise summary and the deliverable links, and parks. Owner approval finalizes (merge/close); an owner change request returns the ticket to status:changes-requested with the note attached for the assigned collaborator.

AC: approving a gated ticket does not merge or close it; owner approval finalizes it; an owner change request routes back to the collaborator carrying the note; non-gated (engineering) tickets behave exactly as today; awaiting-client does not consume collaborator capacity and is not counted as stalled or escalated by the M5 scheduler (it is waiting on a human by design); the notification is de-duplicated; tests cover approve, change-request, and the scheduler interaction.

M11-6 - Targeted (scoped) change requests

Generalize FR-19 so a change request can target the whole plan (today's behavior), a milestone, an issue, or a specific deliverable. Triage resolves the target from the conversation; the plan delta is computed at that granularity. This is the generalization of the "refinement is not design-only" principle - it serves UX flows, user journeys, copy, and API contracts equally.

AC: "change the onboarding flow" resolves to the relevant issue and updates only it, not a global re-plan; whole-plan requests behave as before; the resolved target is stated in the proposal ("I'll update issue #12") so a mis-resolution is visible before approval; the destructive-impact approval gate still applies; ambiguous targets ask rather than guess.

M11-7 - Agent skill registry (composable knowledge skills)

Give agents versioned, composable knowledge skills - transferable domain knowledge such as how to structure a UX study, brand guidelines, Figma file conventions, or Blender asset naming - kept separate from an agent's own identity and task instructions in prompts.py. Skills are prompt-layer only: they shape what an agent produces and execute nothing. The executable half stays in the capability adapters (M11-10/M11-11), invoked by the orchestrator with the agent's structured output as its spec, so agents remain pure structured() calls and side effects remain where the capability policy can gate them (decision 7).

On-disk format follows the SKILL.md convention so publicly available skills can be vendored in beside proprietary ones:

apps/api/src/bloom/skills/<name>/SKILL.md
---
name: ux-design-studies
version: 1.2.0
description: How to produce comparable UX design studies with rationale and tradeoffs
applies_to: [designer] # collaborator roles from M11-2
requires: [figma] # capabilities, gated by a code-owned policy
---

Binding: each agent declares its baseline skills in code (so an agent is never silently skill-less); BLOOM_AGENT_SKILLS may only add optional ones per deployment, parsed and validated like the specialist roster.

AC: the registry loads and validates skills from disk, with a malformed or missing front-matter field failing fast and naming the file; skills compose into the system prompt in a deterministic order inside the stable prefix, with volatile per-request content strictly after it, and a test asserts the prompt prefix is byte-identical across two calls with the same skill set (protecting the prompt cache prompts.py deliberately preserves); baseline skills come from code and BLOOM_AGENT_SKILLS can only add, never remove; an unknown skill name fails fast naming the variable; a skill whose requires: capability is not granted to that agent is rejected and logged, never silently dropped; public skills are vendored into the repo with no runtime fetching, so the suite still runs fully offline; an agent configured with no skills behaves exactly as today.

M11-8 - Bloom Designer agent (design studies)

Add the Designer as a producer agent (distinct in shape from M7's advisory reviewers): given a design-type ticket plus PRD and project context, produce a structured set of comparable design studies - at least two options, each with rationale and tradeoffs. Like the specialist reviewers, the agent itself performs no GitHub side effects; the orchestrator publishes. The Designer is the first consumer of the skill registry (M11-7), declaring its baseline design skills in code, and its output is a design specification that the capability adapters execute.

AC: returns two or more comparable options with rationale and tradeoffs; output is a validated structured model; the Designer's baseline skills are declared in code and demonstrably reach its system prompt; deterministic under the fake provider (so tests and offline dev work); the agent performs no GitHub writes; unit tests cover the study-set shape, skill composition, and the offline path.

M11-9 - Publish design studies to the issue

The orchestrator publishes a study set as one structured issue comment (options, rationale, preview links, attached assets), then flips the issue to status:in-review so the M11-4 review path picks it up. When a deliverable includes repo files (exported assets, design tokens), it also opens a PR that closes the issue on merge.

AC: one structured comment per study round (not one per option); artifacts and preview links render correctly; the issue lands in status:in-review; a repo-file deliverable opens a PR wired to close the issue; republishing after a change request adds a new round without destroying the prior one (the issue is the audit trail); idempotent under redelivery.

M11-10 - Figma integration for UX studies

Behind a DesignSurface seam and a feature flag: create/read Figma frames for UX studies and return shareable preview links. Credentials come from the environment only. When unconfigured or failing, the Designer degrades to a text/markdown study with an explicit note - it never blocks the flow (decision 5).

AC: configured -> real Figma preview links on the study comment; unconfigured -> text-only studies with a visible note explaining why; credentials never logged; an API failure fails soft (the study is still published, the failure is logged); offline tests cover both paths without network access.

M11-11 - Blender integration for 3D asset studies

The same seam pattern for 3D asset studies: headless Blender generates/renders asset options, attached to the study comment. Opt-in and off by default; used only for projects that declare 3D assets. Long-running renders run through the durable job queue so they never block the workflow.

AC: opt-in via config, off by default; renders attach to the study comment; unconfigured or unavailable degrades to a described study; renders execute on the job queue with retry/dead-letter semantics; offline tests do not require a Blender install.

M11-12 - Docs, PRD reconcile & version bump

Reconcile FR-23 with the delivered shape (role routing, deliverable review, client gate), update the docs/coordination.md label table with type:* and status:awaiting-client, update architecture, the docs index, this milestone's status, and the CHANGELOG; bump the version.

AC: docs describe the delivered M11 (not the plan); the label contract table is complete; pyproject/__init__ and CHANGELOG bumped consistently; milestone doc status updated; index links resolve.


Risks / watch-list

  • Two reconcilers. LangGraphCoordinator subclasses Coordinator; routing must land in the base class and the parity matrix must be extended, or the two engines silently diverge on delegation. Highest-risk item in Phase A.
  • awaiting-client interacting with M5. A ticket parked on the owner must not be flagged stalled, escalated to needs-attention, or counted against a collaborator's capacity. Getting this wrong turns normal client latency into false escalations and blocked designers.
  • Capacity across roles. Per-collaborator capacity must not let a saturated design pool stall engineering delegation, or vice versa.
  • Back-compat on live projects. Existing tickets carry no type:* and existing deploys set BLOOM_ENGINEERS. Both must keep working untouched; default-to-engineering is the safety net.
  • Idempotency on a new webhook surface. The issue-comment/label review path is at-least-once like every other webhook; it must go through the durable queue and the event_key fence or a redelivery double-reviews and double-notifies.
  • Two review paths drifting. The PR path and the deliverable path must share the review verdict model, the round counting, and the escalation rule - implement the shared part once.
  • Public skills are untrusted instructions entering a system prompt. A vendored third-party skill body is a prompt-injection surface: it can tell the agent to do things nobody intended. Treat skill text as code - vendored into the repo, reviewed in a PR, version-pinned, never fetched at runtime - and let the capability policy, not the skill itself, decide what it may reach.
  • Skills breaking the prompt cache. prompts.py keeps prompts as constants specifically so the cached prefix stays stable. Composing skills at call time will shatter that prefix and cost real money and latency if the ordering is not deterministic and confined to the stable prefix.
  • Two competing prompt systems. If prompts.py and skills/ both hold persona-ish text they will drift. Hold the boundary: prompts.py owns agent identity and task instructions, skills own transferable domain knowledge that could apply to more than one agent.
  • Scope creep via Figma/Blender. These are the most externally-dependent, least-certain tickets and are deliberately last and optional. If they slip, Phases A and B still ship real value.
  • Label proliferation. status:* + eng:* + type:* is still legible; adding a fourth namespace later deserves resistance.