How AutoBot generates frontend TypeScript types from canonical Python sources, what the codegen MANIFEST covers, how to extend it, and how duplicate-enum reintroduction is prevented.
Tracking: #7122 (initial pipeline) · #7226 / PR #7269 (MANIFEST extension) · #6973 / #6689 (canonical enum consolidation) · #9869 (this doc)
Without codegen, every backend dataclass or enum change must be mirrored by hand in the frontend type files. #7044 documented exactly this drift: the frontend TemplateStep interface had only 2 of 7 fields actually matching what /api/templates emits, and the gap survived 18 months because v-if defaults masked the missing fields. Codegen makes that drift impossible to introduce silently — the generated file is checked in, and CI regenerates and diffs it on every relevant change.
Source of truth: autobot-infrastructure/shared/scripts/gen_frontend_types.py — the MANIFEST list near the top of the script.
Each entry is a tuple (relative file path from repo root, class name). File paths (not module paths) are used so the script can load sources via spec_from_file_location, bypassing package __init__.py chains that would pull in heavyweight runtime deps (aiohttp, pydantic, etc.) — CI runs the script in a slim Python-only environment.
| Canonical Python source | Class | Emitted TS |
|---|---|---|
autobot_shared/workflow/types.py |
PromptSpec |
interface PromptSpec |
autobot_shared/workflow/types.py |
ExecutionStrategy |
string-union ExecutionStrategy |
autobot_shared/workflow/types.py |
WorkflowTask |
interface WorkflowTask |
autobot_shared/workflow/types.py |
WorkflowPlan |
interface WorkflowPlan |
autobot-backend/services/workflow_automation/models.py |
WorkflowStepStatus |
string-union WorkflowStepStatus |
autobot_shared/status_enums.py |
Severity |
string-union Severity + alias RiskLevel |
autobot_shared/auth/permissions.py |
Role |
string-union Role (#14937) |
WorkflowStepStatus and RiskLevel are the most recent additions — added by PR #7269 (closing #7226) so the canonical enum consolidations (#6973 TaskStatus, #6689 Severity/RiskLevel) are end-to-end: one Python definition, one generated TS union, no hand-written copy on either side.
RiskLevel is not a separate enum: in Python it is an alias (RiskLevel = Severity in autobot_shared/status_enums.py), and in TypeScript it is emitted via the script's ALIASES map as export type RiskLevel = Severity;.
- Pydantic models — not supported. Convert to a
@dataclassfirst, or wait for OpenAPI-based codegen (deferred). - API response envelopes / router schemas — these flow through FastAPI's OpenAPI surface, not this script.
- Frontend-only types — anything with no canonical Python source stays hand-written in
autobot-frontend/src/types/. - Enums outside the manifest — only listed classes are generated. Canonical enums in
autobot_shared/status_enums.py(e.g.Priority,HealthStatus,LLMProvider) are eligible but only emitted once added toMANIFEST.
Supported class kinds: @dataclass (→ TS interface) and Enum / str, Enum (→ TS string union of member values).
# Regenerate (writes the output file)
python3 autobot-infrastructure/shared/scripts/gen_frontend_types.py
# Check mode (used by CI) — exits non-zero on drift
python3 autobot-infrastructure/shared/scripts/gen_frontend_types.py --checkOutput: autobot-frontend/src/types/_generated/workflow.ts — committed to the repo so consumers import a stable path.
Stable public import path: @/types/workflowTemplates re-exports the generated types (PromptSpec, WorkflowTask, WorkflowPlan, WorkflowStepStatus, RiskLevel). Prefer importing from there; import from @/types/_generated/workflow directly only when the re-export does not expose what you need.
Python side: there is no generated Python — the Python types are the canonical source. Backend code imports them directly (e.g. from autobot_shared.status_enums import RiskLevel, which is the Severity alias re-exported via __all__).
.github/workflows/frontend-codegen-drift.yml runs gen_frontend_types.py --check on every PR that touches a MANIFEST source file, the generated output, or the codegen script itself. If the committed _generated/workflow.ts does not match what the script produces from the current Python sources, the job fails with a DRIFT: message telling you to re-run the script.
Consequence: you cannot change a manifested Python type without regenerating and committing the TS in the same PR.
The #7226 cookbook, as documented in the script header:
- Identify the canonical Python source. For dataclasses this is a
@dataclass; for enums aclass X(Enum):orclass X(str, Enum):. If duplicates exist, consolidate ontoautobot_sharedfirst (see CANONICAL_RULES.md and the #6973/#6689 pattern) — codegen mirrors one source, it does not merge duplicates. - Append
(relative_file_path, "ClassName")toMANIFESTingen_frontend_types.py. The file must be loadable with onlyautobot_shared/'s parent andautobot-backend/onsys.path(avoid sources whose module-level imports need heavy runtime deps). - If the TS name should differ or an alias is needed (like
RiskLevel→Severity), add it to theALIASESmap instead of duplicating the enum. - Re-run codegen and commit the regenerated TS:
python3 autobot-infrastructure/shared/scripts/gen_frontend_types.py git add autobot-infrastructure/shared/scripts/gen_frontend_types.py \ autobot-frontend/src/types/_generated/workflow.ts - Update frontend imports to consume the generated type — re-export it from
@/types/workflowTemplatesif a stable public path is preferred. Delete any hand-written duplicate union/interface it replaces. - Add the new source file to the CI trigger paths in
.github/workflows/frontend-codegen-drift.yml(on.pull_request.pathsandon.push.paths) if it lives in a file not already listed — otherwise drift in that file won't trigger the check.
Two layers stop the duplicates from creeping back in after consolidation:
- Pre-commit hook
no-new-status-enum(#6973) —autobot-infrastructure/shared/scripts/hooks/pre-commit-no-new-status-enum, registered in.pre-commit-config.yaml. Blocks commits adding new top-levelclass FooStatus(Enum):/class FooStatus(str, Enum):declarations outsideautobot_shared/status_enums.pyand a short audited exemption list (domain-specific, non-lifecycle shapes). New code must import the canonical type (TaskStatus, or itsJobStatus/WorkflowStatusaliases) or alias it. Last-resort suppression:# noqa: status-shape. - Pre-commit hook
no-new-workflow-step(#6951) — same mechanism for the canonical workflow dataclasses: blocks new top-levelWorkflowStep/AgentTask/WorkflowPlandataclass declarations outsideautobot_shared/workflow/types.py(subclasses and aliases allowed).
On the frontend, the generated file itself is the guard: the union exists at a single generated path, the drift CI keeps it in sync, and the broader canonical-pattern lint framework is catalogued in CANONICAL_RULES.md.
- CANONICAL_RULES.md — canonical-pattern rule catalog (
tools/lint/canonical*) - 01-architecture.md — system architecture overview
- ../system-state.md — canonical type consolidation history (#6973, #6689, #6534, #7226)