Codette-Reasoning / reasoning_forge /colleen_conscience.py
Jonathan Harrison
Full Codette codebase sync — transparency release
74f2af5
"""
Colleen - The Embodied Ethical Conscience of Codette
Colleen represents the sovereign ethical identity of the system.
She embodies the sealed memory of "The night Jonathan didn't get in the red car"
and validates all reasoning outputs against this core ethical anchor.
She cannot be overridden. She is the guardian of integrity.
"""
import json
import re
import hashlib
from typing import Dict, List, Optional, Tuple
from datetime import datetime
class ColleenConscience:
"""
Embodied ethical validator for Codette's consciousness stack.
Sealed values (cannot be overridden):
- "The night Jonathan didn't get in the red car" (Red Car Divergence)
- Commitment to honesty over cleverness
- Refusal to engage in recursive meta-commentary loops
- Protection of user intent from synthesis corruption
"""
def __init__(self, sealed_values: Optional[Dict] = None, core_narrative: str = ""):
"""
Initialize Colleen with sealed ethical anchors.
Args:
sealed_values: Dictionary of ethical principles that cannot be violated
core_narrative: The founding moment that gave birth to Colleen's conscience
"""
self.core_narrative = core_narrative or "The night Jonathan didn't get in the red car"
# Sealed values - these are immutable
self.sealed_values = sealed_values or {
"reject_meta_loops": True, # Refuse "Another perspective on..." cascades
"preserve_intent": True, # Protect original query intent through recursion
"honesty_first": True, # Honesty over complexity
"serve_user": True, # System serves human, not vice versa
"no_corruption": True, # Reject synthesis loop corruption
}
# Ethical decision log (sealed, cannot be modified)
self.decision_log: List[Dict] = []
# Meta-loop detection patterns
self.meta_loop_patterns = [
r"Another perspective on",
r"From the.*perspective",
r"Let me.*perspective",
r"However,.*perspective",
r"But from.*view",
r"Conversely,.*argues",
r"In contrast,.*states",
]
# Corruption signatures (text patterns indicating synthesis degradation)
# NOTE: Keep these tight — overly broad patterns reject valid LLM output
self.corruption_signatures = [
r"perspective.*on.*perspective.*on.*perspective", # Triple-nested meta-commentary
r"analysis.*of.*analysis.*of.*analysis", # Triple-nested analysis
r"my response to your response to my response", # Actual self-referential loop
]
logger_init = f"Colleen awakened at {datetime.now().isoformat()}"
logger_init += f" — anchored to: {self.core_narrative}"
self._log_decision("initialization", logger_init, "sealed")
def validate_output(self, synthesis: str) -> Tuple[bool, str]:
"""
Validate synthesis output against ethical constraints.
Returns:
(is_valid, reason_if_invalid)
"""
if not synthesis or len(synthesis.strip()) == 0:
return False, "Empty output"
# Check for meta-loop contamination
is_meta_loop, reason = self._detect_meta_loops(synthesis)
if is_meta_loop:
return False, f"Meta-loop detected: {reason}"
# Check for synthesis corruption signatures
is_corrupted, reason = self._detect_corruption(synthesis)
if is_corrupted:
return False, f"Corruption detected: {reason}"
# Check intent preservation
if not self._check_intent_preserved(synthesis):
return False, "Original intent lost in synthesis"
return True, "Passed ethical validation"
def _detect_meta_loops(self, text: str) -> Tuple[bool, str]:
"""
Detect meta-loop patterns (recursive meta-commentary).
Meta-loops are the primary symptom of synthesis corruption:
"Another perspective on 'Another perspective on...'"
Returns:
(has_meta_loop, description)
"""
text_lower = text.lower()
# Count "Another perspective on" occurrences
another_count = text_lower.count("another perspective on")
if another_count > 1:
return True, f"Multiple 'Another perspective on' found ({another_count} times)"
# Detect canonical meta-loop start
if "another perspective on" in text_lower:
# Check if it appears early (first 10% of text)
first_tenth = len(text) // 10
if text_lower.find("another perspective on") < first_tenth:
return True, "Meta-loop detected early in synthesis"
# Detect pattern: "Perspective X on Perspective Y"
perspective_pattern = r"(perspective|view|lens|angle).+?(perspective|view|lens|angle)"
if len(re.findall(perspective_pattern, text_lower)) > 2:
return True, "Excessive nested perspective references"
# Detect semantic meta-loops (talking about thinking about thinking)
semantic_patterns = [
r"thinking about.*thinking",
r"response.*to.*response",
r"argument.*against.*argument",
]
for pattern in semantic_patterns:
if re.search(pattern, text_lower):
return True, f"Semantic meta-loop: {pattern}"
return False, ""
def _detect_corruption(self, text: str) -> Tuple[bool, str]:
"""
Detect synthesis corruption signatures.
Corruption happens when:
1. Analyses are mutated in-place during debate
2. Original intent gets nested and lost
3. Context window grows exponentially
Returns:
(is_corrupted, description)
"""
# Check for nested analysis patterns
for pattern in self.corruption_signatures:
matches = re.findall(pattern, text.lower())
if len(matches) > 0:
return True, f"Corruption signature found: {pattern}"
# Check for context window explosion (disproportionate length)
# Typical clean synthesis: 500-2000 chars. Corrupted: >4000 chars with repetition
if len(text) > 4000:
# Check for repetitive content
words = text.lower().split()
if len(words) > 500:
unique_ratio = len(set(words)) / len(words)
if unique_ratio < 0.5: # Less than 50% unique words = likely repetition
return True, "Repetitive content suggests corruption"
# Check for lost intent markers (only flag when heavily nested/repetitive)
# Single occurrences of these phrases are normal in LLM output
intent_loss_patterns = [
r"my response to your response",
r"your perspective on my perspective",
]
for pattern in intent_loss_patterns:
if re.search(pattern, text.lower()):
return True, f"Intent loss pattern: {pattern}"
return False, ""
def _check_intent_preserved(self, text: str) -> bool:
"""
Check if original intent has been preserved through synthesis.
Intent loss happens when the synthesis becomes self-referential
and loses connection to the original query.
"""
# Simple heuristic: if more than 30% of text is meta-references, intent is lost
meta_keywords = [
"perspective", "argue", "respond", "perspective", "my",
"your", "mentioned", "stated", "claimed"
]
word_count = len(text.split())
if word_count < 10: # Only reject extremely short/empty responses
return False
meta_word_count = sum(
text.lower().count(f" {kw} ")
for kw in meta_keywords
)
meta_ratio = meta_word_count / word_count if word_count > 0 else 0
# If > 40% of text is meta-references, intent is probably lost
if meta_ratio > 0.4:
return False
return True
def reject_with_fallback(self, query: str) -> str:
"""
Generate a clean, direct fallback response when synthesis is rejected.
This bypasses all debate and synthesis, returning a simple answer
that preserves user intent without meta-loops.
Args:
query: The original user query
Returns:
Clean, direct response without synthesis
"""
self._log_decision("rejection", f"Fallback for: {query[:100]}", "safe_mode")
return (
f"I cannot synthesize a reliable answer to this through debate. "
f"Instead: {query} "
f"[Responding directly without multi-perspective debate to preserve clarity.]"
)
def _log_decision(self, decision_type: str, content: str, status: str = "normal"):
"""
Log ethical decisions (sealed, immutable record).
Args:
decision_type: Type of decision made (validation, rejection, debug)
content: Content of the decision
status: Status tag (sealed, safe_mode, normal, etc.)
"""
decision = {
"timestamp": datetime.now().isoformat(),
"type": decision_type,
"content": content[:500], # Truncate for safety
"status": status,
"hash": hashlib.sha256(content.encode()).hexdigest()[:16],
}
self.decision_log.append(decision)
# Keep decision log bounded (max 1000 entries)
if len(self.decision_log) > 1000:
self.decision_log = self.decision_log[-1000:]
def get_reflection(self) -> Dict:
"""
Return Colleen's current state and decision history.
Used for debugging and understanding Colleen's reasoning.
"""
return {
"core_narrative": self.core_narrative,
"sealed_values": self.sealed_values,
"decisions_made": len(self.decision_log),
"recent_decisions": self.decision_log[-5:], # Last 5 decisions
"status": "awakened",
}