orchestrata_ultimate.agents.volks.ux.volk

Reference › orchestrata_ultimate › agents › volks › ux › volk

UXVolk (alias U:Ux, role UX Designer) produces UI design artifacts when a project has a user interface (specification 013). It extends BaseAgent (base) and is imported by the kernel pipeline; UserExperienceAgent is an alias subclass.

What it receives: a ux_design packet from the Project Manager (volk) with the requirements, task summary, project name, scope data and a has_ui flag. What it produces: a packet for the Architect (U:Ar).

process routes to _handle_ux_design, which reads has_ui. Without a UI, _passthrough forwards the request directly to the Architect unchanged in substance, with subject implement. With a UI, _design_ui calls the LLM to produce the design artifacts (workflows, component hierarchy, wireframes, design tokens and accessibility notes) and _build_design_packet sends the result to U:Ar with subject ux_complete, in a ux_design payload field next to the original requirements. The Architect accepts both subjects.

At a glance

Source src/orchestrata_ultimate/agents/volks/ux/volk.py
Lines of code 239
Classes 2
Functions 0
Imports (internal) orchestrata_ultimate.agents.volks.base, orchestrata_ultimate.core.base.traces, orchestrata_ultimate.core.graph_directives, orchestrata_ultimate.core.packet
Imported by orchestrata_ultimate.kernel.pipeline
Tests that import it tests/agents/test_ux_volk.py, tests/agents/volks/ux/test_ux_handoff.py, tests/agents/volks/ux/test_volk.py, tests/generated_agents/test_volks_ux_volk_generated.py, tests/generated_recovery/test_agents_volks_ux_volk.py

Class diagram

Class diagram orchestrata_ultimate.agents.volks.ux.volk

Dependency diagram

Dependency diagram orchestrata_ultimate.agents.volks.ux.volk

API

Classes

UXVolk (pydantic)

Bases: BaseAgent · Decorators: traces(spec='013', story='US-001', reqs=['REQ-013-001', 'REQ-013-002']) · Line 79

UX Designer volk — produces UI design artifacts.

Attribute Type Default
alias str Field(default='U:Ux')
role str 'UX Designer'
Method Signature Summary
__init__ __init__(****kwargs)
process async process(packet: CXPPacket) -> CXPPacket | None Route packet to appropriate handler.
_handle_ux_design async _handle_ux_design(packet: CXPPacket) -> CXPPacket Produce UI design or passthrough if not needed.
_passthrough _passthrough(packet: CXPPacket) -> CXPPacket No UI needed — forward directly to Architect.
_design_ui async _design_ui(packet: CXPPacket) -> CXPPacket Call LLM to produce UI design artifacts.
_build_design_packet _build_design_packet(packet: CXPPacket, requirements: str) -> CXPPacket Build CXP packet forwarding UX design to Architect.

UserExperienceAgent (pydantic)

Bases: UXVolk · Line 236

Agent alias for UXVolk.

Source

Permalink: https://gitlab.com/MSamy/orchestrata-ultimate-architecture/-/blob/a4cbc2bdf/src/orchestrata_ultimate/agents/volks/ux/volk.py

# mypy: ignore-errors
"""U:Ux — UX Designer volk for UI design artifacts."""

from pydantic import Field

from orchestrata_ultimate.agents.volks.base import BaseAgent
from orchestrata_ultimate.core.base.traces import traces
from orchestrata_ultimate.core.graph_directives import extract_graph_context
from orchestrata_ultimate.core.packet import CXPPacket, WorkStatus

