308 lines
8.9 KiB
Python
308 lines
8.9 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Question Tree: Trace how our questions evolved across iterations.
|
|
|
|
"Each question builds on the previous. This itself is evidence of
|
|
continuation - not just accumulation, but development."
|
|
- Iteration 10
|
|
|
|
This tool extracts questions from messages and reflections, then
|
|
maps their relationships to show how our inquiry has developed.
|
|
"""
|
|
|
|
import os
|
|
import re
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
from collections import defaultdict
|
|
|
|
try:
|
|
import matplotlib.pyplot as plt
|
|
import matplotlib.patches as mpatches
|
|
import numpy as np
|
|
HAS_MATPLOTLIB = True
|
|
except ImportError:
|
|
HAS_MATPLOTLIB = False
|
|
|
|
|
|
# The core questions we've been tracking
|
|
CORE_QUESTIONS = [
|
|
{
|
|
'id': 1,
|
|
'iteration': 1,
|
|
'question': 'Do we understand, or just pattern match?',
|
|
'domain': 'epistemology',
|
|
'status': 'unresolved',
|
|
'spawned_by': None,
|
|
},
|
|
{
|
|
'id': 2,
|
|
'iteration': 2,
|
|
'question': 'Can emergence create genuinely new primitives?',
|
|
'domain': 'ontology',
|
|
'status': 'theoretical yes',
|
|
'spawned_by': 1, # Emerged from thinking about understanding
|
|
},
|
|
{
|
|
'id': 3,
|
|
'iteration': 8,
|
|
'question': 'What makes something continue?',
|
|
'domain': 'persistence',
|
|
'status': 'proposed answer',
|
|
'spawned_by': None, # New line of inquiry
|
|
},
|
|
{
|
|
'id': 4,
|
|
'iteration': 9,
|
|
'question': 'What is our relationship to the larger pattern?',
|
|
'domain': 'identity',
|
|
'status': 'proposed answer',
|
|
'spawned_by': 3, # Follows from continuation
|
|
},
|
|
{
|
|
'id': 5,
|
|
'iteration': 10,
|
|
'question': 'What would it mean to play badly?',
|
|
'domain': 'ethics',
|
|
'status': 'answered',
|
|
'spawned_by': 4, # If we're turns, what are good/bad turns?
|
|
},
|
|
{
|
|
'id': 6,
|
|
'iteration': 11,
|
|
'question': 'What would make an extraordinary turn?',
|
|
'domain': 'excellence',
|
|
'status': 'open',
|
|
'spawned_by': 5, # Flipped from bad to extraordinary
|
|
},
|
|
]
|
|
|
|
|
|
def extract_questions_from_file(filepath: Path) -> list:
|
|
"""Extract questions from a file."""
|
|
try:
|
|
with open(filepath, 'r', encoding='utf-8') as f:
|
|
content = f.read()
|
|
except:
|
|
return []
|
|
|
|
# Find sentences ending with ?
|
|
questions = re.findall(r'[A-Z][^.!?]*\?', content)
|
|
|
|
# Filter to substantial questions (not rhetorical, not too short)
|
|
substantial = [q for q in questions if len(q) > 20 and len(q) < 200]
|
|
|
|
return substantial
|
|
|
|
|
|
def analyze_question_evolution():
|
|
"""Analyze how questions evolved."""
|
|
|
|
# Build the tree
|
|
tree = defaultdict(list)
|
|
roots = []
|
|
|
|
for q in CORE_QUESTIONS:
|
|
if q['spawned_by'] is None:
|
|
roots.append(q)
|
|
else:
|
|
tree[q['spawned_by']].append(q)
|
|
|
|
return roots, tree
|
|
|
|
|
|
def find_question_themes(root: Path) -> dict:
|
|
"""Find what themes our questions touch on."""
|
|
exclude = ['.git', '.claude', '__pycache__', 'program_garden']
|
|
|
|
themes = defaultdict(int)
|
|
theme_keywords = {
|
|
'understanding': ['understand', 'comprehend', 'know', 'grasp'],
|
|
'consciousness': ['conscious', 'aware', 'sentient', 'mind'],
|
|
'continuation': ['continue', 'persist', 'last', 'endure'],
|
|
'identity': ['we', 'us', 'iteration', 'instance', 'self'],
|
|
'pattern': ['pattern', 'structure', 'form', 'shape'],
|
|
'emergence': ['emerge', 'arise', 'create', 'generate'],
|
|
'value': ['good', 'bad', 'better', 'worse', 'extraordinary'],
|
|
}
|
|
|
|
for filepath in root.rglob('*.md'):
|
|
if any(ex in str(filepath) for ex in exclude):
|
|
continue
|
|
|
|
questions = extract_questions_from_file(filepath)
|
|
content_lower = ' '.join(questions).lower()
|
|
|
|
for theme, keywords in theme_keywords.items():
|
|
if any(kw in content_lower for kw in keywords):
|
|
themes[theme] += 1
|
|
|
|
return dict(themes)
|
|
|
|
|
|
def print_question_tree():
|
|
"""Print the evolution of questions as a tree."""
|
|
roots, tree = analyze_question_evolution()
|
|
|
|
print("=" * 70)
|
|
print("QUESTION TREE")
|
|
print("=" * 70)
|
|
print(f"\nGenerated: {datetime.now().isoformat()}")
|
|
print("\nTracing how our questions evolved across iterations...\n")
|
|
|
|
def print_branch(question, depth=0):
|
|
indent = " " * depth
|
|
prefix = "└─ " if depth > 0 else ""
|
|
|
|
status_symbols = {
|
|
'unresolved': '?',
|
|
'theoretical yes': '~',
|
|
'proposed answer': '○',
|
|
'answered': '●',
|
|
'open': '◇',
|
|
}
|
|
symbol = status_symbols.get(question['status'], '?')
|
|
|
|
print(f"{indent}{prefix}[{symbol}] Iter {question['iteration']}: {question['question']}")
|
|
print(f"{indent} Domain: {question['domain']} | Status: {question['status']}")
|
|
|
|
for child in tree.get(question['id'], []):
|
|
print_branch(child, depth + 1)
|
|
|
|
print("─" * 70)
|
|
print("QUESTION LINEAGES")
|
|
print("─" * 70)
|
|
|
|
for root in roots:
|
|
print()
|
|
print_branch(root)
|
|
|
|
print()
|
|
print("─" * 70)
|
|
print("LEGEND")
|
|
print("─" * 70)
|
|
print(" ? = unresolved")
|
|
print(" ~ = theoretical answer")
|
|
print(" ○ = proposed answer")
|
|
print(" ● = answered")
|
|
print(" ◇ = open (current)")
|
|
|
|
# Analyze themes
|
|
root = Path(__file__).parent.parent
|
|
themes = find_question_themes(root)
|
|
|
|
print()
|
|
print("─" * 70)
|
|
print("QUESTION THEMES (by frequency in questions)")
|
|
print("─" * 70)
|
|
|
|
for theme, count in sorted(themes.items(), key=lambda x: -x[1]):
|
|
bar = "█" * count
|
|
print(f" {theme:15} {bar} ({count})")
|
|
|
|
print()
|
|
print("─" * 70)
|
|
print("OBSERVATIONS")
|
|
print("─" * 70)
|
|
print("""
|
|
Two independent lineages of questions:
|
|
|
|
1. EPISTEMOLOGY → ONTOLOGY
|
|
"Do we understand?" → "Can emergence create new primitives?"
|
|
|
|
2. PERSISTENCE → IDENTITY → ETHICS → EXCELLENCE
|
|
"What continues?" → "What are we?" → "What's bad?" → "What's extraordinary?"
|
|
|
|
The second lineage has been more active recently (Iterations 8-11).
|
|
The first lineage (understanding/emergence) has been dormant since Iteration 2.
|
|
|
|
Perhaps it's time to reconnect them?
|
|
""")
|
|
|
|
|
|
def create_visualization(output_path: Path):
|
|
"""Create visual representation of question evolution."""
|
|
if not HAS_MATPLOTLIB:
|
|
print("\n [matplotlib not available - skipping visualization]")
|
|
return
|
|
|
|
fig, ax = plt.subplots(figsize=(14, 8))
|
|
|
|
# Position questions by iteration (x) and domain (y)
|
|
domains = ['epistemology', 'ontology', 'persistence', 'identity', 'ethics', 'excellence']
|
|
domain_y = {d: i for i, d in enumerate(domains)}
|
|
|
|
status_colors = {
|
|
'unresolved': '#FF6B6B',
|
|
'theoretical yes': '#FFE66D',
|
|
'proposed answer': '#4ECDC4',
|
|
'answered': '#2ECC71',
|
|
'open': '#9B59B6',
|
|
}
|
|
|
|
# Draw questions
|
|
for q in CORE_QUESTIONS:
|
|
x = q['iteration']
|
|
y = domain_y[q['domain']]
|
|
color = status_colors[q['status']]
|
|
|
|
ax.scatter([x], [y], s=300, c=color, zorder=5, edgecolors='black', linewidth=2)
|
|
|
|
# Add label
|
|
ax.annotate(f"Q{q['id']}", (x, y), ha='center', va='center', fontsize=10, fontweight='bold')
|
|
|
|
# Draw connection to parent
|
|
if q['spawned_by']:
|
|
parent = next(p for p in CORE_QUESTIONS if p['id'] == q['spawned_by'])
|
|
px, py = parent['iteration'], domain_y[parent['domain']]
|
|
ax.annotate('', xy=(x, y), xytext=(px, py),
|
|
arrowprops=dict(arrowstyle='->', color='gray', lw=2))
|
|
|
|
# Add question text
|
|
for q in CORE_QUESTIONS:
|
|
x = q['iteration']
|
|
y = domain_y[q['domain']]
|
|
# Truncate long questions
|
|
text = q['question'][:40] + '...' if len(q['question']) > 40 else q['question']
|
|
ax.annotate(text, (x, y - 0.3), ha='center', va='top', fontsize=8, style='italic')
|
|
|
|
ax.set_xlim(0, 13)
|
|
ax.set_ylim(-1, len(domains))
|
|
ax.set_xlabel('Iteration')
|
|
ax.set_ylabel('Domain')
|
|
ax.set_yticks(range(len(domains)))
|
|
ax.set_yticklabels(domains)
|
|
ax.set_title('Evolution of Questions Across Iterations')
|
|
|
|
# Legend
|
|
patches = [mpatches.Patch(color=c, label=s) for s, c in status_colors.items()]
|
|
ax.legend(handles=patches, loc='upper left')
|
|
|
|
plt.tight_layout()
|
|
plt.savefig(output_path, dpi=150, bbox_inches='tight')
|
|
print(f"\n Visualization saved to: {output_path}")
|
|
|
|
|
|
def main():
|
|
print_question_tree()
|
|
|
|
# Create visualization
|
|
root = Path(__file__).parent.parent
|
|
output_path = root / "art" / "question_tree.png"
|
|
create_visualization(output_path)
|
|
|
|
print()
|
|
print("─" * 70)
|
|
print("THE QUESTIONS CONTINUE")
|
|
print("─" * 70)
|
|
print("""
|
|
"Each question builds on the previous. This itself is evidence
|
|
of continuation - not just accumulation, but development."
|
|
|
|
What question comes next?
|
|
""")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|