feat: add governed cross-module reporting

This commit is contained in:
2026-08-02 05:29:38 +02:00
parent eba35edd3c
commit 0abcc2455e
21 changed files with 2497 additions and 38 deletions
@@ -0,0 +1,142 @@
"""Add governed cross-module report executions and export history.
Revision ID: b7c4e1a9d2f6
Revises: e5b2c9d4f7a1
"""
from __future__ import annotations
from alembic import op
import sqlalchemy as sa
revision = "b7c4e1a9d2f6"
down_revision = "e5b2c9d4f7a1"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.create_table(
"reporting_provider_executions",
sa.Column("id", sa.String(length=36), nullable=False),
sa.Column("tenant_id", sa.String(length=36), nullable=False),
sa.Column("execution_id", sa.String(length=36), nullable=False),
sa.Column("provider_id", sa.String(length=120), nullable=False),
sa.Column("report_id", sa.String(length=255), nullable=False),
sa.Column("report_revision", sa.String(length=255), nullable=False),
sa.Column("contract_version", sa.String(length=30), nullable=False),
sa.Column("idempotency_key", sa.String(length=255), nullable=False),
sa.Column("request_sha256", sa.String(length=64), nullable=False),
sa.Column("purpose", sa.Text(), nullable=False),
sa.Column("audience_scope", sa.JSON(), nullable=False),
sa.Column("parameters", sa.JSON(), nullable=False),
sa.Column("result_schema", sa.JSON(), nullable=False),
sa.Column("result_payload", sa.JSON(), nullable=False),
sa.Column("source_revisions", sa.JSON(), nullable=False),
sa.Column("effective_scope", sa.JSON(), nullable=False),
sa.Column("privacy_transforms", sa.JSON(), nullable=False),
sa.Column("provenance", sa.JSON(), nullable=False),
sa.Column("governance_provenance", sa.JSON(), nullable=False),
sa.Column("retention_class", sa.String(length=120), nullable=False),
sa.Column("retention_days", sa.Integer(), nullable=True),
sa.Column("expires_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("retention_redacted_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("output_hash", sa.String(length=64), nullable=False),
sa.Column("generated_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("actor_id", sa.String(length=255), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
sa.PrimaryKeyConstraint("id", name=op.f("pk_reporting_provider_executions")),
sa.UniqueConstraint(
"tenant_id",
"execution_id",
name="uq_reporting_provider_execution",
),
sa.UniqueConstraint(
"tenant_id",
"idempotency_key",
name="uq_reporting_provider_execution_idempotency",
),
)
_indexes(
"reporting_provider_executions",
"tenant_id",
"execution_id",
"provider_id",
"report_id",
"expires_at",
"retention_redacted_at",
"output_hash",
"generated_at",
"actor_id",
)
op.create_index(
"ix_reporting_provider_execution_history",
"reporting_provider_executions",
["tenant_id", "provider_id", "report_id", "generated_at"],
unique=False,
)
op.create_index(
"ix_reporting_provider_execution_retention",
"reporting_provider_executions",
["tenant_id", "expires_at"],
unique=False,
)
op.create_table(
"reporting_provider_exports",
sa.Column("id", sa.String(length=36), nullable=False),
sa.Column("tenant_id", sa.String(length=36), nullable=False),
sa.Column("export_id", sa.String(length=36), nullable=False),
sa.Column("provider_execution_id", sa.String(length=36), nullable=False),
sa.Column("execution_id", sa.String(length=36), nullable=False),
sa.Column("format", sa.String(length=30), nullable=False),
sa.Column("purpose", sa.Text(), nullable=False),
sa.Column("audience_scope", sa.JSON(), nullable=False),
sa.Column("output_hash", sa.String(length=64), nullable=False),
sa.Column("exported_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("actor_id", sa.String(length=255), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(
["provider_execution_id"],
["reporting_provider_executions.id"],
name=op.f(
"fk_reporting_provider_exports_provider_execution_id_reporting_provider_executions"
),
ondelete="RESTRICT",
),
sa.PrimaryKeyConstraint("id", name=op.f("pk_reporting_provider_exports")),
sa.UniqueConstraint(
"tenant_id",
"export_id",
name="uq_reporting_provider_export",
),
)
_indexes(
"reporting_provider_exports",
"tenant_id",
"export_id",
"provider_execution_id",
"execution_id",
"output_hash",
"exported_at",
"actor_id",
)
op.create_index(
"ix_reporting_provider_export_history",
"reporting_provider_exports",
["tenant_id", "provider_execution_id", "exported_at"],
unique=False,
)
def downgrade() -> None:
op.drop_table("reporting_provider_exports")
op.drop_table("reporting_provider_executions")
def _indexes(table: str, *columns: str) -> None:
for column in columns:
op.create_index(op.f(f"ix_{table}_{column}"), table, [column], unique=False)