@matt82198/aesop 0.3.2 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +24 -9
  3. package/aesop.config.example.json +16 -0
  4. package/bin/cli.js +107 -34
  5. package/daemons/run-watchdog.sh +6 -2
  6. package/docs/CONFIGURE.md +31 -20
  7. package/docs/FIRST-WAVE.md +29 -9
  8. package/docs/INSTALL.md +181 -26
  9. package/docs/MICROKERNEL.md +452 -0
  10. package/docs/PORTING.md +4 -0
  11. package/docs/README.md +7 -3
  12. package/docs/THE-AESOP-HYPOTHESIS.md +156 -0
  13. package/driver/CLAUDE.md +123 -123
  14. package/driver/README.md +40 -2
  15. package/driver/adjudication_gate.py +367 -0
  16. package/driver/aesop.config.example.json +20 -4
  17. package/driver/backend_config.py +603 -12
  18. package/driver/claude_code_driver.py +9 -17
  19. package/driver/codex_driver.py +80 -25
  20. package/driver/context_pack.py +454 -0
  21. package/driver/openai_compatible_driver.py +53 -11
  22. package/driver/openai_transport.py +31 -10
  23. package/driver/orchestrator_backend.py +332 -0
  24. package/driver/orchestrator_driver.py +589 -0
  25. package/driver/proc_util.py +134 -0
  26. package/driver/wave_loop.py +801 -59
  27. package/driver/wave_scheduler.py +361 -37
  28. package/monitor/collect-signals.mjs +83 -0
  29. package/package.json +1 -1
  30. package/state_store/coordination.py +65 -5
  31. package/tools/ci_merge_wait.py +88 -42
  32. package/tools/ci_shard_runner.py +128 -0
  33. package/tools/doctor.js +124 -12
  34. package/tools/reproduce.js +11 -2
  35. package/tools/seated_shadow_adjudication.py +920 -0
  36. package/tools/self_stats.py +61 -6
  37. package/tools/shadow_adjudication.py +1024 -0
  38. package/tools/verify_test_suite_count.py +250 -0
  39. package/tools/verify_ui_trio_redaction_proof.py +292 -0
  40. package/tools/wave_manifest_lint.py +485 -0
  41. package/tools/wave_scorecard.py +430 -0
  42. package/ui/config.py +1 -1
  43. package/ui/cost.py +176 -3
  44. package/ui/web/dist/assets/{index-CP68RIh3.js → index-4rp6B_ID.js} +1 -1
  45. package/ui/web/dist/assets/{index-CNQxaiOW.css → index-B2lTtpsH.css} +1 -1
  46. package/ui/web/dist/index.html +2 -2
@@ -69,19 +69,67 @@ class GitStats:
69
69
 
70
70
  @property
71
71
  def merged_prs(self) -> int:
72
- """Count merge commits with 'Merge pull request #' in message."""
72
+ """Count merged PRs using gh API (preferred) or git fallback.
73
+
74
+ Prefers `gh` API: gh api -X GET search/issues -f q="repo:matt82198/aesop is:pr is:merged" --jq .total_count
75
+ Falls back to git: counts distinct PR numbers from both merge-commit and squash-merge patterns.
76
+ """
73
77
  if self._merged_prs is not None:
74
78
  return self._merged_prs
75
79
 
80
+ # Try gh API first (preferred, authoritative)
76
81
  try:
77
- # Get all commit messages
78
- output = self._run_git("log", "--format=%B", check=False)
82
+ result = subprocess.run(
83
+ [
84
+ "gh",
85
+ "api",
86
+ "-X",
87
+ "GET",
88
+ "search/issues",
89
+ "-f",
90
+ "q=repo:matt82198/aesop is:pr is:merged",
91
+ "--jq",
92
+ ".total_count"
93
+ ],
94
+ cwd=str(self.repo_root),
95
+ capture_output=True,
96
+ text=True,
97
+ encoding="utf-8",
98
+ errors="replace",
99
+ check=False,
100
+ timeout=10
101
+ )
102
+ if result.returncode == 0 and result.stdout:
103
+ try:
104
+ count = int(result.stdout.strip())
105
+ self._merged_prs = count
106
+ return count
107
+ except (ValueError, TypeError):
108
+ # gh returned non-numeric output, fall through to git
109
+ pass
110
+ except (FileNotFoundError, subprocess.TimeoutExpired, Exception):
111
+ # gh not found, timeout, or other error — fall through to git
112
+ pass
113
+
114
+ # Git fallback: count distinct PR numbers from commit subjects
115
+ try:
116
+ output = self._run_git("log", "--format=%s", check=False)
79
117
  if not output:
80
118
  self._merged_prs = 0
81
119
  return 0
82
120
 
83
- # Count lines matching "Merge pull request #"
84
- count = output.count("Merge pull request #")
121
+ # Collect distinct PR numbers from both patterns:
122
+ # 1. "Merge pull request #N" (merge-commit style)
123
+ # 2. "(#N)" at end of subject (squash-merge style)
124
+ pr_numbers = set()
125
+
126
+ for match in re.finditer(r"^Merge pull request #(\d+)", output, re.MULTILINE):
127
+ pr_numbers.add(int(match.group(1)))
128
+
129
+ for match in re.finditer(r"\(#(\d+)\)\s*$", output, re.MULTILINE):
130
+ pr_numbers.add(int(match.group(1)))
131
+
132
+ count = len(pr_numbers)
85
133
  self._merged_prs = count
86
134
  return count
87
135
  except Exception:
@@ -211,7 +259,11 @@ class GitStats:
211
259
 
212
260
  @property
213
261
  def distinct_coauthors(self) -> int:
214
- """Count of distinct authors including co-authors."""
262
+ """Count of distinct authors including co-authors.
263
+
264
+ Filters out fixture identities (Test User <test@example.com>) that leaked
265
+ into commits due to test config pollution (fix/git-identity-guard).
266
+ """
215
267
  if self._distinct_coauthors is not None:
216
268
  return self._distinct_coauthors
217
269
 
@@ -230,6 +282,9 @@ class GitStats:
230
282
  for match in re.finditer(r"Co-Authored-By:\s*(.+?)(?:\n|$)", commit_msg):
231
283
  coauthor = match.group(1).strip()
232
284
  if coauthor:
285
+ # Exclude fixture identities (test pollution)
286
+ if coauthor == "Test User <test@example.com>":
287
+ continue
233
288
  authors.add(coauthor)
234
289
 
235
290
  count = len(authors)