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
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
"""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