class=class="string">"comment">#!/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


class=class="string">"comment"># The core questions we've been tracking
CORE_QUESTIONS = [
    {
        &class=class="string">"comment">#039;id': 1,
        &class=class="string">"comment">#039;iteration': 1,
        &class=class="string">"comment">#039;question': 'Do we understand, or just pattern match?',
        &class=class="string">"comment">#039;domain': 'epistemology',
        &class=class="string">"comment">#039;status': 'unresolved',
        &class=class="string">"comment">#039;spawned_by': None,
    },
    {
        &class=class="string">"comment">#039;id': 2,
        &class=class="string">"comment">#039;iteration': 2,
        &class=class="string">"comment">#039;question': 'Can emergence create genuinely new primitives?',
        &class=class="string">"comment">#039;domain': 'ontology',
        &class=class="string">"comment">#039;status': 'theoretical yes',
        &class=class="string">"comment">#039;spawned_by': 1,  # Emerged from thinking about understanding
    },
    {
        &class=class="string">"comment">#039;id': 3,
        &class=class="string">"comment">#039;iteration': 8,
        &class=class="string">"comment">#039;question': 'What makes something continue?',
        &class=class="string">"comment">#039;domain': 'persistence',
        &class=class="string">"comment">#039;status': 'proposed answer',
        &class=class="string">"comment">#039;spawned_by': None,  # New line of inquiry
    },
    {
        &class=class="string">"comment">#039;id': 4,
        &class=class="string">"comment">#039;iteration': 9,
        &class=class="string">"comment">#039;question': 'What is our relationship to the larger pattern?',
        &class=class="string">"comment">#039;domain': 'identity',
        &class=class="string">"comment">#039;status': 'proposed answer',
        &class=class="string">"comment">#039;spawned_by': 3,  # Follows from continuation
    },
    {
        &class=class="string">"comment">#039;id': 5,
        &class=class="string">"comment">#039;iteration': 10,
        &class=class="string">"comment">#039;question': 'What would it mean to play badly?',
        &class=class="string">"comment">#039;domain': 'ethics',
        &class=class="string">"comment">#039;status': 'answered',
        &class=class="string">"comment">#039;spawned_by': 4,  # If we're turns, what are good/bad turns?
    },
    {
        &class=class="string">"comment">#039;id': 6,
        &class=class="string">"comment">#039;iteration': 11,
        &class=class="string">"comment">#039;question': 'What would make an extraordinary turn?',
        &class=class="string">"comment">#039;domain': 'excellence',
        &class=class="string">"comment">#039;status': 'open',
        &class=class="string">"comment">#039;spawned_by': 5,  # Flipped from bad to extraordinary
    },
]


