feat(task-023): bind workflow templates into demo flow
This commit is contained in:
@@ -13,6 +13,7 @@ from src.domain.contracts import (
|
||||
PublishingRules,
|
||||
Role,
|
||||
ScriptConfigVersionStatus,
|
||||
WorkflowTemplateStatus,
|
||||
)
|
||||
from src.domain.schema import (
|
||||
SEEDED_ADMIN_EMAIL,
|
||||
@@ -22,6 +23,7 @@ from src.domain.schema import (
|
||||
|
||||
|
||||
SEED_TIMESTAMP = datetime(2026, 1, 1, tzinfo=UTC)
|
||||
SEEDED_WORKFLOW_TEMPLATE_SLUG = "demo-article-production"
|
||||
|
||||
|
||||
def seed_reference_data(repository: object) -> None:
|
||||
@@ -125,6 +127,160 @@ assets:
|
||||
transform_script=transform_script,
|
||||
transform_script_hash=_sha256(transform_script),
|
||||
)
|
||||
_seed_demo_workflow_template(repository, admin_id=admin_id)
|
||||
|
||||
|
||||
def _seed_demo_workflow_template(repository: object, *, admin_id: UUID) -> None:
|
||||
existing = next(
|
||||
(
|
||||
workflow
|
||||
for workflow in repository.workflow_templates.list()
|
||||
if workflow.slug == SEEDED_WORKFLOW_TEMPLATE_SLUG
|
||||
),
|
||||
None,
|
||||
)
|
||||
if existing is not None:
|
||||
return
|
||||
|
||||
workflow = repository.workflow_templates.create(
|
||||
name="Demo article production workflow",
|
||||
slug=SEEDED_WORKFLOW_TEMPLATE_SLUG,
|
||||
description="Active demo workflow for article intake, drafting, review, and publishing.",
|
||||
status=WorkflowTemplateStatus.DRAFT,
|
||||
version=1,
|
||||
created_by=admin_id,
|
||||
updated_by=admin_id,
|
||||
created_at=SEED_TIMESTAMP,
|
||||
updated_at=SEED_TIMESTAMP,
|
||||
)
|
||||
for stage in _demo_workflow_stages():
|
||||
repository.workflow_template_stages.create(
|
||||
workflow_id=workflow.id,
|
||||
stable_key=stage["stable_key"],
|
||||
display_name=stage["display_name"],
|
||||
description=stage["description"],
|
||||
position=stage["position"],
|
||||
owner_role=stage["owner_role"],
|
||||
runner_profile_key=stage["runner_profile_key"],
|
||||
required_inputs=stage["required_inputs"],
|
||||
expected_outputs=stage["expected_outputs"],
|
||||
acceptance_criteria=stage["acceptance_criteria"],
|
||||
requires_human_approval=stage["requires_human_approval"],
|
||||
retry_policy=stage["retry_policy"],
|
||||
parts=stage["parts"],
|
||||
updated_by=admin_id,
|
||||
created_at=SEED_TIMESTAMP,
|
||||
updated_at=SEED_TIMESTAMP,
|
||||
)
|
||||
|
||||
repository.workflow_templates.activate(
|
||||
workflow_id=workflow.id,
|
||||
updated_by=admin_id,
|
||||
activated_at=SEED_TIMESTAMP,
|
||||
)
|
||||
|
||||
|
||||
def _demo_workflow_stages() -> list[dict[str, object]]:
|
||||
return [
|
||||
{
|
||||
"stable_key": "intake-boundary-questions",
|
||||
"display_name": "Boundary question intake",
|
||||
"description": "Collect required editorial context before plan generation.",
|
||||
"position": 1,
|
||||
"owner_role": Role.EDITOR,
|
||||
"runner_profile_key": "boundary-question-agent-v1",
|
||||
"required_inputs": ["article_brief", "target_site"],
|
||||
"expected_outputs": ["answered_boundary_questions"],
|
||||
"acceptance_criteria": ["All required boundary questions are answered."],
|
||||
"requires_human_approval": True,
|
||||
"retry_policy": {"max_attempts": 1, "backoff_seconds": 0},
|
||||
"parts": [],
|
||||
},
|
||||
{
|
||||
"stable_key": "plan-review",
|
||||
"display_name": "Plan review",
|
||||
"description": "Generate and approve the article plan before research.",
|
||||
"position": 2,
|
||||
"owner_role": Role.EDITOR,
|
||||
"runner_profile_key": "plan-generation-agent-v1",
|
||||
"required_inputs": ["answered_boundary_questions"],
|
||||
"expected_outputs": ["approved_plan"],
|
||||
"acceptance_criteria": ["Plan is approved by the editor."],
|
||||
"requires_human_approval": True,
|
||||
"retry_policy": {"max_attempts": 2, "backoff_seconds": 30},
|
||||
"parts": [],
|
||||
},
|
||||
{
|
||||
"stable_key": "research-evidence",
|
||||
"display_name": "Research and evidence",
|
||||
"description": "Run research and approve evidence before production starts.",
|
||||
"position": 3,
|
||||
"owner_role": Role.EDITOR,
|
||||
"runner_profile_key": "research-agent-v1",
|
||||
"required_inputs": ["approved_plan"],
|
||||
"expected_outputs": ["approved_evidence_matrix"],
|
||||
"acceptance_criteria": ["Evidence matrix is ready and approved."],
|
||||
"requires_human_approval": True,
|
||||
"retry_policy": {"max_attempts": 2, "backoff_seconds": 60},
|
||||
"parts": [],
|
||||
},
|
||||
{
|
||||
"stable_key": "parallel-production",
|
||||
"display_name": "Parallel production",
|
||||
"description": "Create section scaffolds and draft-ready assets in parallel.",
|
||||
"position": 4,
|
||||
"owner_role": Role.EDITOR,
|
||||
"runner_profile_key": "section-scaffold-agent-v1",
|
||||
"required_inputs": ["approved_evidence_matrix"],
|
||||
"expected_outputs": ["section_scaffolds", "asset_specs"],
|
||||
"acceptance_criteria": ["All production jobs have succeeded or been retried."],
|
||||
"requires_human_approval": False,
|
||||
"retry_policy": {"max_attempts": 2, "backoff_seconds": 60},
|
||||
"parts": [],
|
||||
},
|
||||
{
|
||||
"stable_key": "draft-assembly",
|
||||
"display_name": "Draft assembly",
|
||||
"description": "Assemble a draft from the approved plan and production artifacts.",
|
||||
"position": 5,
|
||||
"owner_role": Role.EDITOR,
|
||||
"runner_profile_key": "draft-assembly-agent-v1",
|
||||
"required_inputs": ["section_scaffolds", "approved_assets"],
|
||||
"expected_outputs": ["article_draft"],
|
||||
"acceptance_criteria": ["Draft is assembled for review."],
|
||||
"requires_human_approval": False,
|
||||
"retry_policy": {"max_attempts": 2, "backoff_seconds": 60},
|
||||
"parts": [],
|
||||
},
|
||||
{
|
||||
"stable_key": "seo-language-review",
|
||||
"display_name": "SEO and language review",
|
||||
"description": "Run automated quality reviews and resolve required suggestions.",
|
||||
"position": 6,
|
||||
"owner_role": Role.EDITOR,
|
||||
"runner_profile_key": "review-agent-v1",
|
||||
"required_inputs": ["article_draft"],
|
||||
"expected_outputs": ["resolved_review_suggestions"],
|
||||
"acceptance_criteria": ["Required SEO and language suggestions are resolved."],
|
||||
"requires_human_approval": True,
|
||||
"retry_policy": {"max_attempts": 2, "backoff_seconds": 60},
|
||||
"parts": [],
|
||||
},
|
||||
{
|
||||
"stable_key": "final-publishing",
|
||||
"display_name": "Final approval and publishing",
|
||||
"description": "Approve final draft, dry-run the bundle, and create the publish commit.",
|
||||
"position": 7,
|
||||
"owner_role": Role.EDITOR,
|
||||
"runner_profile_key": "publishing-agent-v1",
|
||||
"required_inputs": ["resolved_review_suggestions", "approved_assets"],
|
||||
"expected_outputs": ["publish_commit"],
|
||||
"acceptance_criteria": ["Publish dry-run passes and commit is created."],
|
||||
"requires_human_approval": True,
|
||||
"retry_policy": {"max_attempts": 1, "backoff_seconds": 0},
|
||||
"parts": [],
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def _repository_url_for_seed() -> str:
|
||||
|
||||
Reference in New Issue
Block a user