Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/migration_classes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ migrations:
forward/nodes/node_projection_delegation/0043z_preflight_delegation_shadow_comparisons.sql: forward-only # DROP; ALTER TABLE ALTER COLUMN; ALTER TABLE ADD PRIMARY KEY; ALTER TABLE ADD UNIQUE; ALTER TABLE ENABLE ROW LE
forward/nodes/node_projection_delegation/0044_restore_delegation_shadow_comparisons.sql: forward-only # ALTER TABLE ALTER COLUMN; DROP; ALTER TABLE ENABLE ROW LEVEL; ALTER TABLE FORCE ROW LEVEL
forward/nodes/node_projection_delegation/0045_terminal_construction_outcome_metrics.sql: forward-only # CREATE OR REPLACE (replaces an existing object); ALTER VIEW
forward/nodes/node_projection_delegation/0046_delegation_events_cohort_key.sql: expand-only
forward/nodes/node_projection_delegation_inference_response/0001_create_projection_delegation_inference_response_text.sql: forward-only # EXECUTE; ALTER TABLE ADD CONSTRAINT PROJECTION_DELEGATION_INFERENCE_RESPONSE_TEXT_PKEY; ALTER TABLE ADD CONSTR
forward/nodes/node_projection_delegation_inference_response/0002_inference_response_text_tenant_rekey.sql: forward-only # DROP; ALTER TABLE DROP CONSTRAINT; UPDATE
forward/nodes/node_projection_delegation_inference_response/0003_inference_response_text_rls_tenant_isolation.sql: forward-only # ALTER TABLE ENABLE ROW LEVEL; ALTER TABLE FORCE ROW LEVEL; DROP
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,3 +210,4 @@ nodes/node_projection_delegation/0044_restore_delegation_shadow_comparisons.sql
nodes/node_projection_ci_attempt_outcome/0000_create_ci_attempt_outcome.sql node:node_projection_ci_attempt_outcome node:node_projection_ci_attempt_outcome omninode_internal node:node_projection_ci_attempt_outcome:0000_create_ci_attempt_outcome.sql a1e25857aadeb4590f75762eb2d8c5d1e445c05bc390e4719437e5e45762a100
nodes/node_projection_ci_attempt_outcome/0001_grant_omninode_runtime_ci_attempt_outcome.sql node:node_projection_ci_attempt_outcome node:node_projection_ci_attempt_outcome omninode_internal node:node_projection_ci_attempt_outcome:0001_grant_omninode_runtime_ci_attempt_outcome.sql 011ab13aa68b8bb113d355fbb12fc927ff7fed000f0800ecb82747a577f8465e
nodes/node_projection_delegation/0045_terminal_construction_outcome_metrics.sql node:node_projection_delegation node:node_projection_delegation tenant node:node_projection_delegation:0045_terminal_construction_outcome_metrics.sql 990973fe3d63d9e424789adee322190e8f4b6228a9357e4366df80545a8648e1
nodes/node_projection_delegation/0046_delegation_events_cohort_key.sql node:node_projection_delegation node:node_projection_delegation tenant node:node_projection_delegation:0046_delegation_events_cohort_key.sql 933069368adc0ce83a627a859d5360e7970fba91bdfbd73848fde3820199dd1c
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
-- OMN-18930 (K3 of OMN-18925): every delegation_events row carries the
-- delegation cohort key its terminal carried -- every dimension that must be
-- equal before two runs' outcomes are compared -- so a comparison is made
-- between rows, and two rows differ only where their keys differ.
--
-- cohort_key the complete key as the terminal carried it (JSONB)
-- cohort_key_sha256 SHA-256 of that key, sorted members, compact separators
-- cohort_key_refusal why a key the terminal carried was refused (incomplete
-- or malformed); the key and its digest are then NULL
--
-- All three stay NULL on a row whose terminal carried no key, and on every row
-- written before this migration. Additive and nullable: older writers never
-- name these columns and keep working.
BEGIN;

ALTER TABLE delegation_events
ADD COLUMN IF NOT EXISTS cohort_key JSONB,
ADD COLUMN IF NOT EXISTS cohort_key_sha256 TEXT,
ADD COLUMN IF NOT EXISTS cohort_key_refusal TEXT;

COMMIT;
102 changes: 102 additions & 0 deletions tests/unit/migrations/test_omn18930_cohort_key_vendor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# SPDX-FileCopyrightText: 2025 OmniNode.ai Inc.
# SPDX-License-Identifier: MIT
"""OMN-18930 vendor identity for the delegation_events cohort-key migration.

K3 of OMN-18925, projection half. omnimarket's node_projection_delegation
migration 0046 adds three nullable columns to ``delegation_events`` so every
row carries the delegation cohort key its terminal carried. It is vendored here
FIRST, ahead of the omnimarket source, per the node-migration vendor-parity
ordering. The real-Postgres proof of the columns and both writers lives beside
the source in omnimarket.
"""

