> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

# ADR-0013: actions.yaml Automation System

**Status:** Proposed

**Date:** 2026-06-03

**Deciders:** TBD

**Relates to:** [ADR-0012 (Job and Deployment Management System)](/reference/docs/decisions/0012-job-deployment-management-system.md) — actions.yaml builds on the PostgreSQL persistence layer

## Context

Themelio users need a way to define reusable, parameterized computational workflows directly in their repositories. Currently, creating jobs or deployments requires manual API calls for each operation, which prevents users from packaging, sharing, and automating common computational patterns.

Without repository-scoped automation, users cannot:

- Define custom workflows that others can reuse
- Parameterize operations for different use cases
- Embed computational setup alongside code and data
- Provide self-service automation for repository consumers

ADR-0012 provides the PostgreSQL-based persistence layer for jobs and deployments. This ADR builds on that foundation by introducing a declarative YAML format for defining automated actions.

### Current Problems

The absence of a repository-scoped automation system creates several problems:

1. **No Declarative Automation**: Users cannot define computational workflows in their repositories. Every job or deployment must be created manually through API calls or UI interactions.

2. **No Custom Workflows**: Repository maintainers cannot package common computational patterns (e.g., "train this model with these parameters") for others to use.

3. **No Reusable Action Patterns**: Each operation must be configured from scratch. There is no way to share or reuse action definitions across repositories or teams.

4. **No UI-Guided Setup**: Creating deployable models or spaces requires manual `actions.yaml` creation. There is no guided UI flow to automatically generate appropriate action configurations for supported model types (e.g., KServe for inference) or space types (e.g., Gradio for apps).

### Requirements

An actions.yaml system must satisfy the following requirements:

1. **Repository-Scoped Definition**: Actions must be definable within a GitLab repository via a YAML file (`actions.yaml`) that lives alongside code and data.

2. **Parameter Substitution**: Actions must support parameterization using a `$parameterName` syntax so users can provide runtime values.

3. **Multiple Action Types**: The system must support at least three action types:
   - `slurm-job`: Execute scripts on an external SLURM cluster via SSH
   - `k8s-deployment`: Deploy long-running services on Kubernetes
   - `k8s-inferenceservice`: Deploy ML inference services via KServe

4. **Web UI Integration**: The web service must provide UI components for:
   - Discovering available actions in a repository
   - Displaying action parameters with appropriate input fields
   - Executing actions with user-provided parameter values

5. **UI-Guided Creation**: For supported model and space types, the UI must guide users through automatic `actions.yaml` generation:
   - **Models**: When creating a new model with KServe support, automatically generate a `k8s-inferenceservice` action
   - **Spaces**: When creating a new space of a supported type (e.g., "gradio"), automatically generate a `k8s-deployment` action

6. **ADR-0012 Integration**: All actions must integrate with the PostgreSQL-based persistence layer described in ADR-0012 for job/deployment tracking and history.

7. **Quota Enforcement**: Resources created by actions must be isolated and quota-enforced:
   - **Kubernetes resources**: Created in the k8s namespace linked to the Themelio project that the user is a member of
   - **SLURM resources**: Created under the corresponding SLURM account associated with the user's project
   - This leverages existing quota mechanisms at the namespace/account level

## Decision Drivers

- **User Empowerment**: Repository maintainers should be able to define self-service automation without requiring operator intervention.
- **Reusability**: Common computational patterns should be definable once and reused across many repositories.
- **Integration**: The system must build on ADR-0012's persistence layer without requiring architectural changes.
- **Security**: User-provided YAML must be validated and sandboxed to prevent security vulnerabilities.
- **Simplicity**: The YAML format should be simple enough for non-experts while powerful enough for complex workflows.

## Considered Options

### Option A: actions.yaml with Multiple Action Types

Introduce a `actions.yaml` file that repository maintainers can create at the root of their GitLab repositories. This file defines one or more actions that users can execute through the web UI.

**Architecture:**

1. **YAML Format**: Actions are defined in `actions.yaml` with the following structure:

