Files
CoreRasurae 0c5b140d29
CI / push-validation (pull_request) Successful in 18s
CI / lint (pull_request) Successful in 26s
CI / helm (pull_request) Successful in 38s
CI / build (pull_request) Successful in 48s
CI / quality (pull_request) Successful in 54s
CI / security (pull_request) Successful in 55s
CI / typecheck (pull_request) Successful in 57s
CI / e2e_tests (pull_request) Successful in 4m48s
CI / unit_tests (pull_request) Successful in 8m5s
CI / integration_tests (pull_request) Successful in 9m23s
CI / docker (pull_request) Successful in 1m23s
CI / coverage (pull_request) Successful in 12m29s
CI / benchmark-regression (push) Failing after 0s
CI / benchmark-publish (push) Failing after 0s
CI / status-check (pull_request) Successful in 1s
CI / push-validation (push) Successful in 12s
CI / helm (push) Successful in 29s
CI / build (push) Successful in 3m23s
CI / lint (push) Successful in 3m38s
CI / quality (push) Successful in 3m40s
CI / security (push) Successful in 4m3s
CI / typecheck (push) Successful in 4m5s
CI / e2e_tests (push) Successful in 6m42s
CI / unit_tests (push) Successful in 10m7s
CI / integration_tests (push) Successful in 10m13s
CI / docker (push) Successful in 1m46s
CI / coverage (push) Successful in 10m58s
CI / status-check (push) Successful in 2s
fix(database): include alembic files in package distribution
Move alembic configuration and migration files from repository root into the
Python package structure to ensure they are included in the wheel distribution.

This fix resolves the FileNotFoundError when running `agents init` in Docker
containers or any environment using the built wheel distribution.

Changes:
- Move alembic/ directory from repo root to
  src/cleveragents/infrastructure/database/migrations/
- Move alembic.ini to the same new location and update script_location setting
- Update MigrationRunner._find_alembic_ini() to search from the new canonical
  location within the package
- Update create_template_db.py to point to the new alembic.ini location
- Update documentation references to reflect new migration file locations
- Create __init__.py for migrations package

- The env.py file is imported when running tests that verify all modules can be
  imported without errors. However, context.config is only available when alembic
  is actually running migrations, not during normal module imports. This caused
  an AttributeError when the test tried to import the migrations.env module.

- Fix by using getattr() with a default value to safely access context.config,
  and guard all code that uses config with None checks. This allows the module
  to be safely imported while still functioning correctly during migrations.

Testing:
- Verified MigrationRunner can locate alembic.ini in new location
- Tested agents init succeeds in creating project with database
- Template database creation works correctly
- All migration tests should pass without changes

Alembic files now follow standard Python packaging conventions, making them
automatically included in wheel distributions without special configuration.

ISSUES CLOSED: #4180
2026-04-17 17:40:00 +00:00

20 KiB

Database Schema Reference

Overview

CleverAgents uses SQLAlchemy ORM models with SQLite as the default backend. All tables are created via Base.metadata.create_all through the init_database() helper in src/cleveragents/infrastructure/database/models.py.

Resource Registry Tables

The resource registry consists of three core tables introduced in Stage B1:

Table Model Description
resource_types ResourceTypeModel Schema-level resource type defs
resources ResourceModel Registered resource instances
resource_edges ResourceEdgeModel Parent-child DAG edges

resource_types

Primary key: name (namespaced, e.g. builtin/git-checkout). Stores kind (physical/virtual), handler references, JSON argument schemas, allowed parent/child types, auto-discovery config, and capabilities.

resources

Primary key: resource_id (26-char ULID). FK to resource_types.name. Stores optional namespaced name, location, JSON properties/metadata, and content hash for equivalence tracking.

resource_edges

Composite primary key: (parent_id, child_id). Both columns FK to resources.resource_id with CASCADE delete. Stores link_type (contains, references, derived_from) and a self-loop check constraint.

Robot Migration Smoke Suite

