fix(ac-0024): injective C identity via SHA-256 length-prefix digest + PostgreSQL migration/round-trip tests

Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-08-26 00:38:02 +00:00
committed by GitHub
co-authored by genichin
parent c246fca206
commit 21f8fdd4b6
7 changed files with 250 additions and 14 deletions
@@ -1,5 +1,5 @@
-- req-0007 / ac-0024: durable mismatch notification lifecycle state.
-- PK: identity_key (slot_id TEXT for B-case; "{instance_key}\x00C\x00{symbol}" for C-case).
-- PK: identity_key (slot_id TEXT for B-case; "C:<sha256_hex>" length-prefix-digest for C-case).
-- lifecycle_state: 'notified' | 'pending_resolved'.
-- ON CONFLICT DO UPDATE: replay-safe; re-running migration is a no-op.
CREATE TABLE IF NOT EXISTS reconcile_notification_state (
@@ -4,7 +4,7 @@ Tracks per-identity B/C-case mismatch notification fingerprints so that
duplicate alarms are suppressed across scheduler ticks and process restarts,
and pending resolved transitions can be retried.
PK: identity_key (slot_id for B-case; "{instance_key}\\x1fC\\x1f{symbol}" for C-case).
PK: identity_key (slot_id TEXT for B-case; "C:<sha256_hex>" length-prefix-digest for C-case).
lifecycle_state: 'notified' | 'pending_resolved'.
"""
from __future__ import annotations
+16 -4
View File
@@ -25,6 +25,7 @@ published via AlarmEmitter with durable deduplication through ReconcileNotificat
"""
from __future__ import annotations
import hashlib
import logging
import math
import re
@@ -105,12 +106,23 @@ def _normalize_legacy_cost_basis(value: object) -> tuple[float | None, str, str
# ---------------------------------------------------------------------------
def _c_identity_key(instance_key: str, symbol: str) -> str:
"""Stable C-case notification identity key (PostgreSQL TEXT-safe, collision-resistant).
"""Return an injective, PostgreSQL TEXT-safe key for a (instance_key, symbol) C-case pair.
Uses ASCII Unit Separator (\\x1f) which PostgreSQL TEXT accepts (only \\x00 is
forbidden) and which cannot appear in well-formed instance_key / symbol values.
Uses a length-prefixed SHA-256 hex digest so that *any* inputs — including
those containing separator characters — produce distinct keys with no
collision risk. The prefix ``C:`` makes the key case-code-scoped and
human-scannable in the DB; the digest is 64 hex chars (256-bit).
Injectivity: the raw string fed to SHA-256 is ``"<N>:<instance_key>:<symbol>"``
where ``N = len(instance_key)``. The leading ``N:`` uniquely delimits
``instance_key`` from the rest, so two distinct ``(instance_key, symbol)``
pairs always produce different raw strings (and thus different digests).
Example: _c_identity_key("a\\x1fC", "b") != _c_identity_key("a", "C\\x1fb")
"""
return f"{instance_key}\x1fC\x1f{symbol}"
raw = f"{len(instance_key)}:{instance_key}:{symbol}"
digest = hashlib.sha256(raw.encode()).hexdigest()
return f"C:{digest}"
def _build_alarm_fields(