```yaml
train-model:
  title: "Train Model"
  type: "slurm-job"
  parameters:
    learning_rate:
      type: "number"
      title: "Learning Rate"
      default: 0.001
    epochs:
      type: "number"
      title: "Epochs"
      default: 10
    use_gpu:
      type: "boolean"
      title: "Use GPU"
      default: false
    framework:
      type: "enum"
      title: "Framework"
      options:
        - pytorch
        - tensorflow
      default: pytorch
  script: |
    #!/bin/bash
    python train.py --lr $learning_rate --epochs $epochs --framework $framework

deploy-inference:
  title: "Deploy Inference Service"
  type: "k8s-inferenceservice"
  parameters:
    model_id:
      type: "string"
      title: "Model ID"
    gpu:
      type: "string"
      title: "GPU Count"
      default: "1"
  options:
    gpus: "$gpu"
  script: |
    #!/bin/bash
    vllm serve $model_id

deploy-gradio-app:
  title: "Deploy Gradio Application"
  type: "k8s-deployment"
  parameters:
    app_port:
      type: "string"
      title: "App Port"
      default: "7860"
  options:
    port: "$app_port"
```

2. **Web Service Integration**:
   - `ResourceActions` component displays available actions in a dropdown menu
   - For each action, render parameter input fields based on the YAML definition
   - Submit action requests to `POST /api/repos/[namespace]/[repo]/action/[branch]` with parameter values

3. **k8s-root-actor Execution**:
   - Accept action execution requests from the web service
   - Parse `actions.yaml` from the GitLab repository
   - Substitute `$parameterName` placeholders with user-provided values
   - Route to appropriate handler based on action type:
     - `slurm-job`: Create Kubernetes Job that SSHs to SLURM cluster
     - `k8s-deployment`: Create Kubernetes Deployment with Service and VirtualService
     - `k8s-inferenceservice`: Create KServe InferenceService
   - Create job/deployment records in PostgreSQL per ADR-0012

4. **Template System**:
   - Use Handlebars templates for Kubernetes manifest generation
   - Support YAML references via `#/references/` syntax for reusable configurations
   - Support nested references (references within references) for complex configurations
   - Templates stored in `k8s-root-actor/src/templates/`

5. **Persistence**:
   - All actions create job/deployment records in PostgreSQL via ADR-0012's schema
   - Job/deployment status tracked through ADR-0012's polling mechanism
   - Destroy actions update records to "destroyed" status and clean up Kubernetes resources

**Data Flow:**

**Action Execution:**

1. User navigates to repository in web UI
2. Web service fetches `actions.yaml` from GitLab repository
3. `ResourceActions` component parses YAML and displays available actions
4. User selects action and provides parameter values
5. Web service calls `k8s-root-actor` with action name and parameters
6. `k8s-root-actor` fetches `actions.yaml`, validates action exists
7. `k8s-root-actor` substitutes parameters and creates Kubernetes resources
8. `k8s-root-actor` creates job/deployment record in PostgreSQL
9. User monitors progress through web UI (polling via ADR-0012 endpoints)

**Resource Destruction:**

1. User views job/deployment list for a repository in web UI
2. User clicks "destroy" button on a specific job or deployment
3. Web service calls ADR-0012's destroy endpoint
4. k8s-root-actor deletes corresponding Kubernetes/SLURM resources
5. PostgreSQL record updated to "destroyed" status

**UI-Guided Creation:**

1. User creates a new model or space through web UI
2. If the model/space type is supported (KServe for models, Gradio for spaces), UI prompts to auto-generate `actions.yaml`
3. User confirms, web service commits appropriate action configuration to the repository
4. User can immediately execute the action or modify it manually

**Pros:**

- **User Empowerment**: Repository maintainers can define self-service automation without operator intervention
- **Reusability**: Action definitions can be versioned, forked, and shared like code
- **Flexibility**: Parameter substitution allows a single action to serve many use cases
- **Integration**: Builds cleanly on ADR-0012's PostgreSQL persistence layer
- **Declarative**: YAML format is simple, readable, and familiar to DevOps practitioners
- **UI-Guided Setup**: Automatic `actions.yaml` generation for supported model/space types reduces manual configuration
- **Destroy Support**: Per-resource-type destroy functionality accessible via job/deployment list UI