A Robot Framework smoke suite validates that schema creation produces the expected resource registry tables with correct columns.

Running the suite

# Via nox (recommended — runs all Robot integration tests):
nox -s integration_tests

# Directly with robot:
robot --outputdir build/reports/robot robot/resource_registry_migration.robot

Test cases

Test Case What it checks
Schema Creation Produces Resource Registry Tables resource_types, resources, resource_edges exist
Resource Registry Tables Have Expected Columns Core columns present on each table
Migration Is Idempotent Calling init_database twice is safe

The helper script (robot/helper_resource_registry_migration.py) uses init_database() with a temporary SQLite file and validates via sqlalchemy.inspect.

Behave BDD Tests

The Behave feature file features/resource_registry_tables.feature contains comprehensive scenarios covering:

  • Table existence after schema creation
  • CRUD operations for ResourceTypeModel, ResourceModel, ResourceEdgeModel
  • Constraint enforcement (uniqueness, FK, check constraints)
  • ORM relationship navigation
  • Migration smoke verification

Run Behave tests via:

nox -s unit_tests

ASV Benchmarks

Performance benchmarks for resource registry operations live in benchmarks/resource_registry_migration_bench.py and measure:

  • Schema creation time
  • Insert throughput for types, resources, and edges
  • DAG query performance

Run benchmarks via:

nox -s benchmark

Tool and Validation Registry Tables

Tables

tools

Stores registered tool and validation definitions.

Column Type Constraints Description
tool_id TEXT PRIMARY KEY ULID identifier
name TEXT NOT NULL, UNIQUE Namespaced name (namespace/short_name)
description TEXT Human-readable description
tool_type TEXT NOT NULL, CHECK (tool/validation) Discriminator for registry queries
source_type TEXT NOT NULL, CHECK (see below) Implementation source
input_schema TEXT JSON Schema for tool inputs
output_schema TEXT JSON Schema for tool outputs
read_only BOOLEAN NOT NULL, DEFAULT FALSE Tool only reads, never writes
writes BOOLEAN NOT NULL, DEFAULT FALSE Tool can write to resources
checkpointable BOOLEAN NOT NULL, DEFAULT FALSE Tool supports checkpoint/rollback
side_effects BOOLEAN NOT NULL, DEFAULT FALSE Tool has known side effects
config_yaml TEXT Raw YAML config for reconstruction
created_at TEXT NOT NULL ISO-8601 timestamp
updated_at TEXT NOT NULL ISO-8601 timestamp

Check constraints:

  • ck_tools_tool_type: tool_type IN ('tool', 'validation')
  • ck_tools_source_type: source_type IN ('mcp', 'agent_skill', 'builtin', 'custom', 'wrapped')

Indexes:

  • ix_tools_name on (name)
  • ix_tools_tool_type on (tool_type)

tool_bindings

Stores resource slot bindings for tools.

Column Type Constraints Description
binding_id TEXT PRIMARY KEY ULID identifier
tool_id TEXT NOT NULL, FK → tools.tool_id CASCADE Parent tool reference
slot_name TEXT NOT NULL Named resource slot
resource_type TEXT NOT NULL Expected resource type
binding_mode TEXT NOT NULL, CHECK (see below) How the slot is resolved
access_level TEXT NOT NULL, DEFAULT read_only Access level on bound resource
created_at TEXT NOT NULL ISO-8601 timestamp

Check constraints:

  • ck_tool_bindings_mode: binding_mode IN ('context', 'static', 'parameter')
  • ck_tool_bindings_access: access_level IN ('read_only', 'read_write')

Unique constraints:

  • uq_tool_bindings_tool_slot: UNIQUE(tool_id, slot_name)

validation_attachments

Links validations to resources with mode semantics.

Column Type Constraints Description
attachment_id TEXT PRIMARY KEY ULID identifier
resource_id TEXT NOT NULL, FK → resources.resource_id CASCADE Target resource
validation_name TEXT NOT NULL References tools.name (validation)
mode TEXT NOT NULL, DEFAULT required required or informational
created_at TEXT NOT NULL ISO-8601 timestamp

