algo-cli-runtime 0.14.0__py3-none-any.whl

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 (237) hide show
  1. algo_cli/__init__.py +3 -0
  2. algo_cli/__main__.py +7 -0
  3. algo_cli/_internal/__init__.py +12 -0
  4. algo_cli/_internal/policy_chain.py +259 -0
  5. algo_cli/action_registry.py +1047 -0
  6. algo_cli/agent_blocks.py +550 -0
  7. algo_cli/agent_pipeline.py +1457 -0
  8. algo_cli/agent_threads.py +308 -0
  9. algo_cli/animations.py +316 -0
  10. algo_cli/cache_admission.py +209 -0
  11. algo_cli/capability_mask.py +66 -0
  12. algo_cli/chat_protocol.py +116 -0
  13. algo_cli/chatgpt_auth.py +510 -0
  14. algo_cli/chatgpt_client.py +657 -0
  15. algo_cli/code_rag.py +479 -0
  16. algo_cli/config.py +651 -0
  17. algo_cli/context_budget.py +679 -0
  18. algo_cli/credential_helpers.py +315 -0
  19. algo_cli/deliberation.py +29 -0
  20. algo_cli/display.py +1470 -0
  21. algo_cli/evals/__init__.py +21 -0
  22. algo_cli/evals/algorithm_effectiveness.py +560 -0
  23. algo_cli/evals/competitive_harness_rating.py +702 -0
  24. algo_cli/evals/cot_quality.py +220 -0
  25. algo_cli/evals/harness_retrieval_benchmark.py +401 -0
  26. algo_cli/evals/performance_regression.py +136 -0
  27. algo_cli/evals/scorecard_grading.py +308 -0
  28. algo_cli/evals/session_distribution.py +84 -0
  29. algo_cli/execution_guardrails.py +806 -0
  30. algo_cli/extensions_manifest.py +84 -0
  31. algo_cli/git_evidence.py +227 -0
  32. algo_cli/google_workspace.py +407 -0
  33. algo_cli/google_workspace_auth.py +523 -0
  34. algo_cli/harness.py +2587 -0
  35. algo_cli/identity.py +557 -0
  36. algo_cli/index_compute_lab.py +228 -0
  37. algo_cli/inference_harness.py +70 -0
  38. algo_cli/intelligence/__init__.py +1103 -0
  39. algo_cli/intelligence/acrobat_config.py +307 -0
  40. algo_cli/intelligence/acrobat_manifests.py +338 -0
  41. algo_cli/intelligence/acrobat_models.py +195 -0
  42. algo_cli/intelligence/acrobat_pipeline.py +295 -0
  43. algo_cli/intelligence/acrobat_runtime.py +302 -0
  44. algo_cli/intelligence/acrobat_security.py +261 -0
  45. algo_cli/intelligence/acrobat_workflows.py +226 -0
  46. algo_cli/intelligence/actionability.py +165 -0
  47. algo_cli/intelligence/adversarial_audit.py +136 -0
  48. algo_cli/intelligence/agent_arena.py +92 -0
  49. algo_cli/intelligence/agent_benchmark.py +236 -0
  50. algo_cli/intelligence/agent_runtime.py +171 -0
  51. algo_cli/intelligence/agents_as_tools.py +70 -0
  52. algo_cli/intelligence/artifact_binding.py +80 -0
  53. algo_cli/intelligence/autonomous_engineer.py +1976 -0
  54. algo_cli/intelligence/backpressure.py +99 -0
  55. algo_cli/intelligence/bloom_filter.py +186 -0
  56. algo_cli/intelligence/bonferroni.py +66 -0
  57. algo_cli/intelligence/boundary_compaction.py +98 -0
  58. algo_cli/intelligence/catalog_verifier.py +172 -0
  59. algo_cli/intelligence/cavecrew.py +118 -0
  60. algo_cli/intelligence/changelog.py +176 -0
  61. algo_cli/intelligence/checkpoint_resume.py +92 -0
  62. algo_cli/intelligence/circuit_breaker.py +88 -0
  63. algo_cli/intelligence/clarification_gate.py +101 -0
  64. algo_cli/intelligence/code_graph.py +180 -0
  65. algo_cli/intelligence/coderank.py +97 -0
  66. algo_cli/intelligence/consistent_hash.py +150 -0
  67. algo_cli/intelligence/consortium_synthesis.py +139 -0
  68. algo_cli/intelligence/construction/__init__.py +241 -0
  69. algo_cli/intelligence/construction/common.py +273 -0
  70. algo_cli/intelligence/construction/documents.py +496 -0
  71. algo_cli/intelligence/construction/labor_units.py +1395 -0
  72. algo_cli/intelligence/construction/payments.py +470 -0
  73. algo_cli/intelligence/construction/risk.py +784 -0
  74. algo_cli/intelligence/content_extractor.py +132 -0
  75. algo_cli/intelligence/context_adaptive.py +102 -0
  76. algo_cli/intelligence/context_ops.py +95 -0
  77. algo_cli/intelligence/count_min.py +145 -0
  78. algo_cli/intelligence/cow_state.py +103 -0
  79. algo_cli/intelligence/critic_loop.py +119 -0
  80. algo_cli/intelligence/cross_source.py +113 -0
  81. algo_cli/intelligence/daemon_mode.py +99 -0
  82. algo_cli/intelligence/dag_orchestration.py +151 -0
  83. algo_cli/intelligence/deep_research.py +155 -0
  84. algo_cli/intelligence/degenerate_detector.py +78 -0
  85. algo_cli/intelligence/delta_report.py +92 -0
  86. algo_cli/intelligence/discovery_event_log.py +92 -0
  87. algo_cli/intelligence/document_ingest.py +298 -0
  88. algo_cli/intelligence/dual_layer_validate.py +151 -0
  89. algo_cli/intelligence/echo_fidelity.py +73 -0
  90. algo_cli/intelligence/ema_tuning.py +104 -0
  91. algo_cli/intelligence/event_log.py +92 -0
  92. algo_cli/intelligence/evidence_graph.py +114 -0
  93. algo_cli/intelligence/extension_host.py +162 -0
  94. algo_cli/intelligence/extension_manifest.py +115 -0
  95. algo_cli/intelligence/falsification_suite.py +178 -0
  96. algo_cli/intelligence/finance/__init__.py +169 -0
  97. algo_cli/intelligence/finance/anomalies.py +135 -0
  98. algo_cli/intelligence/finance/ap_ar.py +351 -0
  99. algo_cli/intelligence/finance/cash.py +162 -0
  100. algo_cli/intelligence/finance/close.py +332 -0
  101. algo_cli/intelligence/finance/common.py +244 -0
  102. algo_cli/intelligence/finance/construction.py +135 -0
  103. algo_cli/intelligence/finance/controls.py +172 -0
  104. algo_cli/intelligence/finance/evidence.py +119 -0
  105. algo_cli/intelligence/finance/exceptions.py +157 -0
  106. algo_cli/intelligence/finance/reconciliations.py +254 -0
  107. algo_cli/intelligence/finance/revenue.py +109 -0
  108. algo_cli/intelligence/finance/tax.py +74 -0
  109. algo_cli/intelligence/finance/workpapers.py +111 -0
  110. algo_cli/intelligence/finding_record.py +120 -0
  111. algo_cli/intelligence/flow_dag.py +267 -0
  112. algo_cli/intelligence/gatherer.py +223 -0
  113. algo_cli/intelligence/golden_master.py +98 -0
  114. algo_cli/intelligence/graph_rag.py +195 -0
  115. algo_cli/intelligence/group_chat.py +143 -0
  116. algo_cli/intelligence/hash_dedup.py +145 -0
  117. algo_cli/intelligence/hyperloglog.py +128 -0
  118. algo_cli/intelligence/incremental_index.py +316 -0
  119. algo_cli/intelligence/index_store.py +16 -0
  120. algo_cli/intelligence/iteration_plan.py +133 -0
  121. algo_cli/intelligence/kernel_plugins.py +167 -0
  122. algo_cli/intelligence/lesson_catalog.py +135 -0
  123. algo_cli/intelligence/llm_fallback.py +169 -0
  124. algo_cli/intelligence/log2_histogram.py +267 -0
  125. algo_cli/intelligence/lsp_integration.py +147 -0
  126. algo_cli/intelligence/memory_evolution.py +117 -0
  127. algo_cli/intelligence/minhash_lsh.py +182 -0
  128. algo_cli/intelligence/multi_model_score.py +174 -0
  129. algo_cli/intelligence/multi_tier_grade.py +211 -0
  130. algo_cli/intelligence/negative_controls.py +113 -0
  131. algo_cli/intelligence/numeric_clamp.py +63 -0
  132. algo_cli/intelligence/occ_editor.py +66 -0
  133. algo_cli/intelligence/output_normalize.py +112 -0
  134. algo_cli/intelligence/parallel_delegation.py +98 -0
  135. algo_cli/intelligence/parallel_fanout.py +104 -0
  136. algo_cli/intelligence/permission_modes.py +105 -0
  137. algo_cli/intelligence/pre_push_gate.py +68 -0
  138. algo_cli/intelligence/prefetch.py +171 -0
  139. algo_cli/intelligence/process_framework.py +217 -0
  140. algo_cli/intelligence/project_graph.py +387 -0
  141. algo_cli/intelligence/query_expansion.py +146 -0
  142. algo_cli/intelligence/ralph_loop.py +117 -0
  143. algo_cli/intelligence/rate_limiter.py +153 -0
  144. algo_cli/intelligence/refactor_transaction.py +94 -0
  145. algo_cli/intelligence/research_workspace.py +108 -0
  146. algo_cli/intelligence/retraction_ledger.py +72 -0
  147. algo_cli/intelligence/saga_pattern.py +88 -0
  148. algo_cli/intelligence/session_fork.py +100 -0
  149. algo_cli/intelligence/shadow_editor.py +67 -0
  150. algo_cli/intelligence/shell_session.py +213 -0
  151. algo_cli/intelligence/source_registry.py +143 -0
  152. algo_cli/intelligence/spawn_scales.py +99 -0
  153. algo_cli/intelligence/stat_stability.py +104 -0
  154. algo_cli/intelligence/structural_validator.py +148 -0
  155. algo_cli/intelligence/subagent_spawner.py +111 -0
  156. algo_cli/intelligence/symmetric_verify.py +70 -0
  157. algo_cli/intelligence/task_classifier.py +129 -0
  158. algo_cli/intelligence/team_execution.py +122 -0
  159. algo_cli/intelligence/tiered_access.py +121 -0
  160. algo_cli/intelligence/utility_registry.py +159 -0
  161. algo_cli/intuition_engine.py +560 -0
  162. algo_cli/intuition_injector.py +82 -0
  163. algo_cli/kernels/__init__.py +5 -0
  164. algo_cli/kernels/manifest.py +763 -0
  165. algo_cli/main.py +3903 -0
  166. algo_cli/memory_candidates.py +541 -0
  167. algo_cli/memory_echo_veil.py +394 -0
  168. algo_cli/memory_runtime.py +112 -0
  169. algo_cli/model_info.py +548 -0
  170. algo_cli/model_profile.py +160 -0
  171. algo_cli/model_routing.py +74 -0
  172. algo_cli/oneshot.py +331 -0
  173. algo_cli/perf_telemetry.py +389 -0
  174. algo_cli/plugins.py +245 -0
  175. algo_cli/private_event_store.py +654 -0
  176. algo_cli/quantization/__init__.py +24 -0
  177. algo_cli/quantization/lloyd_max.py +98 -0
  178. algo_cli/quantization/turbo_quant.py +308 -0
  179. algo_cli/reasoning/__init__.py +46 -0
  180. algo_cli/reasoning/combinatorial.py +356 -0
  181. algo_cli/reasoning/graph_of_thought.py +297 -0
  182. algo_cli/reasoning/mcts.py +220 -0
  183. algo_cli/reasoning/neuro_symbolic.py +250 -0
  184. algo_cli/reasoning/react.py +246 -0
  185. algo_cli/reasoning/reflexion.py +225 -0
  186. algo_cli/reasoning/tree_of_thought.py +241 -0
  187. algo_cli/reasoning_bridge.py +150 -0
  188. algo_cli/reconciliation.py +284 -0
  189. algo_cli/reflex.py +385 -0
  190. algo_cli/resources/docs/ALGO.md +13958 -0
  191. algo_cli/resources/docs/algo-cli-algorithm-evidence-contract.md +60 -0
  192. algo_cli/resources/docs/algo-cli-execution-verification-contract.md +59 -0
  193. algo_cli/resources/docs/algo-cli-memory-lifecycle-contract.md +72 -0
  194. algo_cli/resources/docs/harness-extension-cleanup-recommendation.md +41 -0
  195. algo_cli/resources/docs/index-compute-lab-integration.md +32 -0
  196. algo_cli/resources/docs/inference-harness-loop-blueprint-2026-06.md +55 -0
  197. algo_cli/resources/docs/main-split-map.md +35 -0
  198. algo_cli/resources/docs/privacy-and-context.md +48 -0
  199. algo_cli/resources/docs/reflex-loop-v0.2.md +354 -0
  200. algo_cli/resources/skills/README.md +26 -0
  201. algo_cli/resources/skills/algo-cli.md +59 -0
  202. algo_cli/resources/skills/edit-file-precision.md +49 -0
  203. algo_cli/resources/skills/harness-search-first.md +47 -0
  204. algo_cli/resources/skills/memory-recall-ritual.md +51 -0
  205. algo_cli/resources/skills/qol-algorithms.md +224 -0
  206. algo_cli/resources/skills/smart-error-recovery.md +56 -0
  207. algo_cli/resources/skills/tool-selection-cheatsheet.md +65 -0
  208. algo_cli/retrieval_algorithms.py +127 -0
  209. algo_cli/runtime_qos.py +236 -0
  210. algo_cli/runtime_services.py +320 -0
  211. algo_cli/session_commands.py +95 -0
  212. algo_cli/session_mode.py +113 -0
  213. algo_cli/skills.py +430 -0
  214. algo_cli/slash_dispatch.py +1265 -0
  215. algo_cli/small_context.py +206 -0
  216. algo_cli/spawn_budget.py +89 -0
  217. algo_cli/task_ledger.py +84 -0
  218. algo_cli/task_router.py +197 -0
  219. algo_cli/tool_context.py +94 -0
  220. algo_cli/tool_contract.py +99 -0
  221. algo_cli/tool_policy.py +357 -0
  222. algo_cli/tool_runtime.py +647 -0
  223. algo_cli/tools.py +3056 -0
  224. algo_cli/url_scheme.py +174 -0
  225. algo_cli/verify.py +154 -0
  226. algo_cli/version_manifest.py +178 -0
  227. algo_cli/vision_screenshot_verify.py +76 -0
  228. algo_cli/workspace_resolver.py +68 -0
  229. algo_cli/x_account.py +209 -0
  230. algo_cli/xai_auth.py +374 -0
  231. algo_cli/xai_client.py +600 -0
  232. algo_cli_runtime-0.14.0.dist-info/METADATA +369 -0
  233. algo_cli_runtime-0.14.0.dist-info/RECORD +237 -0
  234. algo_cli_runtime-0.14.0.dist-info/WHEEL +4 -0
  235. algo_cli_runtime-0.14.0.dist-info/entry_points.txt +3 -0
  236. algo_cli_runtime-0.14.0.dist-info/licenses/LICENSE +21 -0
  237. ollama_cli/__init__.py +67 -0
