From d3b904b3d445b0898abcf2e8ed868372e00ce97c Mon Sep 17 00:00:00 2001 From: Anthony Volk <14987227+anth-volk@users.noreply.github.com> Date: Sun, 27 Sep 2026 18:41:14 +0400 Subject: [PATCH] Synchronize Stage 12 observability column --- .../stage12_execution.py | 2 ++ .../policyengine_stage12_persistence/tables.py | 1 + .../validation.py | 1 + .../tests/test_store.py | 10 ++++++++++ .../tests/test_tables.py | 16 +++++++++++----- 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/libs/policyengine-simulation-contract/src/policyengine_simulation_contract/stage12_execution.py b/libs/policyengine-simulation-contract/src/policyengine_simulation_contract/stage12_execution.py index 6a6f3ad15..75f1d2d98 100644 --- a/libs/policyengine-simulation-contract/src/policyengine_simulation_contract/stage12_execution.py +++ b/libs/policyengine-simulation-contract/src/policyengine_simulation_contract/stage12_execution.py @@ -20,6 +20,7 @@ ) ContractText = Annotated[str, Field(min_length=1, max_length=255)] +ObservabilityId = Annotated[str, Field(min_length=1, max_length=36)] # ``evaluation_id`` is the already-deployed physical database and wire field # for the temporary comparison-run identifier. Stage 14 owns its removal. Sha256Digest = Annotated[str, Field(pattern=r"^[0-9a-f]{64}$")] @@ -525,6 +526,7 @@ class ComparisonReportRecord(StrictContractModel): environment: ContractText calculation_flow: ContractText originating_request_id: ContractText + observability_id: ObservabilityId | None = None production_identity: ContractText incumbent_execution_id: ContractText | None = None worker_version: ContractText diff --git a/libs/policyengine-stage12-persistence/src/policyengine_stage12_persistence/tables.py b/libs/policyengine-stage12-persistence/src/policyengine_stage12_persistence/tables.py index 3a889f86d..d3169fa02 100644 --- a/libs/policyengine-stage12-persistence/src/policyengine_stage12_persistence/tables.py +++ b/libs/policyengine-stage12-persistence/src/policyengine_stage12_persistence/tables.py @@ -92,6 +92,7 @@ Column("environment", String(255), nullable=False), Column("calculation_flow", String(255), nullable=False), Column("originating_request_id", String(255), nullable=False), + Column("observability_id", String(36)), Column("production_identity", String(255), nullable=False), Column("incumbent_execution_id", String(255)), Column("worker_version", String(255), nullable=False), diff --git a/libs/policyengine-stage12-persistence/src/policyengine_stage12_persistence/validation.py b/libs/policyengine-stage12-persistence/src/policyengine_stage12_persistence/validation.py index c295af851..dbaa5035c 100644 --- a/libs/policyengine-stage12-persistence/src/policyengine_stage12_persistence/validation.py +++ b/libs/policyengine-stage12-persistence/src/policyengine_stage12_persistence/validation.py @@ -229,6 +229,7 @@ def _exercise_runtime_dml(connection: Connection, *, environment: str) -> None: "environment": environment, "calculation_flow": "infrastructure_validation", "originating_request_id": f"validation-{evaluation_id}", + "observability_id": None, "production_identity": f"validation-{evaluation_id}", "incumbent_execution_id": None, "worker_version": "validation", diff --git a/libs/policyengine-stage12-persistence/tests/test_store.py b/libs/policyengine-stage12-persistence/tests/test_store.py index ec7ecec2c..271d35ac3 100644 --- a/libs/policyengine-stage12-persistence/tests/test_store.py +++ b/libs/policyengine-stage12-persistence/tests/test_store.py @@ -29,6 +29,7 @@ def _report() -> ComparisonReportRecord: environment="staging", calculation_flow="economy", originating_request_id="request-1", + observability_id="00000000-0000-0000-0000-000000000003", production_identity="job-1", incumbent_execution_id="job-1", worker_version="5.2.0", @@ -125,6 +126,15 @@ def test_create_and_read_report_use_sqlalchemy_statements_only() -> None: assert result.created is True assert result.record == report assert len(engine.connection.statements) == 1 + assert "observability_id" in str(engine.connection.statements[0]) + + +def test_report_observability_identifier_is_limited_to_36_characters() -> None: + values = _report().model_dump(mode="python") + values["observability_id"] = "x" * 37 + + with pytest.raises(ValueError, match="at most 36 characters"): + ComparisonReportRecord.model_validate(values) def test_report_identity_conflict_resolves_existing_row() -> None: diff --git a/libs/policyengine-stage12-persistence/tests/test_tables.py b/libs/policyengine-stage12-persistence/tests/test_tables.py index b10cb7b88..b95d1aefb 100644 --- a/libs/policyengine-stage12-persistence/tests/test_tables.py +++ b/libs/policyengine-stage12-persistence/tests/test_tables.py @@ -15,7 +15,7 @@ comparison_simulations, metadata, ) -from sqlalchemy import ForeignKeyConstraint, UniqueConstraint +from sqlalchemy import ForeignKeyConstraint, String, UniqueConstraint def test_mappings_cover_every_cross_service_record_field() -> None: @@ -25,6 +25,14 @@ def test_mappings_cover_every_cross_service_record_field() -> None: assert comparison_simulations.schema == "public" +def test_report_observability_identifier_matches_api_owned_schema() -> None: + column = comparison_reports.c.observability_id + + assert column.nullable is True + assert isinstance(column.type, String) + assert column.type.length == 36 + + def test_mappings_retain_identity_and_parent_constraints() -> None: report_unique = next( constraint @@ -60,13 +68,11 @@ def test_mappings_retain_identity_and_parent_constraints() -> None: ) assert parent_key.ondelete == "CASCADE" assert parent_key.name == ( - "fk_stage12_evaluation_simulations_evaluation_id_" - "stage12_evaluation_reports" + "fk_stage12_evaluation_simulations_evaluation_id_stage12_evaluation_reports" ) assert comparison_reports.primary_key.name == "pk_stage12_evaluation_reports" assert ( - comparison_simulations.primary_key.name - == "pk_stage12_evaluation_simulations" + comparison_simulations.primary_key.name == "pk_stage12_evaluation_simulations" )