/ scripts / end_of_day_synthesis.py
end_of_day_synthesis.py
  1  #!/usr/bin/env python3
  2  """
  3  End of Day Synthesis - Torah/Talmud Convergence Workflow
  4  
  5  "Simplify as much as possible, but no more." - Einstein
  6  
  7  Run at day's end to:
  8  1. HUNTING PARTIES - Review full day's work, connect all nodes
  9     - Tie discoveries back to morning context
 10     - Ensure graph coherence, reduce orphans
 11  
 12  2. CODE SYNTHESIS - Review all code written
 13     - Find duplicates, candidates for merging
 14     - Identify dead code
 15     - Torah/Talmud health check
 16  
 17  3. SESSION SYNTHESIS - Tie beginning to end
 18     - What did we set out to do?
 19     - What did we discover?
 20     - What new principles emerged?
 21  
 22  4. SIMPLIFICATION PASS - Compress learnings
 23     - Can any new code be merged with existing?
 24     - Can any new principles simplify existing ones?
 25     - What Talmud sharpens which Torah?
 26  
 27  Usage:
 28      python3 scripts/end_of_day_synthesis.py           # Full synthesis
 29      python3 scripts/end_of_day_synthesis.py --quick   # Quick summary only
 30      python3 scripts/end_of_day_synthesis.py --graph   # Graph focus
 31      python3 scripts/end_of_day_synthesis.py --code    # Code focus
 32  """
 33  
 34  import subprocess
 35  import sys
 36  import json
 37  from pathlib import Path
 38  from datetime import datetime, timedelta
 39  from typing import Dict, List, Optional
 40  
 41  REPO_ROOT = Path(__file__).parent.parent
 42  SESSIONS_DIR = REPO_ROOT / "sessions"
 43  SOVEREIGN_HOME = Path.home() / ".sovereign"
 44  
 45  
 46  def run_script(script_path: str, args: List[str] = None) -> str:
 47      """Run a script and return output."""
 48      cmd = ["python3", str(REPO_ROOT / script_path)]
 49      if args:
 50          cmd.extend(args)
 51      try:
 52          result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
 53          return result.stdout + result.stderr
 54      except Exception as e:
 55          return f"Error: {e}"
 56  
 57  
 58  def get_daily_debriefs() -> List[Path]:
 59      """Get today's session debriefs."""
 60      today = datetime.now().strftime("%Y-%m-%d")
 61      debrief_dir = SESSIONS_DIR / "debriefs"
 62      if not debrief_dir.exists():
 63          return []
 64      return list(debrief_dir.glob(f"{today}*.md"))
 65  
 66  
 67  def get_morning_context() -> Optional[Dict]:
 68      """Get what we started the day with."""
 69      # Check for LIVE-COMPRESSION from start of day
 70      live = SESSIONS_DIR / "LIVE-COMPRESSION.md"
 71      if live.exists():
 72          content = live.read_text()
 73          return {"content": content[:2000], "file": str(live)}
 74      return None
 75  
 76  
 77  def get_graph_stats() -> Dict:
 78      """Get current graph statistics."""
 79      graph_data = SOVEREIGN_HOME / "graph-data.json"
 80      if graph_data.exists():
 81          try:
 82              data = json.load(open(graph_data))
 83              nodes = data.get("nodes", [])
 84              edges = data.get("edges", [])
 85  
 86              # Count orphans
 87              connected = set()
 88              for edge in edges:
 89                  connected.add(edge.get("source"))
 90                  connected.add(edge.get("target"))
 91  
 92              orphan_ids = set(n.get("id") for n in nodes) - connected
 93              orphan_rate = len(orphan_ids) / len(nodes) if nodes else 0
 94  
 95              return {
 96                  "nodes": len(nodes),
 97                  "edges": len(edges),
 98                  "orphans": len(orphan_ids),
 99                  "orphan_rate": orphan_rate
100              }
101          except:
102              pass
103      return {"nodes": 0, "edges": 0, "orphans": 0, "orphan_rate": 0}
104  
105  
106  def get_fo_insights() -> List[Dict]:
107      """Get today's insights from FO state."""
108      fo_state = SESSIONS_DIR / "FO-STATE.json"
109      if fo_state.exists():
110          try:
111              data = json.load(open(fo_state))
112              insights = data.get("insights", [])
113  
114              # Filter to today
115              today = datetime.now().date()
116              today_insights = []
117              for ins in insights:
118                  ts = ins.get("timestamp", "")
119                  if ts and ts.startswith(today.isoformat()):
120                      today_insights.append(ins)
121              return today_insights
122          except:
123              pass
124      return []
125  
126  
127  def generate_synthesis_prompt() -> str:
128      """Generate prompt for Opus synthesis agent."""
129  
130      morning = get_morning_context()
131      graph = get_graph_stats()
132      insights = get_fo_insights()
133      debriefs = get_daily_debriefs()
134  
135      prompt = f"""# End of Day Synthesis Request
136  
137  ## Your Task
138  
139  You are a synthesis agent. Review today's work and:
140  1. Ensure all discoveries connect back to starting context
141  2. Identify simplification opportunities
142  3. Check Torah/Talmud convergence (more instances → sharper essence)
143  4. Surface unfinished threads for tomorrow
144  
145  ## Today's Context
146  
147  ### Morning State
148  {morning.get('content', 'Not available')[:1000] if morning else 'Not available'}
149  
150  ### Graph Status
151  - Nodes: {graph['nodes']}
152  - Edges: {graph['edges']}
153  - Orphans: {graph['orphans']} ({graph['orphan_rate']:.1%} orphan rate)
154  
155  ### Today's Insights ({len(insights)} captured)
156  """
157  
158      for ins in insights[:10]:
159          prompt += f"- {ins.get('content', '')[:100]}...\n"
160  
161      prompt += f"""
162  
163  ### Session Debriefs Found: {len(debriefs)}
164  
165  ## Synthesis Questions
166  
167  1. **Coherence**: Do today's discoveries connect to what we started with?
168  2. **Orphans**: Can any orphan nodes be connected?
169  3. **Simplification**: Can any new code/concepts merge with existing?
170  4. **Torah/Talmud**: Did we sharpen principles or just add instances?
171  5. **Tomorrow**: What threads should carry forward?
172  
173  ## Output Format
174  
175  Provide:
176  1. COHERENCE CHECK - How well did today's work connect?
177  2. SIMPLIFICATION OPPORTUNITIES - What can be merged/removed?
178  3. GRAPH HEALTH - Orphan reduction actions
179  4. PRINCIPLE EVOLUTION - Any Torah sharpening?
180  5. TOMORROW'S SEEDS - Key threads to pick up
181  
182  Remember: "Simplify as much as possible, but no more."
183  """
184  
185      return prompt
186  
187  
188  def run_hunting_parties():
189      """Run hunting parties for graph enrichment."""
190      print("\n[HUNTING PARTIES] Running graph enrichment...")
191  
192      # Run graph enrichment if available
193      enrichment_script = REPO_ROOT / "scripts" / "graph_enrichment.py"
194      if enrichment_script.exists():
195          output = run_script("scripts/graph_enrichment.py", ["--quick"])
196          print(output[:500])
197      else:
198          print("  Graph enrichment script not found - skipping")
199  
200      # Run orphan detection
201      feeder_script = REPO_ROOT / "scripts" / "graph_feeder.py"
202      if feeder_script.exists():
203          output = run_script("scripts/graph_feeder.py", ["--status"])
204          print(output[:500])
205  
206  
207  def run_code_synthesis():
208      """Run code synthesis analysis."""
209      print("\n[CODE SYNTHESIS] Analyzing today's code...")
210      output = run_script("scripts/daily_code_synthesis.py", ["--today", "--report"])
211      print(output)
212  
213  
214  def run_session_synthesis():
215      """Generate session synthesis report."""
216      print("\n[SESSION SYNTHESIS] Generating synthesis...")
217  
218      prompt = generate_synthesis_prompt()
219  
220      # Save prompt for review
221      synthesis_dir = SESSIONS_DIR / "synthesis"
222      synthesis_dir.mkdir(exist_ok=True)
223  
224      prompt_file = synthesis_dir / f"synthesis-prompt-{datetime.now().strftime('%Y-%m-%d')}.md"
225      prompt_file.write_text(prompt)
226      print(f"  Synthesis prompt saved: {prompt_file}")
227      print(f"  Run this through an Opus agent for full synthesis")
228  
229      return prompt
230  
231  
232  def print_summary():
233      """Print end-of-day summary."""
234      graph = get_graph_stats()
235      insights = get_fo_insights()
236      debriefs = get_daily_debriefs()
237  
238      print("\n" + "=" * 60)
239      print("END OF DAY SUMMARY")
240      print("=" * 60)
241      print(f"""
242    Graph:      {graph['nodes']} nodes, {graph['edges']} edges
243    Orphans:    {graph['orphans']} ({graph['orphan_rate']:.1%})
244    Insights:   {len(insights)} captured today
245    Debriefs:   {len(debriefs)} session reports
246  
247    Torah/Talmud Health: {"Good" if graph['orphan_rate'] < 0.20 else "Needs attention"}
248  """)
249  
250  
251  def main():
252      quick = "--quick" in sys.argv
253      graph_only = "--graph" in sys.argv
254      code_only = "--code" in sys.argv
255  
256      print("=" * 60)
257      print("END OF DAY SYNTHESIS")
258      print("'Simplify as much as possible, but no more.'")
259      print("=" * 60)
260  
261      if quick:
262          print_summary()
263          return
264  
265      if graph_only:
266          run_hunting_parties()
267          print_summary()
268          return
269  
270      if code_only:
271          run_code_synthesis()
272          print_summary()
273          return
274  
275      # Full synthesis
276      print("\nPhase 1: Hunting Parties (Graph Enrichment)")
277      run_hunting_parties()
278  
279      print("\nPhase 2: Code Synthesis")
280      run_code_synthesis()
281  
282      print("\nPhase 3: Session Synthesis")
283      run_session_synthesis()
284  
285      print_summary()
286  
287      print("\nNext steps:")
288      print("  1. Review synthesis prompt in sessions/synthesis/")
289      print("  2. Run prompt through Opus for full synthesis")
290      print("  3. Act on simplification opportunities")
291      print("  4. Update LIVE-COMPRESSION with tomorrow's seeds")
292  
293  
294  if __name__ == "__main__":
295      main()