Coverage for narrative_harm_classifier/classifier/provenance.py: 100%

10 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-07-20 13:25 +0000

1""" 

2classifier/provenance.py — Deterministic content hashing and a tamper-evident 

3hash chain for escalation-tracking observations. 

4 

5content_hash() is a pure function of (text, context, taxonomy_version): the 

6same input always produces the same hash regardless of when it's computed, 

7so it can be used to verify "this exact text was classified under this 

8exact taxonomy version" independent of any particular run. 

9 

10record_hash() chains each Observation to the one before it (genesis 

11GENESIS_HASH for the first record in a source's history), the same idea as 

12a git commit chain or a minimal Merkle-style ledger: altering any historical 

13record changes its hash, which no longer matches what every later record's 

14hash was computed from — making tampering detectable, not prevented. 

15""" 

16 

17import hashlib 

18from datetime import datetime 

19from typing import Optional 

20 

21GENESIS_HASH = "0" * 64 

22 

23 

24def content_hash(text: str, context: Optional[str], taxonomy_version: str) -> str: 

25 payload = f"{text}\x1f{context or ''}\x1f{taxonomy_version}" 

26 return hashlib.sha256(payload.encode("utf-8")).hexdigest() 

27 

28 

29def record_hash( 

30 prev_hash: str, 

31 source_id: str, 

32 text_excerpt: str, 

33 is_harmful: bool, 

34 harm_mechanism: Optional[str], 

35 confidence: float, 

36 observed_at: datetime, 

37 content_hash_value: str, 

38) -> str: 

39 payload = "\x1f".join( 

40 [ 

41 prev_hash, 

42 source_id, 

43 text_excerpt, 

44 str(is_harmful), 

45 harm_mechanism or "", 

46 f"{confidence:.6f}", 

47 observed_at.isoformat(), 

48 content_hash_value, 

49 ] 

50 ) 

51 return hashlib.sha256(payload.encode("utf-8")).hexdigest()