**Cons:**

- **Security Surface Area**: User-provided YAML scripts execute in Kubernetes pods with access to cluster resources
- **Complexity**: Three action types (slurm-job, k8s-deployment, k8s-inferenceservice) with different execution paths increase implementation and maintenance burden
- **Validation Challenges**: YAML syntax errors, missing parameters, or invalid action types must be caught and communicated clearly
- **Resource Limits**: Users may create actions that consume excessive resources (CPUs, GPUs, memory), though quota enforcement at the project namespace/SLURM account level provides some protection
- **GitLab Token Scope**: Actions require GitLab API access with appropriate scopes to fetch repository files
- **Learning Curve**: Users must learn the action YAML syntax and parameter substitution patterns
- **UI Complexity**: Guided creation flows for different model/space types increase UI development effort

## Decision

We will implement Option A: the actions.yaml automation system with multiple action types.

This approach provides the best balance of user empowerment, reusability, and integration with the existing ADR-0012 architecture. The declarative YAML format allows repository maintainers to define self-service automation while the parameter substitution system enables flexible runtime configuration.

The UI-guided creation for supported model and space types (KServe for models, Gradio for spaces) reduces manual configuration burden and improves user experience. Destroy functionality is provided per resource type through the job/deployment list UI, leveraging ADR-0012's existing destroy endpoints.

**See also:** [DD-0013: Implementation details](/dd/0013-actions-yaml-automation-system.md)

## Consequences

**Positive:**

- Repository maintainers can define self-service automation without requiring operator intervention for each action
- Action definitions are versioned alongside code, enabling reproducible computational workflows
- Parameter substitution allows a single action definition to serve many use cases
- Clean integration with ADR-0012's PostgreSQL persistence layer ensures job/deployment history and auditability
- UI-guided creation for supported types (KServe models, Gradio spaces) reduces manual configuration
- Destroy functionality accessible via job/deployment list UI enables full lifecycle management
- Reusable action patterns can be shared across repositories and teams

**Negative:**

- Increased security surface area: user-provided scripts execute in Kubernetes pods with cluster access
- Implementation complexity: three action types with different execution paths require careful testing and maintenance
- YAML syntax errors and validation failures create a new category of user-facing errors
- Resource exhaustion risk: users may define actions that consume excessive cluster resources
- Operational burden: operators must monitor action execution and handle failures gracefully
- Additional infrastructure: Handlebars template compilation and reference resolution add runtime complexity
- UI development effort: guided creation flows for different model/space types increase frontend complexity

**Neutral:**

- Actions are stored in GitLab repositories alongside code, leveraging existing access control and versioning
- Web service acts as a passthrough for action execution, delegating logic to k8s-root-actor
- Job/deployment records in PostgreSQL are owned solely by k8s-root-actor, maintaining ADR-0012's single-owner pattern
- The system does not introduce new databases or storage mechanisms beyond ADR-0012's PostgreSQL schema

## References

- [ADR-0012: Job and Deployment Management System](/reference/docs/decisions/0012-job-deployment-management-system.md) — PostgreSQL persistence layer for jobs and deployments
- [Kubernetes API Documentation](https://kubernetes.io/docs/reference/kubernetes-api/) — Kubernetes resource types and APIs
- [SLURM Documentation](https://slurm.schedmd.com/documentation.html) — SLURM workload manager reference
- [KServe InferenceService CRD](https://kserve.github.io/kserve/0.11/modelserving/v1beta1/inference_service.html) — KServe InferenceService specification
- [Handlebars.js](https://handlebarsjs.com/) — Template engine for Kubernetes manifest generation

<details class="page-source"><summary>Page source</summary>

<a href="/source/docs/decisions/0013-actions-yaml-automation-system.md.txt">Source snapshot</a> · Revision `f928834c8`.

</details>