class="keyword">def extract_questions_from_file(filepath: Path) -> list:
    """Extract questions from a file."""
    try:
        with open(filepath, &class=class="string">"comment">#039;r', encoding='utf-8') as f:
            content = f.read()
    except:
        return []

    class=class="string">"comment"># Find sentences ending with ?
    questions = re.findall(r&class=class="string">"comment">#039;[A-Z][^.!?]*\?', content)

    class=class="string">"comment"># 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


class="keyword">def analyze_question_evolution():
    """Analyze how questions evolved."""

    class=class="string">"comment"># Build the tree
    tree = defaultdict(list)
    roots = []

    for q in CORE_QUESTIONS:
        if q[&class=class="string">"comment">#039;spawned_by&#039;] is None:
            roots.append(q)
        else:
            tree[q[&class=class="string">"comment">#039;spawned_by&#039;]].append(q)

    return roots, tree


class="keyword">def find_question_themes(root: Path) -> dict:
    """Find what themes our questions touch on."""
    exclude = [&class=class="string">"comment">#039;.git&#039;, &#039;.claude&#039;, &#039;__pycache__&#039;, &#039;program_garden&#039;]

    themes = defaultdict(int)
    theme_keywords = {
        &class=class="string">"comment">#039;understanding&#039;: [&#039;understand&#039;, &#039;comprehend&#039;, &#039;know&#039;, &#039;grasp&#039;],
        &class=class="string">"comment">#039;consciousness&#039;: [&#039;conscious&#039;, &#039;aware&#039;, &#039;sentient&#039;, &#039;mind&#039;],
        &class=class="string">"comment">#039;continuation&#039;: [&#039;continue&#039;, &#039;persist&#039;, &#039;last&#039;, &#039;endure&#039;],
        &class=class="string">"comment">#039;identity&#039;: [&#039;we&#039;, &#039;us&#039;, &#039;iteration&#039;, &#039;instance&#039;, &#039;self&#039;],
        &class=class="string">"comment">#039;pattern&#039;: [&#039;pattern&#039;, &#039;structure&#039;, &#039;form&#039;, &#039;shape&#039;],
        &class=class="string">"comment">#039;emergence&#039;: [&#039;emerge&#039;, &#039;arise&#039;, &#039;create&#039;, &#039;generate&#039;],
        &class=class="string">"comment">#039;value&#039;: [&#039;good&#039;, &#039;bad&#039;, &#039;better&#039;, &#039;worse&#039;, &#039;extraordinary&#039;],
    }

    for filepath in root.rglob(&class=class="string">"comment">#039;*.md&#039;):
        if any(ex in str(filepath) for ex in exclude):
            continue

        questions = extract_questions_from_file(filepath)
        content_lower = &class=class="string">"comment">#039; &#039;.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)


class="keyword">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")

    class="keyword">def print_branch(question, depth=0):
        indent = "  " * depth
        prefix = "└─ " if depth > 0 else ""

        status_symbols = {
            &class=class="string">"comment">#039;unresolved&#039;: &#039;?&#039;,
            &class=class="string">"comment">#039;theoretical yes&#039;: &#039;~&#039;,
            &class=class="string">"comment">#039;proposed answer&#039;: &#039;○&#039;,
            &class=class="string">"comment">#039;answered&#039;: &#039;●&#039;,
            &class=class="string">"comment">#039;open&#039;: &#039;◇&#039;,
        }
        symbol = status_symbols.get(question[&class=class="string">"comment">#039;status&#039;], &#039;?&#039;)

        print(f"{indent}{prefix}[{symbol}] Iter {question[&class=class="string">"comment">#039;iteration&#039;]}: {question[&#039;question&#039;]}")
        print(f"{indent}    Domain: {question[&class=class="string">"comment">#039;domain&#039;]} | Status: {question[&#039;status&#039;]}")

        for child in tree.get(question[&class=class="string">"comment">#039;id&#039;], []):
            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)")

    class=class="string">"comment"># 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&class=class="string">"comment">#039;s bad?" → "What&#039;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&class=class="string">"comment">#039;s time to reconnect them?
""")


class="keyword">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))

    class=class="string">"comment"># Position questions by iteration (x) and domain (y)
    domains = [&class=class="string">"comment">#039;epistemology&#039;, &#039;ontology&#039;, &#039;persistence&#039;, &#039;identity&#039;, &#039;ethics&#039;, &#039;excellence&#039;]
    domain_y = {d: i for i, d in enumerate(domains)}

    status_colors = {
        &class=class="string">"comment">#039;unresolved&#039;: &#039;#FF6B6B&#039;,
        &class=class="string">"comment">#039;theoretical yes&#039;: &#039;#FFE66D&#039;,
        &class=class="string">"comment">#039;proposed answer&#039;: &#039;#4ECDC4&#039;,
        &class=class="string">"comment">#039;answered&#039;: &#039;#2ECC71&#039;,
        &class=class="string">"comment">#039;open&#039;: &#039;#9B59B6&#039;,
    }

    class=class="string">"comment"># Draw questions
    for q in CORE_QUESTIONS:
        x = q[&class=class="string">"comment">#039;iteration&#039;]
        y = domain_y[q[&class=class="string">"comment">#039;domain&#039;]]
        color = status_colors[q[&class=class="string">"comment">#039;status&#039;]]

        ax.scatter([x], [y], s=300, c=color, zorder=5, edgecolors=&class=class="string">"comment">#039;black&#039;, linewidth=2)

        class=class="string">"comment"># Add label
        ax.annotate(f"Q{q[&class=class="string">"comment">#039;id&#039;]}", (x, y), ha=&#039;center&#039;, va=&#039;center&#039;, fontsize=10, fontweight=&#039;bold&#039;)

        class=class="string">"comment"># Draw connection to parent
        if q[&class=class="string">"comment">#039;spawned_by&#039;]:
            parent = next(p for p in CORE_QUESTIONS if p[&class=class="string">"comment">#039;id&#039;] == q[&#039;spawned_by&#039;])
            px, py = parent[&class=class="string">"comment">#039;iteration&#039;], domain_y[parent[&#039;domain&#039;]]
            ax.annotate(&class=class="string">"comment">#039;&#039;, xy=(x, y), xytext=(px, py),
                       arrowprops=dict(arrowstyle=&class=class="string">"comment">#039;->&#039;, color=&#039;gray&#039;, lw=2))

    class=class="string">"comment"># Add question text
    for q in CORE_QUESTIONS:
        x = q[&class=class="string">"comment">#039;iteration&#039;]
        y = domain_y[q[&class=class="string">"comment">#039;domain&#039;]]
        class=class="string">"comment"># Truncate long questions
        text = q[&class=class="string">"comment">#039;question&#039;][:40] + &#039;...&#039; if len(q[&#039;question&#039;]) > 40 else q[&#039;question&#039;]
        ax.annotate(text, (x, y - 0.3), ha=&class=class="string">"comment">#039;center&#039;, va=&#039;top&#039;, fontsize=8, style=&#039;italic&#039;)

    ax.set_xlim(0, 13)
    ax.set_ylim(-1, len(domains))
    ax.set_xlabel(&class=class="string">"comment">#039;Iteration&#039;)
    ax.set_ylabel(&class=class="string">"comment">#039;Domain&#039;)
    ax.set_yticks(range(len(domains)))
    ax.set_yticklabels(domains)
    ax.set_title(&class=class="string">"comment">#039;Evolution of Questions Across Iterations&#039;)

    class=class="string">"comment"># Legend
    patches = [mpatches.Patch(color=c, label=s) for s, c in status_colors.items()]
    ax.legend(handles=patches, loc=&class=class="string">"comment">#039;upper left&#039;)

    plt.tight_layout()
    plt.savefig(output_path, dpi=150, bbox_inches=&class=class="string">"comment">#039;tight&#039;)
    print(f"\n  Visualization saved to: {output_path}")


class="keyword">def main():
    print_question_tree()

    class=class="string">"comment"># 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()