Check constraints:

  • ck_validation_attachments_mode: mode IN ('required', 'informational')

Unique constraints:

  • uq_validation_attachments_resource_validation: UNIQUE(resource_id, validation_name)

Indexes:

  • ix_validation_attachments_resource_id on (resource_id)

Relationships

tools 1 ──< tool_bindings       (tool_id FK, CASCADE delete)
resources 1 ──< validation_attachments  (resource_id FK, CASCADE delete)
validation_attachments.validation_name → tools.name (logical, not enforced by FK)

actors

Stores actor configurations with YAML text retention, schema version tracking, and optional compiled metadata.

Column Type Constraints Description
id INTEGER PRIMARY KEY, AUTOINCREMENT Auto-generated row ID
name TEXT(255) NOT NULL, UNIQUE Namespaced name (namespace/identifier)
provider TEXT(255) NOT NULL Provider identifier (e.g. openai)
model TEXT(255) NOT NULL Model identifier (e.g. gpt-4)
config_blob JSON NOT NULL, DEFAULT {} Canonical actor configuration blob
config_hash TEXT(128) NOT NULL SHA-256 hash of config_blob
graph_descriptor JSON Adapter-produced graph descriptor
yaml_text TEXT Original YAML source text for the actor config
schema_version TEXT(20) NOT NULL, DEFAULT '1.0' Actor config schema version
compiled_metadata JSON Compiler-produced metadata (graph topology etc)
unsafe BOOLEAN NOT NULL, DEFAULT FALSE True when actor is marked unsafe
is_built_in BOOLEAN NOT NULL, DEFAULT FALSE Generated from provider registry
is_default BOOLEAN NOT NULL, DEFAULT FALSE Designated default actor
created_at DATETIME NOT NULL Row creation timestamp
updated_at DATETIME NOT NULL Last update timestamp

Notes:

  • yaml_text preserves the original YAML source so that actors can be reconstructed or exported without loss.
  • schema_version tracks which version of the actor YAML schema was used to create the configuration. Defaults to 1.0.
  • compiled_metadata stores JSON output from the actor compiler (graph topology, resolved tools, etc.).
  • name follows the namespace/identifier convention enforced by the domain model validator (e.g. local/my-actor, openai/gpt-4).

Skill Registry Tables

The skill registry stores namespaced skill definitions with cached flattened tool sets for fast resolution.

Table Model Description
skills SkillModel Registered skill definitions
skill_items SkillItemModel Child items (tools, includes, etc)

skills

Primary key: name (namespaced, e.g. local/code-tools).

Column Type Constraints Description
name String(255) PK, UNIQUE Namespaced name (namespace/short_name)
namespace String(100) NOT NULL Namespace extracted from name
short_name String(150) NOT NULL Short name extracted from name
description Text NOT NULL Human-readable description
version String(50) Optional version string
metadata_json Text JSON overrides metadata
flattened_tools_json Text JSON-serialized flattened tool descriptors
includes_json Text JSON-serialized includes list
capability_summary_json Text JSON-serialized capability summary
yaml_text Text Original YAML text of the skill definition
flattening_hash String(64) SHA-256 hash for refresh invalidation
created_at String(30) NOT NULL ISO-8601 timestamp
updated_at String(30) NOT NULL ISO-8601 timestamp

Unique constraints:

  • uq_skills_name: UNIQUE(name) (defence-in-depth — PK already unique)

Indexes:

  • ix_skills_namespace on (namespace) — used for filtered skill listing

skill_items

Child table linking skill components to their parent skill.

Column Type Constraints Description
id Integer PK, AUTOINCREMENT Auto-generated row ID
skill_name String(255) NOT NULL, FK -> skills.name CASCADE Parent skill reference
item_type String(30) NOT NULL Item kind (tool_ref, include, etc)
item_name String(500) NOT NULL Item identifier
item_config Text JSON configuration blob
item_order Integer NOT NULL Stable ordering index
created_at String(30) NOT NULL ISO-8601 timestamp

