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 step | Today |
|---|---|
| PRD approved, milestones/tickets planned | Fully 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 Engineer | Missing. 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 review | Missing. 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 approval | Half. 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 PO | Mostly 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:
- Generalize, don't specialize. The refinement loop in FR-19 is already generic - nothing in
revise_plan/compute_plan_deltaknows 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. - 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)
- A validated ticket type taxonomy (
type:*labels) so a ticket's nature is machine-readable. - A role-aware collaborator roster (name + role), replacing the flat engineer list while staying backward compatible.
- 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.
- Assignment label namespace. Does a designer get
eng:<name>or a role-prefixed label? Recommendation: keep the singleeng:<name>namespace for all roles, documented as "the assigned collaborator", with the role carried on the roster and the ticket'stype:*. Renaming toassignee:<name>means a label migration on live repos for no behavioral gain; the reconciler's capacity and_WORKINGlogic stay untouched. Revisit only if the naming causes real confusion. - 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").
- 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. - Unroutable tickets. A
type:designticket with no designer on the roster: fall back to an engineer, or stall visibly? Recommendation: leave itstatus:readyand surface it in the digest/notification. Silently misrouting design work to an engineer is worse than a visible, explainable stall. - 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.
- Two reconcilers.
LangGraphCoordinatorsubclassesCoordinator. Recommendation: implement all routing inCoordinatorso the subclass inherits it, and extend the existing parity test matrix to cover role routing. Any divergence here is a silent correctness bug. - 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 theSKILL.mdconvention so public skills can be vendored in beside proprietary ones; bind by declaring baseline skills in code (an agent is never silently skill-less) withBLOOM_AGENT_SKILLSable 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.
LangGraphCoordinatorsubclassesCoordinator; 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-clientinteracting with M5. A ticket parked on the owner must not be flagged stalled, escalated toneeds-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 setBLOOM_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_keyfence 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.pykeeps 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.pyandskills/both hold persona-ish text they will drift. Hold the boundary:prompts.pyowns 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.