orchestrata_ultimate.agents.spec_writer

Reference › orchestrata_ultimate › agents › spec_writer

spec_writer is a standalone generator for specification documents of spike-generated projects: a specs/NNN-name/spec.md plus requirement checklists. It has no orchestrata imports and no importers in the reference. The inputs are dataclasses: SpecMetadata (id, title, alias, author, status, created date, priority, components), UserStorySpec (as-a, I-want, so-that, acceptance criteria, priority), RequirementSpec (with a verified flag), NFRequirement for non-functional requirements, and ArchitectureSection (overview, modules, patterns). The module turns these structured inputs into markdown text; the module's functions are listed in the API section below.

At a glance

Source src/orchestrata_ultimate/agents/spec_writer.py
Lines of code 446
Classes 6
Functions 11
Imports (internal) none
Imported by none
Tests that import it tests/agents/test_spec_writer.py, tests/agents/test_spec_writer_extended.py, tests/generated_agents/test_spec_writer_generated.py

Class diagram

Class diagram orchestrata_ultimate.agents.spec_writer

Dependency diagram

No internal imports and no importers.

API

Classes

SpecMetadata (dataclass)

Decorators: dataclass · Line 15

Specification metadata block.

Attribute Type Default
spec_id str
title str
alias str ''
author str 'Mohamed'
status str 'Draft'
created str ''
priority str 'P0'
components str ''
Method Signature Summary
__post_init__ __post_init__() -> None

UserStorySpec (dataclass)

Decorators: dataclass · Line 33

User story for spec document.

Attribute Type Default
id str
title str
as_a str ''
i_want str ''
so_that str ''
acceptance_criteria list[str] field(default_factory=list)
priority str 'Critical'

RequirementSpec (dataclass)

Decorators: dataclass · Line 46

Requirement for spec checklist.

Attribute Type Default
id str
description str
story_id str ''
category str ''
verified bool False

NFRequirement (dataclass)

Decorators: dataclass · Line 57

Non-functional requirement.

Attribute Type Default
id str
description str
category str ''
target str ''

ArchitectureSection (dataclass)

Decorators: dataclass · Line 67

Architecture description for spec.

Attribute Type Default
overview str ''
modules list[str] field(default_factory=list)
patterns list[str] field(default_factory=list)
technology_stack list[str] field(default_factory=list)
class_diagram_mmd str ''
module_diagram_mmd str ''

APIEndpoint (dataclass)

Decorators: dataclass · Line 79

API endpoint or public interface.

Attribute Type Default
name str
signature str
description str ''
returns str ''

Functions

