Files
ss-tools/backend/alembic/versions/b2a3c4d5e6f7_add_agent_lifecycle_events.py
busya 20071b8c7a security: fullstack hardening — task ownership, mapping validation, API-key scoping, test fixes
Backend:
- Add validate_mapping_database_ownership() to verify source/target UUIDs
  belong to declared environments before persisting mappings (mappings.py)
- Add API-key environment scoping to get_mappings (filter) and
  suggest_mappings_api (enforce) (mappings.py)
- Add user_id Column to TaskRecord model + Alembic migration (task.py)
- Persist task.user_id on save, restore on load (persistence.py)
- Wire current_user.id into migrate_dashboards + backup_dashboards
  task creation (_action_routes.py)
- Fix test_migration_routes.py: module-level patch leak → autouse fixture,
  SupersetClient→AsyncSupersetClient, AsyncMock for sync_environment/run
- Fix 7 Pydantic serializer warnings: 'PENDING'→TaskStatus.PENDING
  in test_tasks.py + import TaskStatus

Frontend:
- Deepen isDryRunResult(): validate selection field, risk.items entries
  (all 5 fields), and diff object uuids individually (ExecutorModel.svelte.ts)

Prior work included: task password redaction, resume ownership checks,
canonical dry-run DTO alignment, migration UI callback fixes, credential
exposure reduction, assistant dry-run await fix.
2026-07-15 23:02:23 +03:00

77 lines
3.2 KiB
Python

# #region Alembic.AddAgentLifecycleEvents [C:3] [TYPE Module] [SEMANTICS alembic,agent,lifecycle,audit]
# @ingroup Alembic
# @BRIEF Add agent_lifecycle_events table for Phase 3 durable agent lifecycle audit.
# @LAYER Database
# @RELATION DEPENDS_ON -> [Models.Agent.AgentLifecycleEvent]
# @INVARIANT table has composite indexes for common query patterns (user+type+created, conv+type+created).
# @RATIONALE Immutable, indexed events make trace/conversation diagnostics queryable without
# keeping raw prompts or tool output in application logs.
# @REJECTED Reusing agent_messages was rejected — message content has a separate retention and
# privacy contract and cannot represent request/tool lifecycle boundaries safely.
"""add agent_lifecycle_events table
Revision ID: b2a3c4d5e6f7
Revises: 7eaf84b7f6be
Create Date: 2026-07-15 10:00:00.000000
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = "b2a3c4d5e6f7"
down_revision: Union[str, Sequence[str], None] = "7eaf84b7f6be"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.create_table(
"agent_lifecycle_events",
sa.Column("id", sa.String(), nullable=False),
sa.Column("trace_id", sa.String(), nullable=False, index=True),
sa.Column("conversation_id", sa.String(), nullable=False, index=True),
sa.Column("user_id", sa.String(), nullable=False, index=True),
sa.Column("environment_id", sa.String(), nullable=True, index=True),
sa.Column("event_type", sa.String(), nullable=False, index=True),
sa.Column("tool_name", sa.String(), nullable=True, index=True),
sa.Column("status", sa.String(), nullable=True, index=True),
# Store UTC timestamps explicitly; the model normalizes values to UTC
# before serialization, independent of the database session timezone.
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("elapsed_ms", sa.Float(), nullable=True),
sa.Column("payload", sa.JSON(), nullable=True),
sa.Column("error_code", sa.String(), nullable=True, index=True),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(
"ix_agent_lifecycle_events_user_type_created",
"agent_lifecycle_events",
["user_id", "event_type", "created_at"],
unique=False,
)
op.create_index(
"ix_agent_lifecycle_events_conv_type_created",
"agent_lifecycle_events",
["conversation_id", "event_type", "created_at"],
unique=False,
)
op.create_index(
"ix_agent_lifecycle_events_created",
"agent_lifecycle_events",
["created_at"],
unique=False,
)
def downgrade() -> None:
op.drop_index("ix_agent_lifecycle_events_created", table_name="agent_lifecycle_events")
op.drop_index("ix_agent_lifecycle_events_conv_type_created", table_name="agent_lifecycle_events")
op.drop_index("ix_agent_lifecycle_events_user_type_created", table_name="agent_lifecycle_events")
op.drop_table("agent_lifecycle_events")
# #endregion Alembic.AddAgentLifecycleEvents