@@ -0,0 +1,118 @@
1
+ """B75. Cavecrew: Model Specialization per Role.
2
+
3
+ Match model cost to task complexity.
4
+ Investigator (cheap) → Builder (standard) → Reviewer (cheap) → Orchestrator (expensive).
5
+ Saves ~60% tokens. Source: Claudient pattern.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ from dataclasses import dataclass, field
10
+ from enum import Enum, auto
11
+ from typing import Callable
12
+
13
+
14
+ class CrewRole(Enum):
15
+ INVESTIGATOR = auto() # cheap model — search, read, gather
16
+ BUILDER = auto() # standard model — write, edit, implement
17
+ REVIEWER = auto() # cheap model — check, verify, audit
18
+ ORCHESTRATOR = auto() # expensive model — plan, decide, coordinate
19
+
20
+
21
+ @dataclass
22
+ class CrewMember:
23
+ role: CrewRole
24
+ model: str
25
+ system_prompt: str = ""
26
+ max_iterations: int = 5
27
+
28
+
29
+ @dataclass
30
+ class CrewTask:
31
+ description: str
32
+ assigned_role: CrewRole
33
+ dependencies: list[str] = field(default_factory=list)
34
+ result: str = ""
35
+
36
+
37
+ DEFAULT_CREW: dict[CrewRole, CrewMember] = {
38
+ CrewRole.INVESTIGATOR: CrewMember(
39
+ role=CrewRole.INVESTIGATOR,
40
+ model="qwen3:4b",
41
+ system_prompt="You are an investigator. Search, read, and gather information. Be concise.",
42
+ max_iterations=3,
43
+ ),
44
+ CrewRole.BUILDER: CrewMember(
45
+ role=CrewRole.BUILDER,
46
+ model="glm-5.2",
47
+ system_prompt="You are a builder. Write and edit code. Follow existing patterns.",
48
+ max_iterations=5,
49
+ ),
50
+ CrewRole.REVIEWER: CrewMember(
51
+ role=CrewRole.REVIEWER,
52
+ model="qwen3:4b",
53
+ system_prompt="You are a reviewer. Check for errors, missing tests, and improvements.",
54
+ max_iterations=3,
55
+ ),
56
+ CrewRole.ORCHESTRATOR: CrewMember(
57
+ role=CrewRole.ORCHESTRATOR,
58
+ model="gpt-5.5",
59
+ system_prompt="You are the orchestrator. Plan, decide, and coordinate. Be strategic.",
60
+ max_iterations=5,
61
+ ),
62
+ }
63
+
64
+
65
+ class Cavecrew:
66
+ """Model-specialized crew for cost-efficient multi-agent work."""
67
+
68
+ def __init__(self, crew: dict[CrewRole, CrewMember] | None = None) -> None:
69
+ self._crew = crew or dict(DEFAULT_CREW)
70
+ self._tasks: list[CrewTask] = []
71
+ self._results: dict[str, str] = {}
72
+
73
+ def assign(self, task: CrewTask) -> None:
74
+ self._tasks.append(task)
75
+
76
+ def run(self, run_fn: Callable[[CrewMember, str, str], str]) -> dict[str, str]:
77
+ """Execute tasks in dependency order, routing to appropriate models."""
78
+ completed: set[str] = set()
79
+
80
+ for _ in range(len(self._tasks) * 2): # safety limit
81
+ ready = [
82
+ t for t in self._tasks
83
+ if t.description not in completed
84
+ and all(d in completed for d in t.dependencies)
85
+ ]
86
+ if not ready:
87
+ break
88
+
89
+ for task in ready:
90
+ member = self._crew.get(task.assigned_role)
91
+ if not member:
92
+ continue
93
+ try:
94
+ result = run_fn(member, member.system_prompt, task.description)
95
+ task.result = result
96
+ self._results[task.description] = result
97
+ completed.add(task.description)
98
+ except Exception as e:
99
+ task.result = f"ERROR: {e}"
100
+ self._results[task.description] = task.result
101
+ completed.add(task.description)
102
+
103
+ return dict(self._results)
104
+
105
+ def estimate_cost(self) -> dict[CrewRole, int]:
106
+ """Estimate relative token cost per role."""
107
+ costs = {
108
+ CrewRole.INVESTIGATOR: 1, # cheap
109
+ CrewRole.BUILDER: 3, # standard
110
+ CrewRole.REVIEWER: 1, # cheap
111
+ CrewRole.ORCHESTRATOR: 5, # expensive
112
+ }
113
+ return {role: costs.get(role, 1) * sum(1 for t in self._tasks if t.assigned_role == role)
114
+ for role in CrewRole}
115
+
116
+ @property
117
+ def crew(self) -> dict[CrewRole, CrewMember]:
118
+ return dict(self._crew)
@@ -0,0 +1,176 @@
1
+ """B44. Changefile-Driven Release Notes (Microsoft Beachball Pattern).
2
+
3
+ Requires a changefile for every user-visible code change. Generates
4
+ release notes grouped by kind (feature, fix, maintenance, safety, docs,
5
+ breaking). Validates that no release is missing changefiles.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ from dataclasses import dataclass, field
12
+ from datetime import datetime
13
+ from pathlib import Path
14
+ from typing import Any
15
+
16
+
17
+ class ChangeKind:
18
+ FEATURE = "feature"
19
+ FIX = "fix"
20
+ MAINTENANCE = "maintenance"
21
+ SAFETY = "safety"
22
+ DOCS = "docs"
23
+ BREAKING = "breaking"
24
+
25
+
26
+ @dataclass
27
+ class ChangeFile:
28
+ """A single change file (changes/*.json)."""
29
+ filename: str
30
+ kind: str
31
+ description: str
32
+ component: str = ""
33
+ author: str = ""
34
+ created_at: str = field(default_factory=lambda: datetime.now().isoformat())
35
+ version_bump: str = "patch" # "patch", "minor", "major"
36
+
37
+
38
+ @dataclass
39
+ class ReleaseNotes:
40
+ version: str
41
+ date: str
42
+ sections: dict[str, list[str]] = field(default_factory=dict)
43
+ breaking_changes: list[str] = field(default_factory=list)
44
+ total_changes: int = 0
45
+
46
+ def to_markdown(self) -> str:
47
+ lines = [f"# Release {self.version}", f"Date: {self.date}", ""]
48
+ if self.breaking_changes:
49
+ lines.append("## ⚠️ Breaking Changes")
50
+ for bc in self.breaking_changes:
51
+ lines.append(f"- {bc}")
52
+ lines.append("")
53
+ kind_order = [ChangeKind.FEATURE, ChangeKind.FIX, ChangeKind.SAFETY, ChangeKind.MAINTENANCE, ChangeKind.DOCS]
54
+ kind_labels = {
55
+ ChangeKind.FEATURE: "✨ Features",
56
+ ChangeKind.FIX: "🐛 Fixes",
57
+ ChangeKind.SAFETY: "🛡️ Safety",
58
+ ChangeKind.MAINTENANCE: "🔧 Maintenance",
59
+ ChangeKind.DOCS: "📚 Documentation",
60
+ }
61
+ for kind in kind_order:
62
+ entries = self.sections.get(kind, [])
63
+ if entries:
64
+ lines.append(f"## {kind_labels.get(kind, kind)}")
65
+ for entry in entries:
66
+ lines.append(f"- {entry}")
67
+ lines.append("")
68
+ return "\n".join(lines)
69
+
70
+
71
+ class ChangelogManager:
72
+ """Manages change files and release note generation."""
73
+
74
+ def __init__(self, changes_dir: str = "changes"):
75
+ self.changes_dir = Path(changes_dir)
76
+ self._changes: list[ChangeFile] = []
77
+
78
+ def add_change(self, change: ChangeFile) -> None:
79
+ self._changes.append(change)
80
+
81
+ def load_from_dir(self, directory: Path | None = None) -> list[ChangeFile]:
82
+ """Load change files from a directory."""
83
+ d = directory or self.changes_dir
84
+ if not d.exists():
85
+ return []
86
+ loaded: list[ChangeFile] = []
87
+ for f in sorted(d.glob("*.json")):
88
+ try:
89
+ data = json.loads(f.read_text(encoding="utf-8"))
90
+ loaded.append(ChangeFile(
91
+ filename=f.name,
92
+ kind=data.get("kind", ChangeKind.MAINTENANCE),
93
+ description=data.get("description", ""),
94
+ component=data.get("component", ""),
95
+ author=data.get("author", ""),
96
+ created_at=data.get("created_at", datetime.now().isoformat()),
97
+ version_bump=data.get("version_bump", "patch"),
98
+ ))
99
+ except (json.JSONDecodeError, KeyError):
100
+ continue
101
+ self._changes.extend(loaded)
102
+ return loaded
103
+
104
+ def pending_changes(self) -> list[ChangeFile]:
105
+ """Return all loaded changes (not yet released)."""
106
+ return list(self._changes)
107
+
108
+ def has_changes(self) -> bool:
109
+ return len(self._changes) > 0
110
+
111
+ def required_version_bump(self) -> str:
112
+ """Determine the minimum version bump from pending changes."""
113
+ if any(c.version_bump == "major" or c.kind == ChangeKind.BREAKING for c in self._changes):
114
+ return "major"
115
+ if any(c.version_bump == "minor" or c.kind == ChangeKind.FEATURE for c in self._changes):
116
+ return "minor"
117
+ return "patch"
118
+
119
+ def generate_release_notes(self, version: str, date: str | None = None) -> ReleaseNotes:
120
+ """Generate release notes from pending changes."""
121
+ date = date or datetime.now().strftime("%Y-%m-%d")
122
+ sections: dict[str, list[str]] = {}
123
+ breaking: list[str] = []
124
+ for change in self._changes:
125
+ kind = change.kind
126
+ if kind == ChangeKind.BREAKING:
127
+ breaking.append(change.description)
128
+ kind = ChangeKind.FIX # also list under fixes
129
+ sections.setdefault(kind, []).append(
130
+ f"{change.description}" + (f" ({change.component})" if change.component else "")
131
+ )
132
+ notes = ReleaseNotes(
133
+ version=version,
134
+ date=date,
135
+ sections=sections,
136
+ breaking_changes=breaking,
137
+ total_changes=len(self._changes),
138
+ )
139
+ return notes
140
+
141
+ def release_check(self) -> dict[str, Any]:
142
+ """Validate release readiness."""
143
+ issues: list[str] = []
144
+ if not self._changes:
145
+ issues.append("no changefiles found — user-visible changes require a changefile")
146
+ for c in self._changes:
147
+ if not c.description:
148
+ issues.append(f"changefile {c.filename} has empty description")
149
+ if c.kind not in [ChangeKind.FEATURE, ChangeKind.FIX, ChangeKind.MAINTENANCE, ChangeKind.SAFETY, ChangeKind.DOCS, ChangeKind.BREAKING]:
150
+ issues.append(f"changefile {c.filename} has unknown kind: {c.kind}")
151
+ return {
152
+ "ready": len(issues) == 0,
153
+ "issues": issues,
154
+ "change_count": len(self._changes),
155
+ "required_bump": self.required_version_bump(),
156
+ }
157
+
158
+ def clear_released(self) -> None:
159
+ """Clear changes after generating release notes."""
160
+ self._changes.clear()
161
+
162
+
163
+ def write_change_file(directory: Path, name: str, kind: str, description: str, component: str = "", author: str = "") -> Path:
164
+ """Write a change file to disk."""
165
+ directory.mkdir(parents=True, exist_ok=True)
166
+ data = {
167
+ "kind": kind,
168
+ "description": description,
169
+ "component": component,
170
+ "author": author,
171
+ "created_at": datetime.now().isoformat(),
172
+ "version_bump": "major" if kind == ChangeKind.BREAKING else "minor" if kind == ChangeKind.FEATURE else "patch",
173
+ }
174
+ path = directory / f"{name}.json"
175
+ path.write_text(json.dumps(data, indent=2), encoding="utf-8")
176
+ return path
@@ -0,0 +1,92 @@
1
+ """H11 — Checkpoint/Resume.
2
+
3
+ Resumable long catalog operations via state serialization.
4
+ Mined from GLOSSOPETRAE experiment harnesses.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import json
9
+ import time
10
+ from dataclasses import dataclass, field
11
+ from typing import Any
12
+
13
+
14
+ @dataclass
15
+ class Checkpoint:
16
+ """A single checkpoint capturing operation state."""
17
+
18
+ operation_id: str
19
+ step: int
20
+ total_steps: int
21
+ state: dict[str, Any] = field(default_factory=dict)
22
+ created_at: float = field(default_factory=time.time)
23
+ completed: bool = False
24
+
25
+ def to_dict(self) -> dict[str, Any]:
26
+ return {
27
+ "operation_id": self.operation_id,
28
+ "step": self.step,
29
+ "total_steps": self.total_steps,
30
+ "state": dict(self.state),
31
+ "created_at": self.created_at,
32
+ "completed": self.completed,
33
+ }
34
+
35
+ @classmethod
36
+ def from_dict(cls, data: dict[str, Any]) -> Checkpoint:
37
+ return cls(
38
+ operation_id=data["operation_id"],
39
+ step=data["step"],
40
+ total_steps=data["total_steps"],
41
+ state=dict(data.get("state", {})),
42
+ created_at=data.get("created_at", time.time()),
43
+ completed=data.get("completed", False),
44
+ )
45
+
46
+
47
+ class CheckpointManager:
48
+ """Save and load checkpoints for resumable operations."""
49
+
50
+ def __init__(self) -> None:
51
+ self._checkpoints: dict[str, Checkpoint] = {}
52
+
53
+ def save(self, checkpoint: Checkpoint) -> None:
54
+ self._checkpoints[checkpoint.operation_id] = checkpoint
55
+
56
+ def load(self, operation_id: str) -> Checkpoint | None:
57
+ return self._checkpoints.get(operation_id)
58
+
59
+ def complete(self, operation_id: str) -> Checkpoint | None:
60
+ cp = self._checkpoints.get(operation_id)
61
+ if cp is not None:
62
+ cp.completed = True
63
+ return cp
64
+
65
+ def is_complete(self, operation_id: str) -> bool:
66
+ cp = self._checkpoints.get(operation_id)
67
+ return cp is not None and cp.completed
68
+
69
+ def resume_step(self, operation_id: str) -> int:
70
+ """Return the step to resume from (0 if no checkpoint)."""
71
+ cp = self._checkpoints.get(operation_id)
72
+ if cp is None or cp.completed:
73
+ return 0
74
+ return cp.step
75
+
76
+ def serialize(self, operation_id: str) -> str:
77
+ cp = self._checkpoints.get(operation_id)
78
+ if cp is None:
79
+ raise KeyError(f"No checkpoint for {operation_id!r}")
80
+ return json.dumps(cp.to_dict())
81
+
82
+ def deserialize(self, data: str) -> Checkpoint:
83
+ return Checkpoint.from_dict(json.loads(data))
84
+
85
+ def all(self) -> list[Checkpoint]:
86
+ return list(self._checkpoints.values())
87
+
88
+ def count(self) -> int:
89
+ return len(self._checkpoints)
90
+
91
+ def clear(self) -> None:
92
+ self._checkpoints.clear()
@@ -0,0 +1,88 @@
1
+ """H16 — Detection Risk Circuit Breaker.
2
+
3
+ Accumulate risk per failure, auto-disable at threshold.
4
+ Mined from T3MP3ST WHITEPAPER §3.5.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ from dataclasses import dataclass, field
9
+ from typing import Callable
10
+
11
+
12
+ @dataclass
13
+ class CircuitState:
14
+ """State for a single circuit."""
15
+
16
+ name: str
17
+ risk: float = 0.0
18
+ threshold: float = 1.0
19
+ increment: float = 0.1
20
+ tripped: bool = False
21
+ trip_count: int = 0
22
+ last_failure: str = ""
23
+ metadata: dict[str, object] = field(default_factory=dict)
24
+
25
+
26
+ class CircuitBreaker:
27
+ """Manage multiple named circuit breakers."""
28
+
29
+ def __init__(self) -> None:
30
+ self._circuits: dict[str, CircuitState] = {}
31
+ self._on_trip: list[Callable[[str, CircuitState], None]] = []
32
+
33
+ def register(
34
+ self,
35
+ name: str,
36
+ threshold: float = 1.0,
37
+ increment: float = 0.1,
38
+ ) -> CircuitState:
39
+ circuit = CircuitState(name=name, threshold=threshold, increment=increment)
40
+ self._circuits[name] = circuit
41
+ return circuit
42
+
43
+ def record_failure(self, name: str, reason: str = "") -> CircuitState:
44
+ circuit = self._circuits.get(name)
45
+ if circuit is None:
46
+ raise KeyError(f"Circuit {name!r} not registered")
47
+ if circuit.tripped:
48
+ return circuit
49
+ circuit.risk += circuit.increment
50
+ circuit.last_failure = reason
51
+ if circuit.risk >= circuit.threshold:
52
+ circuit.tripped = True
53
+ circuit.trip_count += 1
54
+ for cb in self._on_trip:
55
+ cb(name, circuit)
56
+ return circuit
57
+
58
+ def record_success(self, name: str) -> CircuitState:
59
+ circuit = self._circuits.get(name)
60
+ if circuit is None:
61
+ raise KeyError(f"Circuit {name!r} not registered")
62
+ circuit.risk = max(0.0, circuit.risk - circuit.increment)
63
+ return circuit
64
+
65
+ def is_tripped(self, name: str) -> bool:
66
+ circuit = self._circuits.get(name)
67
+ return circuit is not None and circuit.tripped
68
+
69
+ def reset(self, name: str) -> CircuitState:
70
+ circuit = self._circuits.get(name)
71
+ if circuit is None:
72
+ raise KeyError(f"Circuit {name!r} not registered")
73
+ circuit.risk = 0.0
74
+ circuit.tripped = False
75
+ circuit.last_failure = ""
76
+ return circuit
77
+
78
+ def on_trip(self, callback: Callable[[str, CircuitState], None]) -> None:
79
+ self._on_trip.append(callback)
80
+
81
+ def get(self, name: str) -> CircuitState | None:
82
+ return self._circuits.get(name)
83
+
84
+ def all(self) -> list[CircuitState]:
85
+ return list(self._circuits.values())
86
+
87
+ def count(self) -> int:
88
+ return len(self._circuits)
@@ -0,0 +1,101 @@
1
+ """B67. Human-in-the-Loop Clarification.
2
+
3
+ Ask clarifying questions before expensive research.
4
+ Source: DeepResearch-Agent pattern.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ from dataclasses import dataclass, field
9
+ from enum import Enum, auto
10
+
11
+
12
+ class ClarificationType(Enum):
13
+ SCOPE = auto() # "Do you mean X or Y?"
14
+ DEPTH = auto() # "Brief overview or deep dive?"
15
+ AUDIENCE = auto() # "For beginners or experts?"
16
+ FORMAT = auto() # "Report, bullet points, or code?"
17
+ TIMEFRAME = auto() # "Recent only or historical?"
18
+
19
+
20
+ @dataclass
21
+ class Clarification:
22
+ id: str
23
+ type: ClarificationType
24
+ question: str
25
+ options: list[str] = field(default_factory=list)
26
+ default: str = ""
27
+ answer: str = ""
28
+
29
+
30
+ @dataclass
31
+ class ClarificationResult:
32
+ clarifications: list[Clarification] = field(default_factory=list)
33
+ refined_query: str = ""
34
+ skipped: bool = False
35
+
36
+
37
+ class ClarificationGate:
38
+ """Generate and collect clarifying questions before research."""
39
+
40
+ AMBIGUOUS_WORDS = {"best", "good", "fast", "simple", "advanced", "modern", "latest"}
41
+
42
+ def generate(self, query: str) -> list[Clarification]:
43
+ """Generate clarifying questions based on query analysis."""
44
+ clarifications: list[Clarification] = []
45
+ words = set(query.lower().split())
46
+
47
+ # Check for ambiguous terms
48
+ ambiguous = words & self.AMBIGUOUS_WORDS
49
+ if ambiguous:
50
+ clarifications.append(Clarification(
51
+ id="clar_1",
52
+ type=ClarificationType.SCOPE,
53
+ question=f"You mentioned '{', '.join(ambiguous)}'. Can you be more specific?",
54
+ default="general overview",
55
+ ))
56
+
57
+ # Check for depth signals
58
+ if not any(w in query.lower() for w in ["deep", "detailed", "overview", "brief", "summary"]):
59
+ clarifications.append(Clarification(
60
+ id="clar_2",
61
+ type=ClarificationType.DEPTH,
62
+ question="How deep should the research go?",
63
+ options=["Brief overview", "Standard depth", "Deep dive"],
64
+ default="Standard depth",
65
+ ))
66
+
67
+ # Check for format
68
+ if not any(w in query.lower() for w in ["report", "bullet", "code", "table", "list"]):
69
+ clarifications.append(Clarification(
70
+ id="clar_3",
71
+ type=ClarificationType.FORMAT,
72
+ question="What format would you prefer?",
73
+ options=["Markdown report", "Bullet points", "Code examples"],
74
+ default="Markdown report",
75
+ ))
76
+
77
+ # Check for timeframe
78
+ if not any(w in query.lower() for w in ["2024", "2025", "2026", "recent", "latest", "historical"]):
79
+ clarifications.append(Clarification(
80
+ id="clar_4",
81
+ type=ClarificationType.TIMEFRAME,
82
+ question="What timeframe should I focus on?",
83
+ options=["Recent (2025-2026)", "Last 3 years", "Historical"],
84
+ default="Recent (2025-2026)",
85
+ ))
86
+
87
+ return clarifications
88
+
89
+ def refine_query(self, query: str, answers: dict[str, str]) -> str:
90
+ """Refine the original query based on clarification answers."""
91
+ refined = query
92
+ for clar_id, answer in answers.items():
93
+ if answer and answer != "general overview":
94
+ refined += f" ({answer})"
95
+ return refined
96
+
97
+ def should_ask(self, query: str, auto_skip: bool = False) -> bool:
98
+ """Determine if clarification is needed."""
99
+ if auto_skip:
100
+ return False
101
+ return len(self.generate(query)) > 0