from __future__ import annotations

import hashlib
import re
from pathlib import Path

import pytest
import yaml

pytestmark = pytest.mark.unit

_ROOT = Path(__file__).resolve().parents[3]
_FORWARD = _ROOT / "docker" / "migrations" / "forward"
_VENDOR = _FORWARD / "nodes" / "node_projection_delegation"
_MANIFEST = _FORWARD / "_ledger" / "application-migrations.tsv"
_CLASSES = _ROOT / "config" / "migration_classes.yaml"
_FILENAME = "0046_delegation_events_cohort_key.sql"
_SHA256 = "933069368adc0ce83a627a859d5360e7970fba91bdfbd73848fde3820199dd1c"


def _manifest_rows() -> dict[str, list[str]]:
return {
row.split("\t", 1)[0]: row.split("\t")
for row in _MANIFEST.read_text(encoding="utf-8").splitlines()
if row.strip()
}


def test_vendor_bytes_and_manifest_binding_are_exact() -> None:
artifact_path = f"nodes/node_projection_delegation/{_FILENAME}"
assert hashlib.sha256((_VENDOR / _FILENAME).read_bytes()).hexdigest() == _SHA256
assert _manifest_rows()[artifact_path] == [
artifact_path,
"node:node_projection_delegation",
"node:node_projection_delegation",
"tenant",
f"node:node_projection_delegation:{_FILENAME}",
_SHA256,
]


def test_migration_is_declared_expand_only_and_is_purely_additive() -> None:
"""The class line matches the bytes: three nullable columns, nothing else."""
classes = yaml.safe_load(_CLASSES.read_text(encoding="utf-8"))["migrations"]
assert classes[f"forward/nodes/node_projection_delegation/{_FILENAME}"] == (
"expand-only"
)
sql = (_VENDOR / _FILENAME).read_text(encoding="utf-8")
statements = "\n".join(
line for line in sql.splitlines() if not line.lstrip().startswith("--")
)
added = re.findall(r"ADD COLUMN IF NOT EXISTS (\w+) (\w+)", statements)
assert added == [
("cohort_key", "JSONB"),
("cohort_key_sha256", "TEXT"),
("cohort_key_refusal", "TEXT"),
]
for forbidden in ("NOT NULL", "DROP", "UPDATE", "DELETE", "CREATE OR REPLACE"):
assert forbidden not in statements.upper(), forbidden


def test_migration_passes_the_application_database_sql_gate() -> None:
"""0046 clears the OMN-15361 gate on every shipped profile."""
from omnibase_infra.topology.application_database import load_topology_profile
from omnibase_infra.validation.application_database_domain_enforcement import (
lint_application_database_sql,
)

sql = (_VENDOR / _FILENAME).read_text(encoding="utf-8")
for profile in ("local", "onex-dev", "onex-prod", "stability-test"):
violations = lint_application_database_sql(sql, load_topology_profile(profile))
assert violations == (), f"{profile}: {violations}"


def test_the_linter_is_live_positive_control() -> None:
"""A zero from the linter means something only if it can return non-zero.

The same file with its target unqualified away into a public-schema write
of an unknown relation must not lint clean, or the test above proves
nothing.
"""
from omnibase_infra.topology.application_database import load_topology_profile
from omnibase_infra.validation.application_database_domain_enforcement import (
lint_application_database_sql,
)

sql = (_VENDOR / _FILENAME).read_text(encoding="utf-8")
broken = sql.replace("ALTER TABLE delegation_events", "ALTER TABLE public.pg_class")
assert broken != sql
assert lint_application_database_sql(broken, load_topology_profile("local")) != ()
Original file line number Diff line number Diff line change
Expand Up @@ -803,7 +803,12 @@ def test_checked_in_manifest_is_exact_and_all_blockers_are_explicit() -> None:
# correction must be a first-class vendored application migration. Its
# exact source bytes and manifest binding are pinned separately by
# test_omn19013_terminal_construction_vendor.py.
assert len(result.declarations) == 212
#
# 212 -> 213 for OMN-18930: node_projection_delegation/0046 adds the three
# nullable delegation_events cohort-key columns (additive, expand-only).
# Vendored FIRST, ahead of the omnimarket source, per the vendor-parity
# ordering.
assert len(result.declarations) == 213
assert result.blocked == ()
assert len(result.legacy_node_declarations) == 2
#
Expand Down
Loading