Workflow Slug System#
Agenta's workflow versioning layer uses slugs as human-readable, URL-safe immutable identifiers alongside UUIDs. Every Workflow (artifact), WorkflowVariant, and WorkflowRevision carries a slug field that uniquely identifies the entity within a project. The pattern mirrors git's branch/tag naming model, where you can address resources by a stable name rather than an opaque UUID.
Data Model and Validation#
The slug field is defined in api/oss/src/core/shared/dtos.py as a Pydantic mixin:
class Slug(BaseModel):
slug: Optional[str] = None
@field_validator("slug")
def check_url_safety(cls, v):
if v is not None:
if not match(r"^[a-zA-Z0-9_-]+$", v):
raise ValueError("slug must be URL-safe.")
return v
Allowed characters: alphanumeric, underscore (_), and hyphen (-). No spaces, dots, or other special characters. Validation runs on input, but only when a value is provided (i.e., None passes through without error at the DTO layer).
The Slug mixin is composed into all three git-layer create DTOs :
ArtifactCreateβWorkflow/WorkflowCreateVariantCreateβWorkflowVariantCreateRevisionCreate/RevisionCommitβWorkflowRevisionCreate/WorkflowRevisionCommit
The workflow-specific DTOs in api/oss/src/core/workflows/dtos.py extend these generics and inherit slug support transparently.
Database Constraints#
At the PostgreSQL layer, the slug column is defined via the SlugDBA mixin as String, nullable=False. The corresponding concrete tables enforce:
workflow_artifacts: unique constraint on(project_id, slug)workflow_variants: unique constraint on(project_id, slug)workflow_revisions: unique constraint on(project_id, slug)
Slugs are therefore unique per project, not globally. Each table also has an index on (project_id, slug) for fast lookups.
Write Path: No Automatic Slug Generation#
The DAO layer in api/oss/src/dbs/postgres/git/dao.py passes slug=artifact_create.slug (and equivalent for variants/revisions) directly to the DB entity β no generation, transformation, or fallback logic exists. This creates a tension with the database constraint:
- The DTO field is
Optional[str] = None(no slug is required by the API). - The DB column is
nullable=False(slug is required in storage). - No server-side default or auto-generation bridges the gap.
If a caller omits the slug in a WorkflowCreate / WorkflowVariantCreate / WorkflowRevisionCreate request, the insert will fail at the database level with an integrity error. There is no slugify(name) utility or UUID-based fallback anywhere in the service or DAO layers.
The one exception is forking: when fork_variant is called, forked revision slugs are derived by appending a hash suffix to the source slug (e.g., slug = revision.slug + _hash), but this is fork-specific logic, not a general solution.
Legacy App Creation Flow: Missing Slug Field#
The legacy app creation path β POST /apps/ in api/oss/src/routers/app_router.py β calls db_manager.create_app_and_envs() with only app_name, project_id, and template_key. The legacy AppDB model does not have a slug column; app_name doubles as the identifier .
The frontend template-creation modal AddAppFromTemplateModal and the createAndStartTemplate service function both submit only app_name and template_key β there is no slug field in the request payload. Validation in the UI enforces isAppNameInputValid() (letters, numbers, underscore, dash β a subset of the slug character set), but this is a UI-level guard and does not produce or submit a slug to the new workflow layer.
The new workflows API (routed through api/oss/src/apis/fastapi/workflows/router.py) is a separate path from the legacy /apps/ router; apps created via the legacy flow do not get a slug populated in the workflow layer.
Key Files#
| File | Purpose |
|---|---|
api/oss/src/core/shared/dtos.py | Slug DTO mixin + validation regex |
api/oss/src/core/git/dtos.py | ArtifactCreate, VariantCreate, RevisionCreate β slug is optional |
api/oss/src/core/workflows/dtos.py | Workflow* DTOs wrapping the git generics |
api/oss/src/dbs/postgres/shared/dbas.py | SlugDBA mixin β slug = Column(String, nullable=False) |
api/oss/src/dbs/postgres/workflows/dbes.py | WorkflowArtifactDBE, WorkflowVariantDBE, WorkflowRevisionDBE with unique constraints |
api/oss/src/dbs/postgres/git/dao.py | create_artifact, create_variant, create_revision β slug passed as-is, no generation |
api/oss/src/routers/app_router.py | Legacy app creation β no slug field |
web/oss/src/services/app-selector/api/index.ts | Frontend createAndStartTemplate β no slug parameter |