Files
cleveragents-core/docs/reference/database_schema.md
T
freemo 9b30421b34
CI / lint (pull_request) Successful in 16s
CI / typecheck (pull_request) Successful in 27s
CI / security (pull_request) Successful in 21s
CI / quality (pull_request) Successful in 16s
CI / integration_tests (pull_request) Successful in 4m10s
CI / build (pull_request) Successful in 15s
CI / unit_tests (pull_request) Successful in 8m0s
CI / docker (pull_request) Successful in 40s
CI / coverage (pull_request) Successful in 6m22s
CI / lint (push) Successful in 13s
CI / typecheck (push) Successful in 27s
CI / security (push) Successful in 22s
CI / quality (push) Successful in 15s
CI / integration_tests (push) Successful in 4m16s
CI / build (push) Successful in 15s
CI / unit_tests (push) Successful in 8m2s
CI / docker (push) Successful in 39s
CI / coverage (push) Successful in 6m23s
feat(db): add spec-aligned action and plan tables with migrations, ORM models, and benchmarks
2026-02-13 13:05:20 -05:00

297 lines
13 KiB
Markdown

# Database Schema Reference
This document describes the spec-aligned database tables created by the
A5.alpha migrations (`a5_003_spec_aligned_actions` and `a5_004_spec_aligned_plans`).
These tables replace the legacy `actions_v3` and `lifecycle_plans` tables with
a normalized schema using namespaced names as action identity and child tables
for arguments, invariants, and project links.
## Migration Chain
| Revision | Description |
|---|---|
| `a5_003_spec_aligned_actions` | Drops `lifecycle_plans` and `actions_v3`; creates `actions`, `action_invariants`, `action_arguments` |
| `a5_004_spec_aligned_plans` | Creates `v3_plans`, `plan_projects`, `plan_arguments`, `plan_invariants` |
---
## Table: `actions`
The primary action definition table. Each row represents a reusable action
template identified by its namespaced name.
**Primary Key:** `namespaced_name`
| Column | Type | Nullable | Default | Description |
|---|---|---|---|---|
| `namespaced_name` | String(255) | No | -- | PK. Full namespaced name (e.g. `local/code-review`) |
| `namespace` | String(100) | No | -- | Extracted namespace component |
| `name` | String(150) | No | -- | Extracted short name component |
| `description` | Text | No | -- | Short description |
| `long_description` | Text | Yes | NULL | Detailed description |
| `definition_of_done` | Text | No | -- | Explicit testable completion criteria |
| `strategy_actor` | String(255) | No | -- | Actor for Strategize phase |
| `execution_actor` | String(255) | No | -- | Actor for Execute phase |
| `review_actor` | String(255) | Yes | NULL | Actor for reviewing results |
| `apply_actor` | String(255) | Yes | NULL | Actor for Apply phase |
| `estimation_actor` | String(255) | Yes | NULL | Actor for cost estimation |
| `invariant_actor` | String(255) | Yes | NULL | Actor for invariant reconciliation |
| `automation_profile` | String(255) | Yes | NULL | Default automation profile name |
| `reusable` | Boolean | No | `1` | Whether action persists after use |
| `read_only` | Boolean | No | `0` | Read-only mode |
| `inputs_schema_json` | Text | Yes | NULL | JSON Schema dict for input validation |
| `state` | String(20) | No | `'available'` | `available` or `archived` |
| `created_by` | String(255) | Yes | NULL | Creator identifier |
| `tags_json` | Text | No | `'[]'` | JSON array of tag strings |
| `created_at` | String(30) | No | -- | ISO-8601 creation timestamp |
| `updated_at` | String(30) | No | -- | ISO-8601 last-update timestamp |
**Check Constraints:**
- `ck_actions_state`: `state IN ('available', 'archived')`
**Indexes:**
- `ix_actions_namespace` on `namespace`
- `ix_actions_state` on `state`
- `ix_actions_name` on `name`
---
## Table: `action_invariants`
Ordered invariant constraints attached to an action. Cascades on action delete.
**Primary Key:** `id` (auto-increment)
| Column | Type | Nullable | Default | Description |
|---|---|---|---|---|
| `id` | Integer | No | auto | Surrogate PK |
| `action_name` | String(255) | No | -- | FK to `actions.namespaced_name` (CASCADE) |
| `invariant_text` | Text | No | -- | The invariant constraint text |
| `position` | Integer | No | -- | Display ordering |
| `created_at` | String(30) | No | -- | ISO-8601 timestamp |
**Foreign Keys:**
- `fk_action_invariants_action`: `action_name` -> `actions.namespaced_name` ON DELETE CASCADE
**Indexes:**
- `ix_action_invariants_action` on `action_name`
- `ix_action_invariants_position` on `(action_name, position)`
---
## Table: `action_arguments`
Typed argument definitions for an action. Cascades on action delete.
**Primary Key:** `id` (auto-increment)
| Column | Type | Nullable | Default | Description |
|---|---|---|---|---|
| `id` | Integer | No | auto | Surrogate PK |
| `action_name` | String(255) | No | -- | FK to `actions.namespaced_name` (CASCADE) |
| `name` | String(100) | No | -- | Argument identifier |
| `arg_type` | String(20) | No | `'string'` | `string`, `integer`, `float`, `boolean`, `list` |
| `requirement` | String(20) | No | `'required'` | `required` or `optional` |
| `description` | Text | Yes | NULL | Human-readable description |
| `default_value_json` | Text | Yes | NULL | JSON-encoded default value |
| `min_value` | Float | Yes | NULL | Min bound for numeric types |
| `max_value` | Float | Yes | NULL | Max bound for numeric types |
| `validation_pattern` | String(500) | Yes | NULL | Regex pattern for string validation |
| `position` | Integer | No | -- | Display ordering |
**Foreign Keys:**
- `fk_action_arguments_action`: `action_name` -> `actions.namespaced_name` ON DELETE CASCADE
**Unique Constraints:**
- `uq_action_arguments_name` on `(action_name, name)`
**Check Constraints:**
- `ck_action_arguments_type`: `arg_type IN ('string', 'integer', 'float', 'boolean', 'list')`
- `ck_action_arguments_requirement`: `requirement IN ('required', 'optional')`
**Indexes:**
- `ix_action_arguments_action` on `action_name`
- `ix_action_arguments_position` on `(action_name, position)`
---
## Table: `v3_plans`
The primary plan table. Each row is a plan instance created from an action,
identified by a ULID. Plans reference actions by namespaced name and support
hierarchical subplan trees via self-referencing foreign keys.
**Primary Key:** `plan_id` (ULID, String(26))
| Column | Type | Nullable | Default | Description |
|---|---|---|---|---|
| `plan_id` | String(26) | No | -- | ULID primary key |
| `parent_plan_id` | String(26) | Yes | NULL | FK to `v3_plans.plan_id` (SET NULL) |
| `root_plan_id` | String(26) | Yes | NULL | FK to `v3_plans.plan_id` (SET NULL) |
| `action_name` | String(255) | No | -- | FK to `actions.namespaced_name` (RESTRICT) |
| `namespaced_name` | String(255) | No | -- | Plan's own namespaced name |
| `namespace` | String(100) | No | -- | Extracted namespace |
| `phase` | String(20) | No | `'strategize'` | Current lifecycle phase |
| `processing_state` | String(20) | No | `'queued'` | Current processing state |
| `attempt` | Integer | No | `1` | Retry attempt counter |
| `description` | Text | No | -- | Rendered description (from action template + args) |
| `definition_of_done` | Text | Yes | NULL | Rendered completion criteria |
| `strategy_actor` | String(255) | Yes | NULL | Strategize phase actor |
| `execution_actor` | String(255) | Yes | NULL | Execute phase actor |
| `review_actor` | String(255) | Yes | NULL | Review actor |
| `apply_actor` | String(255) | Yes | NULL | Apply phase actor |
| `estimation_actor` | String(255) | Yes | NULL | Estimation actor |
| `invariant_actor` | String(255) | Yes | NULL | Invariant reconciliation actor |
| `automation_profile` | String(255) | Yes | NULL | Resolved automation profile name |
| `automation_level` | String(30) | No | `'manual'` | `manual`, `review_before_apply`, `full_automation` |
| `reusable` | Boolean | No | `1` | Copied from action |
| `read_only` | Boolean | No | `0` | Copied from action |
| `inputs_schema_json` | Text | Yes | NULL | JSON Schema dict (copied from action) |
| `changeset_id` | String(26) | Yes | NULL | Changeset ULID from Execute phase |
| `sandbox_refs_json` | Text | Yes | NULL | JSON sandbox references |
| `validation_summary_json` | Text | Yes | NULL | JSON validation summary |
| `decision_root_id` | String(26) | Yes | NULL | Root decision tree ULID |
| `error_message` | Text | Yes | NULL | Error message if errored |
| `error_details_json` | Text | Yes | NULL | JSON error details dict |
| `cost_estimate_usd` | Float | Yes | NULL | Estimated cost in USD |
| `cost_actual_usd` | Float | Yes | NULL | Actual cost in USD |
| `token_count_input` | Integer | Yes | `0` | Input token count |
| `token_count_output` | Integer | Yes | `0` | Output token count |
| `created_by` | String(255) | Yes | NULL | Creator identifier |
| `tags_json` | Text | No | `'[]'` | JSON array of tag strings |
| `created_at` | String(30) | No | -- | ISO-8601 creation timestamp |
| `updated_at` | String(30) | No | -- | ISO-8601 last-update timestamp |
| `completed_at` | String(30) | Yes | NULL | ISO-8601 completion timestamp |
| `strategize_started_at` | String(30) | Yes | NULL | Strategize phase start |
| `strategize_completed_at` | String(30) | Yes | NULL | Strategize phase end |
| `execute_started_at` | String(30) | Yes | NULL | Execute phase start |
| `execute_completed_at` | String(30) | Yes | NULL | Execute phase end |
| `apply_started_at` | String(30) | Yes | NULL | Apply phase start |
| `applied_at` | String(30) | Yes | NULL | Terminal apply timestamp |
**Foreign Keys:**
- `fk_v3_plans_parent`: `parent_plan_id` -> `v3_plans.plan_id` ON DELETE SET NULL
- `fk_v3_plans_root`: `root_plan_id` -> `v3_plans.plan_id` ON DELETE SET NULL
- `fk_v3_plans_action`: `action_name` -> `actions.namespaced_name` ON DELETE RESTRICT
**Check Constraints:**
- `ck_v3_plans_phase`: `phase IN ('strategize', 'execute', 'apply', 'applied')`
- `ck_v3_plans_state`: `processing_state IN ('queued', 'processing', 'errored', 'complete', 'cancelled')`
- `ck_v3_plans_automation`: `automation_level IN ('manual', 'review_before_apply', 'full_automation')`
**Indexes:**
- `ix_v3_plans_phase` on `phase`
- `ix_v3_plans_state` on `processing_state`
- `ix_v3_plans_parent` on `parent_plan_id`
- `ix_v3_plans_root` on `root_plan_id`
- `ix_v3_plans_created` on `created_at`
- `ix_v3_plans_action` on `action_name`
- `ix_v3_plans_namespace` on `namespace`
- `ix_v3_plans_phase_state` on `(phase, processing_state)` (composite)
---
## Table: `plan_projects`
Links plans to target projects. Composite primary key. Cascades on plan delete.
**Primary Key:** `(plan_id, project_name)`
| Column | Type | Nullable | Default | Description |
|---|---|---|---|---|
| `plan_id` | String(26) | No | -- | FK to `v3_plans.plan_id` (CASCADE) |
| `project_name` | String(255) | No | -- | Target project namespaced name |
| `alias` | String(100) | Yes | NULL | Short alias for plan context |
| `read_only` | Boolean | No | `0` | Whether project is read-only for this plan |
| `created_at` | String(30) | No | -- | ISO-8601 timestamp |
**Foreign Keys:**
- `fk_plan_projects_plan`: `plan_id` -> `v3_plans.plan_id` ON DELETE CASCADE
**Indexes:**
- `ix_plan_projects_project` on `project_name`
---
## Table: `plan_arguments`
Resolved argument values for a plan instance. Cascades on plan delete.
**Primary Key:** `id` (auto-increment)
| Column | Type | Nullable | Default | Description |
|---|---|---|---|---|
| `id` | Integer | No | auto | Surrogate PK |
| `plan_id` | String(26) | No | -- | FK to `v3_plans.plan_id` (CASCADE) |
| `name` | String(100) | No | -- | Argument name |
| `value_json` | Text | Yes | NULL | JSON-encoded argument value |
| `value_type` | String(20) | No | `'string'` | Value type hint |
| `position` | Integer | No | -- | Display ordering |
**Foreign Keys:**
- `fk_plan_arguments_plan`: `plan_id` -> `v3_plans.plan_id` ON DELETE CASCADE
**Unique Constraints:**
- `uq_plan_arguments_name` on `(plan_id, name)`
**Indexes:**
- `ix_plan_arguments_plan` on `plan_id`
- `ix_plan_arguments_position` on `(plan_id, position)`
---
## Table: `plan_invariants`
Plan-level invariant constraints with scope provenance. Cascades on plan delete.
**Primary Key:** `id` (auto-increment)
| Column | Type | Nullable | Default | Description |
|---|---|---|---|---|
| `id` | Integer | No | auto | Surrogate PK |
| `plan_id` | String(26) | No | -- | FK to `v3_plans.plan_id` (CASCADE) |
| `invariant_text` | Text | No | -- | The invariant constraint text |
| `source_scope` | String(20) | No | `'plan'` | Origin scope: `global`, `project`, `action`, `plan` |
| `source_name` | String(255) | Yes | NULL | Name of the source entity |
| `position` | Integer | No | -- | Display ordering |
| `created_at` | String(30) | No | -- | ISO-8601 timestamp |
**Foreign Keys:**
- `fk_plan_invariants_plan`: `plan_id` -> `v3_plans.plan_id` ON DELETE CASCADE
**Unique Constraints:**
- `uq_plan_invariants_text` on `(plan_id, invariant_text)`
**Check Constraints:**
- `ck_plan_invariants_scope`: `source_scope IN ('global', 'project', 'action', 'plan')`
**Indexes:**
- `ix_plan_invariants_plan` on `plan_id`
- `ix_plan_invariants_position` on `(plan_id, position)`
---
## Entity Relationship Diagram
```
actions (PK: namespaced_name)
|-- 1:N --> action_invariants (FK: action_name)
|-- 1:N --> action_arguments (FK: action_name)
|-- 1:N --> v3_plans (FK: action_name, RESTRICT)
v3_plans (PK: plan_id)
|-- self --> parent_plan_id (SET NULL)
|-- self --> root_plan_id (SET NULL)
|-- 1:N --> plan_projects (FK: plan_id, CASCADE)
|-- 1:N --> plan_arguments (FK: plan_id, CASCADE)
|-- 1:N --> plan_invariants (FK: plan_id, CASCADE)
```
## Source Location
- Migration (actions): `alembic/versions/a5_003_spec_aligned_actions.py`
- Migration (plans): `alembic/versions/a5_004_spec_aligned_plans.py`
- ORM models: `src/cleveragents/infrastructure/database/models.py`
- Repositories: `src/cleveragents/infrastructure/database/repositories.py`