@@ -987,6 +987,87 @@ def _require_unique_text(values: Iterable[str], *, label: str) -> None:
987987 seen .add (value )
988988
989989
990+ def _stable_sha256 (payload : Mapping [str , Any ]) -> str :
991+ """Return deterministic SHA-256 over a canonical JSON payload."""
992+
993+ encoded = json .dumps (payload , sort_keys = True , separators = ("," , ":" )).encode ("utf-8" )
994+ return hashlib .sha256 (encoded ).hexdigest ()
995+ def _probe_id (contract_artifact_id : str , obligation : WaveSixIxObligation ) -> str :
996+ """Return deterministic falsification-probe id for an IX obligation."""
997+
998+ return f"ix-obligation-probe:{ contract_artifact_id } :{ obligation .obligation_id } "
999+
1000+
1001+ def _sort_pressures_by_canonical_order (
1002+ pressures : Iterable [WaveSixIxObligationPressure ],
1003+ ) -> tuple [WaveSixIxObligationPressure , ...]:
1004+ """Return pressure records sorted by canonical IX obligation order."""
1005+
1006+ by_id : dict [str , WaveSixIxObligationPressure ] = {}
1007+ for pressure in pressures :
1008+ if pressure .obligation_id in by_id :
1009+ raise ValueError (
1010+ f"Duplicate IX obligation pressure: { pressure .obligation_id } "
1011+ )
1012+ by_id [pressure .obligation_id ] = pressure
1013+ return tuple (
1014+ by_id [obligation_id ]
1015+ for obligation_id in canonical_ix_cognition_obligation_ids ()
1016+ if obligation_id in by_id
1017+ )
1018+
1019+
1020+ def _require_exact_obligation_ids (obligation_ids : tuple [str , ...]) -> None :
1021+ """Require pressure coverage for every canonical IX cognition obligation."""
1022+
1023+ expected = set (canonical_ix_cognition_obligation_ids ())
1024+ actual = set (obligation_ids )
1025+ missing = tuple (
1026+ obligation_id
1027+ for obligation_id in canonical_ix_cognition_obligation_ids ()
1028+ if obligation_id not in actual
1029+ )
1030+ extra = tuple (sorted (actual - expected ))
1031+ if missing :
1032+ raise ValueError (f"Missing IX obligation pressure: { missing [0 ]} " )
1033+ if extra :
1034+ raise ValueError (f"Unknown IX obligation pressure: { extra [0 ]} " )
1035+
1036+
1037+ def _unique_preserving_order (values : Iterable [str ]) -> tuple [str , ...]:
1038+ """Return unique text values while preserving first-seen order."""
1039+
1040+ seen : set [str ] = set ()
1041+ unique : list [str ] = []
1042+ for value in values :
1043+ if value not in seen :
1044+ unique .append (value )
1045+ seen .add (value )
1046+ return tuple (unique )
1047+
1048+
1049+ def _require_non_empty (value : str , label : str ) -> str :
1050+ """Return stripped text or raise when empty."""
1051+
1052+ normalized = value .strip ()
1053+ if not normalized :
1054+ raise ValueError (f"{ label } must not be empty." )
1055+ return normalized
1056+
1057+
1058+ def _require_sha256 (value : str , label : str ) -> str :
1059+ """Require a deterministic SHA-256 fingerprint value."""
1060+
1061+ normalized = _require_non_empty (value , label )
1062+ if len (normalized ) != 64 :
1063+ raise ValueError (f"{ label } must be a SHA-256 fingerprint." )
1064+ try :
1065+ int (normalized , 16 )
1066+ except ValueError as exc :
1067+ raise ValueError (f"{ label } must be hexadecimal." ) from exc
1068+ return normalized
1069+
1070+
9901071def _stable_sha256 (payload : Mapping [str , Any ]) -> str :
9911072 """Return deterministic SHA-256 over a canonical JSON payload."""
9921073
0 commit comments