Indexes:

  • ix_skill_items_skill_name on (skill_name)
  • ix_skill_items_item_type on (item_type)

Persistence field mapping

Database Column Domain Model Property
flattened_tools_json ResolvedToolEntry list (JSON)
includes_json SkillInclude list (JSON)
capability_summary_json SkillCapabilitySummary (JSON)
yaml_text Original YAML source text
flattening_hash SHA-256 digest of yaml_text

Decision Tree Tables

The decision tree subsystem consists of two tables introduced in Stage M3.2:

Table Model Description
decisions DecisionModel Decision tree nodes
decision_dependencies DecisionDependencyModel Decision influence DAG edges

decisions

Primary key: decision_id (26-char ULID). FK to v3_plans.plan_id (CASCADE delete). Self-referential FKs for parent_decision_id, corrects_decision_id, and superseded_by (SET NULL).

Column Type Constraints Description
decision_id String(26) PRIMARY KEY ULID identifier
plan_id String(26) NOT NULL, FK -> v3_plans CASCADE Parent plan reference
parent_decision_id String(26) FK -> decisions SET NULL Parent decision in tree
sequence_number Integer NOT NULL Monotonic order within plan
decision_type String(30) NOT NULL, CHECK (11 enum values) Classification of the decision
question Text NOT NULL What question was being answered
chosen_option Text NOT NULL The option that was chosen
alternatives_considered_json Text JSON array of alternative options
confidence_score Float CHECK (0.0-1.0 or NULL) Confidence in the decision
context_snapshot_json Text NOT NULL JSON blob of ContextSnapshot
rationale Text Human-readable rationale
actor_reasoning Text Raw LLM reasoning trace
downstream_decision_ids_json Text JSON array of dependent decision ULIDs
downstream_plan_ids_json Text JSON array of spawned plan ULIDs
artifacts_produced_json Text JSON array of ArtifactRef objects
created_at String(30) NOT NULL ISO-8601 timestamp
is_correction Boolean NOT NULL, DEFAULT FALSE Whether this is a correction
corrects_decision_id String(26) FK -> decisions SET NULL Decision being corrected
correction_reason Text Why the correction was made
superseded_by String(26) FK -> decisions SET NULL Decision that supersedes this one

Check constraints:

  • ck_decisions_type: decision_type IN ('prompt_definition', 'invariant_enforced', 'strategy_choice', 'implementation_choice', 'resource_selection', 'subplan_spawn', 'subplan_parallel_spawn', 'tool_invocation', 'error_recovery', 'validation_response', 'user_intervention')
  • ck_decisions_confidence: confidence_score IS NULL OR (confidence_score >= 0.0 AND confidence_score <= 1.0)

Indexes:

  • ix_decisions_plan on (plan_id)
  • ix_decisions_parent on (parent_decision_id)
  • ix_decisions_type on (decision_type)
  • ix_decisions_created on (created_at)
  • ix_decisions_plan_seq on (plan_id, sequence_number)

decision_dependencies

Composite primary key: (source_decision_id, target_decision_id). Both columns FK to decisions.decision_id with CASCADE delete. Stores relationship_type (default influences) and a self-loop check constraint.

Column Type Constraints Description
source_decision_id String(26) PK, FK -> decisions CASCADE Decision that influences
target_decision_id String(26) PK, FK -> decisions CASCADE Decision that is influenced
relationship_type String(30) NOT NULL, DEFAULT 'influences' Type of influence relationship
created_at String(30) NOT NULL ISO-8601 timestamp

Check constraints:

  • ck_decision_deps_no_self_loop: source_decision_id != target_decision_id

Indexes:

  • ix_decision_deps_source on (source_decision_id)
  • ix_decision_deps_target on (target_decision_id)

Migration

  • Revision: c1_001_tool_registry
  • Depends on: b0_001_projects
  • File: src/cleveragents/infrastructure/database/migrations/versions/c1_001_tool_registry.py