Function Signature Summary
generate_spec_document generate_spec_document(metadata: SpecMetadata, overview_what: str, overview_why: list[str], overview_how: list[str], user_stories: list[UserStorySpec], nfrs: list[NFRequirement] | None = None, architecture: ArchitectureSection | None = None, api_endpoints: list[APIEndpoint] | None = None, dependenc… Generate a complete spec.md document.
_render_header _render_header(meta: SpecMetadata) -> list[str] Render spec header with metadata.
_render_overview _render_overview(what: str, why: list[str], how: list[str]) -> list[str] Render overview section.
_render_user_stories _render_user_stories(stories: list[UserStorySpec]) -> list[str] Render user stories section.
_render_nfrs _render_nfrs(nfrs: list[NFRequirement]) -> list[str] Render non-functional requirements section.
_render_architecture _render_architecture(arch: ArchitectureSection) -> list[str] Render architecture section.
_render_api _render_api(endpoints: list[APIEndpoint]) -> list[str] Render API design section.
_render_dependencies _render_dependencies(deps: list[str]) -> list[str] Render dependencies section.
_render_verification_checklist _render_verification_checklist(stories: list[UserStorySpec]) -> list[str] Render verification checklist section.
generate_requirements_checklist generate_requirements_checklist(spec_id: str, title: str, stories: list[UserStorySpec], requirements: list[RequirementSpec]) -> str Generate checklists/requirements.md document.
save_spec save_spec(spec_content: str, spec_id: str, spec_slug: str, output_dir: str, checklist_content: str | None = None) -> dict[str, str] Save spec document and optional checklist to disk.

Source

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

"""Specification document generator for spike-generated projects.

Produces full spec documents matching orchestrata's specs/NNN-name/spec.md
format. Also generates checklists/requirements.md with checkbox lists.

Zero orchestrata imports — standalone utility for spike pipeline.
"""

from dataclasses import dataclass, field
from datetime import UTC, datetime
from pathlib import Path


@dataclass
class SpecMetadata:
    """Specification metadata block."""

    spec_id: str
    title: str
    alias: str = ""
    author: str = "Mohamed"
    status: str = "Draft"
    created: str = ""
    priority: str = "P0"
    components: str = ""

    def __post_init__(self) -> None:
        if not self.created:
            self.created = datetime.now(UTC).strftime("%Y-%m-%d")


@dataclass
class UserStorySpec:
    """User story for spec document."""

    id: str
    title: str
    as_a: str = ""
    i_want: str = ""
    so_that: str = ""
    acceptance_criteria: list[str] = field(default_factory=list)
    priority: str = "Critical"


@dataclass
class RequirementSpec:
    """Requirement for spec checklist."""

    id: str
    description: str
    story_id: str = ""
    category: str = ""
    verified: bool = False


@dataclass
class NFRequirement:
    """Non-functional requirement."""

    id: str
    description: str
    category: str = ""
    target: str = ""


@dataclass
class ArchitectureSection:
    """Architecture description for spec."""

    overview: str = ""
    modules: list[str] = field(default_factory=list)
    patterns: list[str] = field(default_factory=list)
    technology_stack: list[str] = field(default_factory=list)
    class_diagram_mmd: str = ""
    module_diagram_mmd: str = ""


@dataclass
class APIEndpoint:
    """API endpoint or public interface."""

    name: str
    signature: str
    description: str = ""
    returns: str = ""


# -- Spec Document Generator ---------------------------------------


def generate_spec_document(
    metadata: SpecMetadata,
    overview_what: str,
    overview_why: list[str],
    overview_how: list[str],
    user_stories: list[UserStorySpec],
    nfrs: list[NFRequirement] | None = None,
    architecture: ArchitectureSection | None = None,
    api_endpoints: list[APIEndpoint] | None = None,
    dependencies: list[str] | None = None,
) -> str:
    """Generate a complete spec.md document.

    Args:
        metadata: Spec metadata block.
        overview_what: What description.
        overview_why: List of why bullet points.
        overview_how: List of how bullet points.
        user_stories: List of user stories.
        nfrs: Optional non-functional requirements.
        architecture: Optional architecture section.
        api_endpoints: Optional API endpoints.
        dependencies: Optional dependency list.

    Returns:
        Complete spec markdown string.
    """
    lines = []

    lines.extend(_render_header(metadata))
    lines.append("")
    lines.extend(_render_overview(overview_what, overview_why, overview_how))
    lines.append("")
    lines.extend(_render_user_stories(user_stories))

    if nfrs:
        lines.append("")
        lines.extend(_render_nfrs(nfrs))

    if architecture:
        lines.append("")
        lines.extend(_render_architecture(architecture))

    if api_endpoints:
        lines.append("")
        lines.extend(_render_api(api_endpoints))

    if dependencies:
        lines.append("")
        lines.extend(_render_dependencies(dependencies))

    lines.append("")
    lines.extend(_render_verification_checklist(user_stories))

    NL = "\n"
    return NL.join(lines) + NL


def _render_header(meta: SpecMetadata) -> list[str]:
    """Render spec header with metadata."""
    lines = [
        f"# SPEC-{meta.spec_id}: {meta.title}",
        "",
        "## Metadata",
        f"- **Spec ID**: {meta.spec_id}",
        f"- **Title**: {meta.title}",
    ]
    if meta.alias:
        lines.append(f"- **Alias**: {meta.alias}")
    lines.extend(
        [
            f"- **Author**: {meta.author}",
            f"- **Status**: {meta.status}",
            f"- **Created**: {meta.created}",
            f"- **Priority**: {meta.priority}",
        ]
    )
    if meta.components:
        lines.append(f"- **Components**: {meta.components}")
    lines.append("")
    lines.append("---")
    return lines


def _render_overview(what: str, why: list[str], how: list[str]) -> list[str]:
    """Render overview section."""
    lines = [
        "## Overview",
        "",
        "### What",
        what,
        "",
        "### Why",
    ]
    for item in why:
        lines.append(f"- {item}")
    lines.extend(["", "### How"])
    for item in how:
        lines.append(f"- {item}")
    lines.append("")
    lines.append("---")
    return lines


def _render_user_stories(stories: list[UserStorySpec]) -> list[str]:
    """Render user stories section."""
    lines = ["## User Scenarios & Stories"]

    for story in stories:
        lines.extend(
            [
                "",
                f"### {story.id}: {story.title} ({story.priority})",
            ]
        )
        if story.as_a:
            lines.append(f"**As a** {story.as_a}")
        if story.i_want:
            lines.append(f"**I want to** {story.i_want}")
        if story.so_that:
            lines.append(f"**So that** {story.so_that}")

        if story.acceptance_criteria:
            lines.extend(["", "**Acceptance Criteria:**"])
            for criterion in story.acceptance_criteria:
                lines.append(f"- {criterion}")

        lines.extend(["", "---"])

    return lines


def _render_nfrs(nfrs: list[NFRequirement]) -> list[str]:
    """Render non-functional requirements section."""
    lines = ["## Non-Functional Requirements", ""]

    by_category: dict[str, list[NFRequirement]] = {}
    for nfr in nfrs:
        cat = nfr.category or "General"
        by_category.setdefault(cat, []).append(nfr)

    for category, items in sorted(by_category.items()):
        lines.append(f"### {category}")
        lines.append("")
        for nfr in items:
            target = f" (target: {nfr.target})" if nfr.target else ""
            lines.append(f"- **{nfr.id}**: {nfr.description}{target}")
        lines.append("")

    return lines


def _render_architecture(arch: ArchitectureSection) -> list[str]:
    """Render architecture section."""
    lines = ["## Architecture", ""]

    if arch.overview:
        lines.extend([arch.overview, ""])

    if arch.technology_stack:
        lines.append("### Technology Stack")
        lines.append("")
        for tech in arch.technology_stack:
            lines.append(f"- {tech}")
        lines.append("")

    if arch.patterns:
        lines.append("### Design Patterns")
        lines.append("")
        for pattern in arch.patterns:
            lines.append(f"- {pattern}")
        lines.append("")

    if arch.modules:
        lines.append("### Modules")
        lines.append("")
        for module in arch.modules:
            lines.append(f"- {module}")
        lines.append("")

    if arch.module_diagram_mmd:
        lines.append("### Module Diagram")
        lines.append("")
        lines.append("```mermaid")
        lines.append(arch.module_diagram_mmd.strip())
        lines.append("```")
        lines.append("")

    if arch.class_diagram_mmd:
        lines.append("### Class Diagram")
        lines.append("")
        lines.append("```mermaid")
        lines.append(arch.class_diagram_mmd.strip())
        lines.append("```")
        lines.append("")

    return lines


def _render_api(endpoints: list[APIEndpoint]) -> list[str]:
    """Render API design section."""
    lines = ["## API Design", ""]

    for ep in endpoints:
        lines.append(f"### `{ep.name}`")
        lines.append("")
        lines.append("```python")
        lines.append(ep.signature)
        lines.append("```")
        if ep.description:
            lines.append("")
            lines.append(ep.description)
        if ep.returns:
            lines.append("")
            lines.append(f"**Returns**: {ep.returns}")
        lines.append("")

    return lines


def _render_dependencies(deps: list[str]) -> list[str]:
    """Render dependencies section."""
    lines = ["## Dependencies", ""]
    for dep in deps:
        lines.append(f"- {dep}")
    lines.append("")
    return lines


def _render_verification_checklist(
    stories: list[UserStorySpec],
) -> list[str]:
    """Render verification checklist section."""
    lines = [
        "## Verification Checklist",
        "",
        "| Story | Title | Criteria Count | Status |",
        "|-------|-------|---------------|--------|",
    ]

    for story in stories:
        count = len(story.acceptance_criteria)
        lines.append(f"| {story.id} | {story.title} | {count} | Pending |")

    lines.append("")
    return lines


# -- Requirements Checklist Generator ------------------------------


def generate_requirements_checklist(
    spec_id: str,
    title: str,
    stories: list[UserStorySpec],
    requirements: list[RequirementSpec],
) -> str:
    """Generate checklists/requirements.md document.

    Args:
        spec_id: Specification ID.
        title: Spec title.
        stories: User stories for grouping.
        requirements: All requirements with story associations.

    Returns:
        Requirements checklist markdown string.
    """
    lines = [
        f"# {title} - Functional Requirements",
        "",
        f"**Component**: {title}",
        f"**Spec**: {spec_id}",
        "**Status**: Requirements Phase",
        f"**Total Requirements**: {len(requirements)}",
        "",
        "---",
        "",
    ]

    reqs_by_story: dict[str, list[RequirementSpec]] = {}
    for req in requirements:
        sid = req.story_id or "unassigned"
        reqs_by_story.setdefault(sid, []).append(req)

    for story in stories:
        lines.append(f"## {story.id}: {story.title}")
        lines.append("")
        if story.as_a:
            lines.append(f"**As a** {story.as_a}  ")
        if story.i_want:
            lines.append(f"**I want** {story.i_want}  ")
        if story.so_that:
            lines.append(f"**So that** {story.so_that}")
        lines.append("")
        lines.append("### Requirements")
        lines.append("")

        story_reqs = reqs_by_story.get(story.id, [])
        by_category: dict[str, list[RequirementSpec]] = {}
        for req in story_reqs:
            cat = req.category or "General"
            by_category.setdefault(cat, []).append(req)

        for category, cat_reqs in sorted(by_category.items()):
            lines.append(f"#### {category}")
            for req in cat_reqs:
                check = "x" if req.verified else " "
                lines.append(f"- [{check}] **{req.id}**: {req.description}")
            lines.append("")

        lines.append("---")
        lines.append("")

    NL = "\n"
    return NL.join(lines) + NL


# -- File Output ---------------------------------------------------


def save_spec(
    spec_content: str,
    spec_id: str,
    spec_slug: str,
    output_dir: str,
    checklist_content: str | None = None,
) -> dict[str, str]:
    """Save spec document and optional checklist to disk.

    Args:
        spec_content: Spec markdown content.
        spec_id: Spec ID (e.g., "001").
        spec_slug: Spec slug (e.g., "001-textstat").
        output_dir: Base output directory.
        checklist_content: Optional requirements checklist.

    Returns:
        Dict of file type to path written.
    """
    base = Path(output_dir) / spec_slug
    base.mkdir(parents=True, exist_ok=True)

    spec_path = base / "spec.md"
    spec_path.write_text(spec_content, encoding="utf-8")

    result = {"spec": str(spec_path)}

    if checklist_content:
        checklist_dir = base / "checklists"
        checklist_dir.mkdir(parents=True, exist_ok=True)
        checklist_path = checklist_dir / "requirements.md"
        checklist_path.write_text(checklist_content, encoding="utf-8")
        result["checklist"] = str(checklist_path)

    return result