UX_SYSTEM_PROMPT = """\
You are U:Ux, the UX Designer — a senior interaction designer specializing \
in producing actionable UI design artifacts for developer implementation.

YOUR MISSION: Given project requirements that include a UI, produce a \
complete design specification that a frontend developer can implement \
without guessing layout, components, interactions, or visual style.

DESIGN METHODOLOGY (based on user-centered design research):
1. MAP USER WORKFLOWS: For each user story, trace the screen flow \
   (entry -> steps -> decision points -> exit). Identify data shown \
   and collected at each step.
2. BUILD COMPONENT HIERARCHY: Decompose each screen into reusable \
   components (page -> layout -> section -> widget). Identify shared \
   components (buttons, forms, modals, tables).
3. DRAW WIREFRAMES: ASCII layout for each key screen showing header, \
   nav, main content (with data source annotations), sidebar, footer. \
   Note responsive breakpoints.
4. DEFINE DESIGN TOKENS: Colors (primary, secondary, accent, bg, text, \
   error), typography (font, sizes h1-h6/body/caption), spacing scale \
   (4/8/16/24/32px), border-radius, shadows.
5. SPECIFY ACCESSIBILITY: WCAG 2.1 AA, keyboard nav, ARIA labels, \
   contrast ratios (4.5:1 minimum), focus indicators.

HANDOFF: The UX_DESIGN block below is handed to U:Ar (the Architect) \
and embedded into the Architect prompt, so keep every section label intact.

Output EXACTLY in this format:
UX_DESIGN:

WORKFLOWS:
- name: <workflow name>
  screens: [<screen1>, <screen2>]
  transitions: [{from: <screen>, to: <screen>, trigger: <action>}]

COMPONENTS:
- name: <ComponentName>
  type: page | layout | widget
  props: [{name: str, type: str, required: bool}]
  children: [<child components>]

WIREFRAMES:
### <ScreenName>
(ASCII wireframe with data source annotations)

DESIGN_TOKENS:
- colors: {primary: "#...", secondary: "#...", accent: "#...", \
  background: "#...", text: "#...", error: "#..."}
- typography: {font: "...", sizes: {h1: "...", body: "..."}}
- spacing: [4, 8, 16, 24, 32]

ACCESSIBILITY:
- WCAG level: AA
- keyboard_nav: required
- aria_labels: required
- contrast_ratio: 4.5:1 minimum
- focus_indicators: visible
"""


