ACTIVITY-WP-0032-T02 / ACT-ADR-006. ops_runs grows harness_profile_ref (text, indexed) and execution_refs (jsonb), migration 0008, threaded through the emission path, queue projection, and run artefacts. The important part is the enforcement, not the columns. ACT-ADR-006 says approach_hint must never override, synthesize, or fall back from an absent or invalid harness_profile_ref — a silent fallback would reintroduce the claim-time routing failure of 2026-08-17. resolve_execution_selector never consults the hint: a malformed ref raises even when a hint is present, and ACTIVITY_CORE_REQUIRE_HARNESS_PROFILE turns an absent ref into an error once definitions have migrated. Validation is structural only, since the glas-harness catalogue is authoritative and must not be mirrored. Requiring the <id>@<version> pin is worth doing locally: GlasProfiles.resolve matches an unpinned ref against every version and refuses it as ambiguous, so the pin converts a late failure into an emission-time error without knowing any profile id. Migration verified on real PostgreSQL 16: upgrade, downgrade, re-upgrade, and a legacy-shaped row still inserts and stays claimable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
188 lines
7.6 KiB
Python
188 lines
7.6 KiB
Python
"""SQLAlchemy ORM table definitions for activity-core.
|
|
|
|
These are the persistence-layer counterparts to the Pydantic domain models in
|
|
models.py. Alembic reads Base.metadata (imported via db.py) for autogenerate.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import uuid
|
|
from datetime import datetime
|
|
|
|
from sqlalchemy import (
|
|
Boolean,
|
|
DateTime,
|
|
ForeignKey,
|
|
Integer,
|
|
String,
|
|
Text,
|
|
func,
|
|
)
|
|
from sqlalchemy.dialects.postgresql import JSONB, UUID
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from activity_core.db import Base
|
|
|
|
|
|
class ActivityDefinition(Base):
|
|
__tablename__ = "activity_definitions"
|
|
|
|
id: Mapped[uuid.UUID] = mapped_column(
|
|
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
|
|
)
|
|
name: Mapped[str] = mapped_column(Text, nullable=False)
|
|
enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
|
|
trigger_type: Mapped[str] = mapped_column(Text, nullable=False)
|
|
trigger_config: Mapped[dict] = mapped_column(JSONB, nullable=False)
|
|
context_sources: Mapped[list] = mapped_column(JSONB, nullable=False, default=list)
|
|
task_templates: Mapped[list] = mapped_column(JSONB, nullable=False, default=list)
|
|
rules_json: Mapped[list] = mapped_column(JSONB, nullable=False, default=list)
|
|
instructions_json: Mapped[list] = mapped_column(JSONB, nullable=False, default=list)
|
|
dedupe_key_strategy: Mapped[str] = mapped_column(
|
|
Text, nullable=False, default="skip"
|
|
)
|
|
version: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), nullable=False, server_default=func.now()
|
|
)
|
|
updated_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
nullable=False,
|
|
server_default=func.now(),
|
|
onupdate=func.now(),
|
|
)
|
|
|
|
|
|
class ActivityRun(Base):
|
|
__tablename__ = "activity_runs"
|
|
|
|
run_id: Mapped[uuid.UUID] = mapped_column(
|
|
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
|
|
)
|
|
activity_id: Mapped[uuid.UUID] = mapped_column(
|
|
UUID(as_uuid=True),
|
|
ForeignKey("activity_definitions.id", ondelete="RESTRICT"),
|
|
nullable=False,
|
|
index=True,
|
|
)
|
|
scheduled_for: Mapped[datetime | None] = mapped_column(
|
|
DateTime(timezone=True), nullable=True
|
|
)
|
|
fired_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), nullable=False, server_default=func.now()
|
|
)
|
|
context_snapshot: Mapped[dict] = mapped_column(
|
|
JSONB, nullable=False, default=dict
|
|
)
|
|
tasks_spawned: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
version_used: Mapped[int] = mapped_column(Integer, nullable=False)
|
|
|
|
|
|
class TaskSpawnLog(Base):
|
|
__tablename__ = "task_spawn_log"
|
|
|
|
id: Mapped[uuid.UUID] = mapped_column(
|
|
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
|
|
)
|
|
activity_def_id: Mapped[uuid.UUID] = mapped_column(
|
|
UUID(as_uuid=True),
|
|
ForeignKey("activity_definitions.id"),
|
|
nullable=False,
|
|
index=True,
|
|
)
|
|
source_type: Mapped[str] = mapped_column(String(20), nullable=False)
|
|
source_id: Mapped[str] = mapped_column(Text, nullable=False)
|
|
source_version: Mapped[str] = mapped_column(Text, nullable=False)
|
|
triggering_event_id: Mapped[str] = mapped_column(Text, nullable=False, index=True)
|
|
task_ref: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
condition_matched: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
prompt_hash: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
|
model: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
output_validated: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
|
|
review_required: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), nullable=False, server_default=func.now()
|
|
)
|
|
|
|
|
|
class EventType(Base):
|
|
__tablename__ = "event_types"
|
|
|
|
type_id: Mapped[str] = mapped_column(Text, primary_key=True)
|
|
version: Mapped[str] = mapped_column(Text, nullable=False)
|
|
publisher: Mapped[str] = mapped_column(Text, nullable=False)
|
|
governance: Mapped[str] = mapped_column(Text, nullable=False, default="publisher-declared")
|
|
status: Mapped[str] = mapped_column(Text, nullable=False, default="active")
|
|
attribute_schema: Mapped[dict] = mapped_column(JSONB, nullable=False)
|
|
raw_md: Mapped[str] = mapped_column(Text, nullable=False)
|
|
synced_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), nullable=False, server_default=func.now()
|
|
)
|
|
|
|
|
|
class TaskInstance(Base):
|
|
__tablename__ = "task_instances"
|
|
|
|
id: Mapped[uuid.UUID] = mapped_column(
|
|
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
|
|
)
|
|
run_id: Mapped[uuid.UUID] = mapped_column(
|
|
UUID(as_uuid=True),
|
|
ForeignKey("activity_runs.run_id", ondelete="CASCADE"),
|
|
nullable=False,
|
|
index=True,
|
|
)
|
|
type: Mapped[str] = mapped_column(Text, nullable=False)
|
|
params: Mapped[dict] = mapped_column(JSONB, nullable=False, default=dict)
|
|
status: Mapped[str] = mapped_column(Text, nullable=False, default="pending")
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), nullable=False, server_default=func.now()
|
|
)
|
|
|
|
|
|
class OpsRun(Base):
|
|
"""Claimable automation run instance (ACT-ADR-005 / ACTIVITY-WP-0026)."""
|
|
|
|
__tablename__ = "ops_runs"
|
|
|
|
id: Mapped[uuid.UUID] = mapped_column(
|
|
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
|
|
)
|
|
activity_definition_id: Mapped[uuid.UUID] = mapped_column(
|
|
UUID(as_uuid=True),
|
|
ForeignKey("activity_definitions.id", ondelete="RESTRICT"),
|
|
nullable=False,
|
|
index=True,
|
|
)
|
|
idempotency_key: Mapped[str] = mapped_column(Text, nullable=False, unique=True)
|
|
target_repo: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
title: Mapped[str] = mapped_column(Text, nullable=False)
|
|
description: Mapped[str] = mapped_column(Text, nullable=False, default="")
|
|
labels: Mapped[list] = mapped_column(JSONB, nullable=False, default=list)
|
|
priority: Mapped[str] = mapped_column(Text, nullable=False, default="medium")
|
|
state: Mapped[str] = mapped_column(Text, nullable=False, default="open", index=True)
|
|
claim_owner: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
lease_until: Mapped[datetime | None] = mapped_column(
|
|
DateTime(timezone=True), nullable=True, index=True
|
|
)
|
|
attempt: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
source_type: Mapped[str] = mapped_column(Text, nullable=False)
|
|
source_id: Mapped[str] = mapped_column(Text, nullable=False)
|
|
triggering_event_id: Mapped[str] = mapped_column(Text, nullable=False, index=True)
|
|
approach_hint: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
# ACT-ADR-006: authoritative execution-constellation selector. Legacy
|
|
# `approach_hint` above must never substitute for an absent/invalid one.
|
|
harness_profile_ref: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
# Attribution refs carried through from workforce/leadership vocabulary;
|
|
# activity-core does not author or interpret them.
|
|
execution_refs: Mapped[dict] = mapped_column(JSONB, nullable=False, default=dict)
|
|
result: Mapped[dict] = mapped_column(JSONB, nullable=False, default=dict)
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), nullable=False, server_default=func.now()
|
|
)
|
|
updated_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
nullable=False,
|
|
server_default=func.now(),
|
|
onupdate=func.now(),
|
|
)
|