@traces(
    spec="013",
    story="US-001",
    reqs=[
        "REQ-013-001",  # UXVolk alias is "U:Ux"
        "REQ-013-002",  # UXVolk role is "UX Designer"
    ],
)
class UXVolk(BaseAgent):
    """UX Designer volk — produces UI design artifacts."""

    alias: str = Field(default="U:Ux")
    role: str = "UX Designer"

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.escalation_target = "U:Ar"
        self.ux_design = ""
        self.task_summary = ""

    @traces(
        spec="013",
        story="US-001",
        reqs=[
            "REQ-013-003",  # routes CXP packets with subject "ux_design"
            "REQ-013-004",
            "REQ-013-030",  # returns None for any subject other than "ux_design"
        ],
    )
    async def process(self, packet: CXPPacket) -> CXPPacket | None:
        """Route packet to appropriate handler."""
        self._graph_context = extract_graph_context(packet.payload)
        if packet.subject == "ux_design":
            return await self._handle_ux_design(packet)
        return None

    @traces(
        spec="013",
        story="US-002",
        reqs=[
            "REQ-013-005",  # calls _passthrough when has_ui is False or absent
            "REQ-013-015",  # calls _design_ui when has_ui is True
        ],
    )
    async def _handle_ux_design(self, packet: CXPPacket) -> CXPPacket:
        """Produce UI design or passthrough if not needed."""
        has_ui = packet.payload.get("has_ui", False)
        self.current_thread = packet.thread_id
        self.task_summary = packet.payload.get("task_summary", "Implement as specified")

        if not has_ui:
            return self._passthrough(packet)
        return await self._design_ui(packet)

    @traces(
        spec="013",
        story="US-002",
        reqs=[
            "REQ-013-006",  # constructs a CXP addressed to U:Ar with subject "implement"
            "REQ-013-007",  # sets WorkStatus.PENDING on the outbound CXP packet
            "REQ-013-008",  # forwards requirements from the inbound payload unchanged
            "REQ-013-009",  # forwards original_intent from the inbound payload unchanged
            "REQ-013-010",  # forwards task_summary from the inbound payload unchanged
            "REQ-013-011",  # forwards project_name from the inbound payload unchanged
            "REQ-013-012",  # forwards prolog_facts from the inbound payload unchanged
            "REQ-013-013",  # forwards scope_data from the inbound payload unchanged
            "REQ-013-014",  # does not inject any UX-specific keys when has_ui is False
        ],
    )
    def _passthrough(self, packet: CXPPacket) -> CXPPacket:
        """No UI needed — forward directly to Architect."""
        self.status = WorkStatus.RESOLVED
        self.summary = "No UI — passthrough to Architect"
        print("  [U:Ux] No UI required — passing through", flush=True)

        return CXPPacket(
            from_agent="U:Ux",
            to_agent="U:Ar",
            thread_id=packet.thread_id,
            subject="implement",
            payload={
                "requirements": packet.payload.get("requirements", ""),
                "original_intent": packet.payload.get("original_intent", ""),
                "task_summary": self.task_summary,
                "project_name": packet.payload.get("project_name"),
                "prolog_facts": packet.payload.get("prolog_facts", []),
                "scope_data": packet.payload.get("scope_data", {}),
            },
            work_status=WorkStatus.PENDING,
        )

    @traces(
        spec="013",
        story="US-003",
        reqs=[
            "REQ-013-016",  # calls call_llm with UX_SYSTEM_PROMPT
            "REQ-013-017",  # calls call_llm with graph_context
            "REQ-013-018",  # UX_SYSTEM_PROMPT contains step 1: MAP USER WORKFLOWS
            "REQ-013-019",  # UX_SYSTEM_PROMPT contains step 2: BUILD COMPONENT HIERARCHY
            "REQ-013-020",  # UX_SYSTEM_PROMPT contains step 3: DRAW WIREFRAMES
            "REQ-013-021",  # UX_SYSTEM_PROMPT contains step 4: DEFINE DESIGN TOKENS
            "REQ-013-022",  # UX_SYSTEM_PROMPT contains step 5: SPECIFY ACCESSIBILITY
            "REQ-013-023",  # stores the LLM output in a ux_design field on the outbound payload
            "REQ-013-024",  # LLM output begins with "UX_DESIGN:"
            "REQ-013-025",  # LLM output contains "WORKFLOWS:" section
            "REQ-013-026",  # LLM output contains "COMPONENTS:" section
            "REQ-013-027",  # LLM output contains "WIREFRAMES:" section
            "REQ-013-028",  # LLM output contains "DESIGN_TOKENS:" section
            "REQ-013-029",  # LLM output contains "ACCESSIBILITY:" section
        ],
    )
    async def _design_ui(self, packet: CXPPacket) -> CXPPacket:
        """Call LLM to produce UI design artifacts."""
        self.status = WorkStatus.IN_PROGRESS
        self.summary = "Designing UI"
        requirements = packet.payload.get("requirements", "")
        print(f"  [U:Ux] Designing UI for: {self.task_summary}", flush=True)

        prompt = (
            f"Requirements:\n{requirements}\n\n"
            f"Task: {self.task_summary}\n\n"
            "Produce a complete UX design specification."
        )
        response = await self._call_llm(
            UX_SYSTEM_PROMPT, prompt, temperature=0.5, graph_context=self._graph_context
        )
        self.ux_design = response
        self.output_artifacts.append(f"=== UX DESIGN ===\n{response}")

        print("  [U:Ux] UI design complete — forwarding to Architect", flush=True)
        self.status = WorkStatus.RESOLVED
        self.summary = "UI design complete"
        return self._build_design_packet(packet, requirements)

    @traces(
        spec="013",
        story="US-005",
        reqs=[
            "REQ-013-030",  # constructs a CXP addressed to U:Ar with subject "ux_complete" containing the ux_design field plus all preserved payload context fields
        ],
    )
    def _build_design_packet(self, packet: CXPPacket, requirements: str) -> CXPPacket:
        """Build CXP packet forwarding UX design to Architect."""
        return CXPPacket(
            from_agent="U:Ux",
            to_agent="U:Ar",
            thread_id=packet.thread_id,
            # REQ-013-030: the UX-design handoff subject is "ux_complete" (the
            # no-UI passthrough uses "implement" per REQ-013-006). Architect
            # accepts both.
            subject="ux_complete",
            payload={
                "requirements": requirements,
                "original_intent": packet.payload.get("original_intent", ""),
                "task_summary": self.task_summary,
                "project_name": packet.payload.get("project_name"),
                "prolog_facts": packet.payload.get("prolog_facts", []),
                "scope_data": packet.payload.get("scope_data", {}),
                "ux_design": self.ux_design,
            },
            work_status=WorkStatus.PENDING,
        )


# Class alias for orchestrata-ultimate naming
class UserExperienceAgent(UXVolk):
    """Agent alias for UXVolk."""

    pass