specsmith 0.2.4.dev54__py3-none-any.whl → 0.2.4.dev589__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 (236) hide show
  1. epistemic/__init__.py +174 -0
  2. epistemic/belief.py +364 -0
  3. epistemic/certainty.py +199 -0
  4. epistemic/failure_graph.py +156 -0
  5. epistemic/py.typed +0 -0
  6. epistemic/recovery.py +223 -0
  7. epistemic/session.py +428 -0
  8. epistemic/stress_tester.py +355 -0
  9. epistemic/trace.py +209 -0
  10. specsmith/__init__.py +2 -2
  11. specsmith/__main__.py +1 -1
  12. specsmith/agent/__init__.py +0 -0
  13. specsmith/agent/broker.py +694 -0
  14. specsmith/agent/chat_runner.py +627 -0
  15. specsmith/agent/cleanup.py +314 -0
  16. specsmith/agent/context_seed.py +215 -0
  17. specsmith/agent/core.py +98 -0
  18. specsmith/agent/dispatch/__init__.py +35 -0
  19. specsmith/agent/dispatch/_status.py +18 -0
  20. specsmith/agent/dispatch/dag.py +292 -0
  21. specsmith/agent/dispatch/dispatcher.py +498 -0
  22. specsmith/agent/dispatch/events.py +277 -0
  23. specsmith/agent/dispatch/result.py +64 -0
  24. specsmith/agent/endpoints.py +495 -0
  25. specsmith/agent/events.py +254 -0
  26. specsmith/agent/execution_profiles.py +274 -0
  27. specsmith/agent/fallback.py +142 -0
  28. specsmith/agent/hf_leaderboard.py +947 -0
  29. specsmith/agent/hf_sync.py +181 -0
  30. specsmith/agent/indexer.py +72 -0
  31. specsmith/agent/llm_client.py +550 -0
  32. specsmith/agent/mcp.py +387 -0
  33. specsmith/agent/memory.py +81 -0
  34. specsmith/agent/model_intelligence.py +293 -0
  35. specsmith/agent/model_profiles.py +494 -0
  36. specsmith/agent/orchestrator.py +411 -0
  37. specsmith/agent/permissions.py +278 -0
  38. specsmith/agent/profiles.py +691 -0
  39. specsmith/agent/provider_registry.py +662 -0
  40. specsmith/agent/repl.py +174 -0
  41. specsmith/agent/router.py +78 -0
  42. specsmith/agent/rules.py +62 -0
  43. specsmith/agent/runner.py +716 -0
  44. specsmith/agent/safety.py +109 -0
  45. specsmith/agent/spawner.py +179 -0
  46. specsmith/agent/suggester.py +264 -0
  47. specsmith/agent/teams.py +103 -0
  48. specsmith/agent/tools.py +693 -0
  49. specsmith/agent/verifier.py +123 -0
  50. specsmith/agent/voice.py +143 -0
  51. specsmith/approvals.py +124 -0
  52. specsmith/architect.py +1 -1
  53. specsmith/auditor.py +928 -58
  54. specsmith/auth.py +279 -0
  55. specsmith/block_export.py +106 -0
  56. specsmith/channel.py +112 -0
  57. specsmith/ci_manager.py +498 -0
  58. specsmith/cli.py +11084 -246
  59. specsmith/commands/__init__.py +1 -1
  60. specsmith/commands/intelligence.py +416 -0
  61. specsmith/commands/issues_policy.py +241 -0
  62. specsmith/commands/reporting.py +891 -0
  63. specsmith/compliance/__init__.py +73 -0
  64. specsmith/compliance/_compat.py +281 -0
  65. specsmith/compliance/checker.py +311 -0
  66. specsmith/compliance/evidence.py +497 -0
  67. specsmith/compliance/regulations.py +798 -0
  68. specsmith/compliance/reporter.py +284 -0
  69. specsmith/compressor.py +30 -15
  70. specsmith/config.py +409 -17
  71. specsmith/console_utils.py +63 -0
  72. specsmith/context_orchestrator.py +290 -0
  73. specsmith/context_window.py +274 -0
  74. specsmith/credit_analyzer.py +1 -1
  75. specsmith/credits.py +11 -2
  76. specsmith/dashboard.py +125 -0
  77. specsmith/datasources/__init__.py +14 -0
  78. specsmith/datasources/base.py +141 -0
  79. specsmith/datasources/citations.py +83 -0
  80. specsmith/datasources/fpd.py +72 -0
  81. specsmith/datasources/odp.py +75 -0
  82. specsmith/datasources/patentsview.py +272 -0
  83. specsmith/datasources/pfw.py +81 -0
  84. specsmith/datasources/ppubs.py +111 -0
  85. specsmith/datasources/ptab.py +126 -0
  86. specsmith/differ.py +45 -2
  87. specsmith/doctor.py +146 -35
  88. specsmith/drive.py +126 -0
  89. specsmith/editor.py +277 -0
  90. specsmith/epistemic/__init__.py +62 -0
  91. specsmith/epistemic/belief.py +393 -0
  92. specsmith/epistemic/certainty.py +252 -0
  93. specsmith/epistemic/failure_graph.py +214 -0
  94. specsmith/epistemic/recovery.py +349 -0
  95. specsmith/epistemic/stress_tester.py +440 -0
  96. specsmith/esdb/__init__.py +194 -0
  97. specsmith/esdb/_license.py +226 -0
  98. specsmith/esdb/bridge.py +65 -0
  99. specsmith/esdb/sqlite_store.py +481 -0
  100. specsmith/eval/__init__.py +111 -0
  101. specsmith/eval/builtins.py +95 -0
  102. specsmith/eval/runner.py +208 -0
  103. specsmith/executor.py +1 -1
  104. specsmith/exporter.py +44 -7
  105. specsmith/governance_logic.py +1068 -0
  106. specsmith/governance_store.py +173 -0
  107. specsmith/governance_yaml.py +544 -0
  108. specsmith/governed_pr.py +76 -0
  109. specsmith/gui/__init__.py +9 -0
  110. specsmith/gui/app.py +50 -0
  111. specsmith/gui/main_window.py +317 -0
  112. specsmith/gui/session_tab.py +346 -0
  113. specsmith/gui/theme.py +369 -0
  114. specsmith/gui/widgets/__init__.py +3 -0
  115. specsmith/gui/widgets/chat_view.py +230 -0
  116. specsmith/gui/widgets/input_bar.py +227 -0
  117. specsmith/gui/widgets/provider_bar.py +150 -0
  118. specsmith/gui/widgets/token_meter.py +132 -0
  119. specsmith/gui/widgets/tool_panel.py +117 -0
  120. specsmith/gui/widgets/update_checker.py +81 -0
  121. specsmith/gui/worker.py +230 -0
  122. specsmith/history_search.py +159 -0
  123. specsmith/importer.py +499 -66
  124. specsmith/instinct.py +330 -0
  125. specsmith/integrations/__init__.py +25 -6
  126. specsmith/integrations/{warp.py → agent_skill.py} +19 -9
  127. specsmith/integrations/aider.py +1 -1
  128. specsmith/integrations/base.py +2 -2
  129. specsmith/integrations/claude_code.py +1 -1
  130. specsmith/integrations/codity.py +311 -0
  131. specsmith/integrations/copilot.py +1 -1
  132. specsmith/integrations/cursor.py +1 -1
  133. specsmith/integrations/gemini.py +1 -1
  134. specsmith/integrations/windsurf.py +1 -1
  135. specsmith/issue_reporter.py +373 -0
  136. specsmith/languages.py +332 -0
  137. specsmith/ledger.py +126 -11
  138. specsmith/mcp_generator.py +210 -0
  139. specsmith/mcp_server.py +809 -0
  140. specsmith/migrations/__init__.py +138 -0
  141. specsmith/migrations/m001_governance_yaml.py +133 -0
  142. specsmith/migrations/m002_agents_slim.py +106 -0
  143. specsmith/migrations/m003_compliance_init.py +108 -0
  144. specsmith/migrations/m004_ledger_esdb.py +91 -0
  145. specsmith/migrations/m005_agent_run_tool.py +191 -0
  146. specsmith/migrations/m006_session_governance.py +230 -0
  147. specsmith/migrations/runner.py +116 -0
  148. specsmith/ollama_cmds.py +409 -0
  149. specsmith/patent.py +287 -0
  150. specsmith/paths.py +152 -0
  151. specsmith/phase.py +627 -0
  152. specsmith/plugins.py +103 -2
  153. specsmith/policy.py +125 -0
  154. specsmith/profiles.py +425 -0
  155. specsmith/rate_limits.py +153 -4
  156. specsmith/recover.py +69 -0
  157. specsmith/releaser.py +1 -1
  158. specsmith/requirements.py +119 -28
  159. specsmith/requirements_parser.py +88 -0
  160. specsmith/retrieval.py +150 -0
  161. specsmith/risk.py +148 -0
  162. specsmith/safe_write.py +188 -0
  163. specsmith/scaffolder.py +251 -7
  164. specsmith/serve.py +808 -0
  165. specsmith/session.py +1 -1
  166. specsmith/session_init.py +280 -0
  167. specsmith/session_store.py +144 -0
  168. specsmith/skills/__init__.py +282 -0
  169. specsmith/skills/ai_agents.py +1439 -0
  170. specsmith/skills/cloud.py +457 -0
  171. specsmith/skills/corporate.py +901 -0
  172. specsmith/skills/cross_platform.py +570 -0
  173. specsmith/skills/data_engineering.py +960 -0
  174. specsmith/skills/devops.py +887 -0
  175. specsmith/skills/docs.py +1002 -0
  176. specsmith/skills/embedded.py +1361 -0
  177. specsmith/skills/governance.py +1247 -0
  178. specsmith/skills/hardware.py +1442 -0
  179. specsmith/skills/mobile.py +420 -0
  180. specsmith/skills/platform_engineering.py +1391 -0
  181. specsmith/skills/productivity.py +336 -0
  182. specsmith/skills/software_engineering.py +1521 -0
  183. specsmith/skills/specsmith_skills.py +267 -0
  184. specsmith/skills/ssh.py +377 -0
  185. specsmith/skills/web_backend.py +1427 -0
  186. specsmith/skills_builder.py +306 -0
  187. specsmith/sync.py +631 -0
  188. specsmith/templates/agents.md.j2 +150 -168
  189. specsmith/templates/community/contributing.md.j2 +2 -2
  190. specsmith/templates/community/pull_request_template.md.j2 +1 -1
  191. specsmith/templates/docs/requirements.md.j2 +7 -7
  192. specsmith/templates/docs/test-spec.md.j2 +3 -3
  193. specsmith/templates/editorconfig.j2 +10 -10
  194. specsmith/templates/gitattributes.j2 +13 -13
  195. specsmith/templates/gitignore.j2 +52 -19
  196. specsmith/templates/governance/belief-registry.md.j2 +77 -0
  197. specsmith/templates/governance/context-budget.md.j2 +3 -2
  198. specsmith/templates/governance/epistemic-axioms.md.j2 +105 -0
  199. specsmith/templates/governance/failure-modes.md.j2 +92 -0
  200. specsmith/templates/governance/lifecycle.md.j2 +44 -0
  201. specsmith/templates/governance/rules.md.j2 +27 -0
  202. specsmith/templates/governance/{workflow.md.j2 → session-protocol.md.j2} +1 -1
  203. specsmith/templates/governance/uncertainty-map.md.j2 +76 -0
  204. specsmith/templates/js/package.json.j2 +2 -2
  205. specsmith/templates/ledger.md.j2 +3 -3
  206. specsmith/templates/python/pyproject.toml.j2 +9 -2
  207. specsmith/templates/readme.md.j2 +2 -2
  208. specsmith/templates/rust/Cargo.toml.j2 +2 -2
  209. specsmith/templates/scripts/setup.cmd.j2 +25 -2
  210. specsmith/templates/scripts/setup.sh.j2 +24 -3
  211. specsmith/tool_installer.py +600 -0
  212. specsmith/toolrules.py +404 -0
  213. specsmith/tools.py +309 -13
  214. specsmith/trace.py +302 -0
  215. specsmith/transcripts.py +140 -0
  216. specsmith/updater.py +69 -18
  217. specsmith/upgrader.py +161 -31
  218. specsmith/validator.py +132 -7
  219. specsmith/vcs/__init__.py +1 -1
  220. specsmith/vcs/base.py +1 -1
  221. specsmith/vcs/bitbucket.py +1 -1
  222. specsmith/vcs/github.py +12 -12
  223. specsmith/vcs/gitlab.py +1 -1
  224. specsmith/vcs_commands.py +40 -4
  225. specsmith/wi_store.py +382 -0
  226. specsmith/wireframes.py +86 -0
  227. specsmith/workspace.py +203 -0
  228. specsmith-0.2.4.dev589.dist-info/METADATA +1233 -0
  229. specsmith-0.2.4.dev589.dist-info/RECORD +255 -0
  230. {specsmith-0.2.4.dev54.dist-info → specsmith-0.2.4.dev589.dist-info}/licenses/LICENSE +1 -1
  231. {specsmith-0.2.4.dev54.dist-info → specsmith-0.2.4.dev589.dist-info}/top_level.txt +1 -0
  232. specsmith/templates/docs/workflow.md.j2 +0 -15
  233. specsmith-0.2.4.dev54.dist-info/METADATA +0 -198
  234. specsmith-0.2.4.dev54.dist-info/RECORD +0 -88
  235. {specsmith-0.2.4.dev54.dist-info → specsmith-0.2.4.dev589.dist-info}/WHEEL +0 -0
  236. {specsmith-0.2.4.dev54.dist-info → specsmith-0.2.4.dev589.dist-info}/entry_points.txt +0 -0
epistemic/__init__.py ADDED
@@ -0,0 +1,174 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2026 Layer1Labs Silicon, Inc. All rights reserved.
3
+ """Applied Epistemic Engineering — standalone Python library.
4
+
5
+ Install
6
+ -------
7
+ pip install specsmith # installs both specsmith and epistemic
8
+
9
+ The ``epistemic`` package is co-installed with specsmith and has **zero
10
+ external dependencies** beyond Python 3.10+. It can be imported by any
11
+ Python project to incorporate AEE practices.
12
+
13
+ Quick start
14
+ -----------
15
+ The simplest entry point is ``AEESession``::
16
+
17
+ from epistemic import AEESession
18
+
19
+ session = AEESession(project_name="my-research")
20
+ session.add_belief(
21
+ artifact_id="HYP-001",
22
+ propositions=["Our decipherment hypothesis is internally consistent"],
23
+ epistemic_boundary=["Corpus: 500 inscriptions, phase 1 only"],
24
+ domain="linguistics",
25
+ )
26
+ result = session.run()
27
+ print(result.summary())
28
+
29
+ For non-trivial projects you'll want to:
30
+
31
+ 1. ``add_belief()`` — register claims, hypotheses, requirements
32
+ 2. ``add_evidence()`` / ``mark_covered()`` — record what's been validated
33
+ 3. ``accept()`` — elevate to ACCEPTED status (binds stress-testing)
34
+ 4. ``run()`` — execute the full AEE pipeline
35
+ 5. ``seal()`` — cryptographically seal the epistemic state
36
+
37
+ What AEE provides
38
+ -----------------
39
+ Applied Epistemic Engineering treats belief systems like code: codable,
40
+ testable, and deployable. Its formal mathematical foundations include:
41
+
42
+ **Five axioms:**
43
+ 1. Observability — every belief must be inspectable (hidden assumptions → stop)
44
+ 2. Falsifiability — every belief must be challengeable (dogma ≠ engineering)
45
+ 3. Irreducibility — decompose to atomic primitives (compound claims → Logic Knots)
46
+ 4. Reconstructability — every failed belief can be reconstructed (scope may narrow)
47
+ 5. Convergence — S+R iteration always converges to equilibrium E
48
+
49
+ **Core process: Frame → Disassemble → Stress-Test → Reconstruct**
50
+
51
+ **Key operators:**
52
+ S(B) = StressTester — adversarial challenge function
53
+ R(K) = RecoveryOperator — resolves Logic Knots K
54
+ G = FailureModeGraph — maps stress-test → breakpoint relations
55
+ E = Equilibrium point where S(G) yields no new failures
56
+ C = Certainty score ∈ [0, 1] from CertaintyEngine
57
+
58
+ Real-world use cases
59
+ --------------------
60
+ Software engineering: requirements as BeliefArtifacts, tests as falsifiers
61
+ Linguistics research: hypotheses as BeliefArtifacts, experiments as evidence
62
+ AI alignment: model assumptions as BeliefArtifacts, red-teaming as stress-tests
63
+ Policy/compliance: regulations as BeliefArtifacts, audits as failure modes
64
+ Patent prosecution: claims as BeliefArtifacts, prior art as stress-tests
65
+
66
+ glossa-lab example
67
+ ------------------
68
+ from epistemic import AEESession, ConfidenceLevel, BeliefStatus
69
+ from pathlib import Path
70
+
71
+ # Create a session for Indus script decipherment research
72
+ session = AEESession(
73
+ project_name="glossa-lab-indus",
74
+ threshold=0.65,
75
+ state_file=Path(".epistemic/indus.json"),
76
+ trace_dir=Path(".epistemic"),
77
+ )
78
+
79
+ # Load previous session state
80
+ session.load()
81
+
82
+ # Register competing hypotheses
83
+ session.add_belief(
84
+ artifact_id="HYP-IND-001",
85
+ propositions=["The Indus script is logosyllabic"],
86
+ epistemic_boundary=["Mahadevan corpus, 2977 inscriptions, 1977 edition"],
87
+ domain="epigraphy",
88
+ status=BeliefStatus.ACCEPTED,
89
+ confidence=ConfidenceLevel.LOW,
90
+ )
91
+ session.add_belief(
92
+ artifact_id="HYP-IND-002",
93
+ propositions=["Indus sign frequency follows Zipf distribution"],
94
+ epistemic_boundary=["Same corpus as HYP-IND-001"],
95
+ inferential_links=["HYP-IND-001"], # confidence propagates from HYP-IND-001
96
+ domain="epigraphy",
97
+ status=BeliefStatus.ACCEPTED,
98
+ )
99
+
100
+ # Record experimental evidence
101
+ session.add_evidence("HYP-IND-001", "Rao et al. 2009 — conditional entropy study")
102
+ session.add_evidence("HYP-IND-002", "Mahadevan 1977 — sign frequency tables")
103
+ session.mark_covered("HYP-IND-001") # has experimental coverage
104
+
105
+ # Run the full AEE pipeline
106
+ result = session.run()
107
+ print(result.summary())
108
+
109
+ # If there are Logic Knots (competing theories), inspect them:
110
+ for id1, id2, reason in result.stress_result.logic_knots:
111
+ print(f"Conflict: {id1} vs {id2}: {reason}")
112
+
113
+ # See the certainty map by domain
114
+ for score in result.certainty_report.scores:
115
+ print(f" {score.artifact_id}: {score.propagated_score:.2f} [{score.label}]")
116
+
117
+ # Seal this session's epistemic state
118
+ session.seal("stress-test", "Indus decipherment hypothesis stress-test v2")
119
+ session.save()
120
+
121
+ References
122
+ ----------
123
+ - AEE: https://appliedepistemicengineering.com/
124
+ - ARE: https://github.com/organvm-i-theoria/auto-revision-epistemic-engine
125
+ - VERITAS: https://github.com/AionSystem/VERITAS
126
+ - Springer: https://doi.org/10.1007/s11948-023-00451-3
127
+ """
128
+
129
+ from __future__ import annotations
130
+
131
+ from epistemic.belief import (
132
+ BeliefArtifact,
133
+ BeliefStatus,
134
+ ConfidenceLevel,
135
+ FailureMode,
136
+ FailureSeverity,
137
+ beliefs_from_dicts,
138
+ parse_requirements_as_beliefs,
139
+ )
140
+ from epistemic.certainty import ArtifactCertainty, CertaintyEngine, CertaintyReport
141
+ from epistemic.failure_graph import FailureModeGraph, GraphNode
142
+ from epistemic.recovery import RecoveryOperator, RecoveryProposal, RecoveryStrategy
143
+ from epistemic.session import AEEResult, AEESession
144
+ from epistemic.stress_tester import StressTester, StressTestResult
145
+
146
+ __version__ = "0.3.0"
147
+
148
+ __all__ = [
149
+ # Session facade (primary entry point)
150
+ "AEESession",
151
+ "AEEResult",
152
+ # Belief model
153
+ "BeliefArtifact",
154
+ "BeliefStatus",
155
+ "ConfidenceLevel",
156
+ "FailureMode",
157
+ "FailureSeverity",
158
+ "parse_requirements_as_beliefs",
159
+ "beliefs_from_dicts",
160
+ # Stress testing
161
+ "StressTester",
162
+ "StressTestResult",
163
+ # Failure graph
164
+ "FailureModeGraph",
165
+ "GraphNode",
166
+ # Recovery
167
+ "RecoveryOperator",
168
+ "RecoveryProposal",
169
+ "RecoveryStrategy",
170
+ # Certainty
171
+ "CertaintyEngine",
172
+ "CertaintyReport",
173
+ "ArtifactCertainty",
174
+ ]
epistemic/belief.py ADDED
@@ -0,0 +1,364 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2026 Layer1Labs Silicon, Inc. All rights reserved.
3
+ """Belief Artifact model — core AEE primitive.
4
+
5
+ This module is part of the standalone ``epistemic`` library and has
6
+ **zero dependencies outside Python's stdlib**. It can be imported by any
7
+ Python 3.10+ project, independent of specsmith.
8
+
9
+ pip install specsmith # installs both specsmith and epistemic
10
+ from epistemic import BeliefArtifact, StressTester, CertaintyEngine
11
+
12
+ Applied Epistemic Engineering (AEE) treats belief systems like code:
13
+ codable, testable, and deployable. A BeliefArtifact is the fundamental
14
+ unit — a structured claim about what a system does, should do, or must not
15
+ do, with an explicit epistemic boundary (the assumptions within which it holds)
16
+ and a confidence level.
17
+
18
+ Five foundational AEE axioms
19
+ -----------------------------
20
+ 1. Observability — Every belief must be inspectable. Hidden assumptions
21
+ are a stop condition.
22
+ 2. Falsifiability — Every belief must be challengeable. Unchallenged claims
23
+ are dogma, not engineering.
24
+ 3. Irreducibility — Decompose beliefs to atomic primitives. Compound claims
25
+ often hide Logic Knots.
26
+ 4. Reconstructability — Every failed belief can be reconstructed. Recovery is
27
+ always possible; scope may need to narrow.
28
+ 5. Convergence — Systematic S+R application always converges to an
29
+ Equilibrium Point E where S(G) yields no new failures.
30
+
31
+ Real-world applications
32
+ -----------------------
33
+ - **Software engineering**: Requirements as BeliefArtifacts, test gaps as
34
+ stress-test failures, conflicting specs as Logic Knots.
35
+ - **Research**: Hypotheses as BeliefArtifacts, experimental results as
36
+ evidence, competing theories as Logic Knots.
37
+ - **AI alignment**: Model assumptions as BeliefArtifacts, red-teaming as
38
+ stress-testing, hallucinations as confidence failures.
39
+ - **Policy / compliance**: Regulatory claims as BeliefArtifacts, audit
40
+ findings as failure modes, contradictory requirements as Logic Knots.
41
+
42
+ References
43
+ ----------
44
+ - AEE: https://appliedepistemicengineering.com/
45
+ - ARE framework: https://github.com/organvm-i-theoria/auto-revision-epistemic-engine
46
+ - VERITAS/CERTUS: https://github.com/AionSystem/VERITAS
47
+ - AI as Epistemic Technology (Springer): https://doi.org/10.1007/s11948-023-00451-3
48
+ """
49
+
50
+ from __future__ import annotations
51
+
52
+ import re
53
+ from dataclasses import dataclass, field
54
+ from enum import Enum
55
+ from pathlib import Path
56
+
57
+
58
+ class ConfidenceLevel(str, Enum):
59
+ """Epistemic confidence assigned to a BeliefArtifact.
60
+
61
+ Maps to a numeric score used by CertaintyEngine:
62
+ UNKNOWN → 0.0 (no evidence, new claim)
63
+ LOW → 0.25 (asserted but not stress-tested)
64
+ MEDIUM → 0.55 (stress-tested with minor failures resolved)
65
+ HIGH → 0.85 (stress-tested, equilibrium reached, evidence present)
66
+ """
67
+
68
+ UNKNOWN = "unknown"
69
+ LOW = "low"
70
+ MEDIUM = "medium"
71
+ HIGH = "high"
72
+
73
+ @property
74
+ def score(self) -> float:
75
+ """Numeric score for propagation calculations."""
76
+ return _CONFIDENCE_SCORES[self]
77
+
78
+
79
+ _CONFIDENCE_SCORES: dict[ConfidenceLevel, float] = {
80
+ ConfidenceLevel.UNKNOWN: 0.0,
81
+ ConfidenceLevel.LOW: 0.25,
82
+ ConfidenceLevel.MEDIUM: 0.55,
83
+ ConfidenceLevel.HIGH: 0.85,
84
+ }
85
+
86
+
87
+ class BeliefStatus(str, Enum):
88
+ """Lifecycle status of a BeliefArtifact."""
89
+
90
+ DRAFT = "draft"
91
+ ACCEPTED = "accepted"
92
+ STRESS_TESTED = "stress-tested"
93
+ RECONSTRUCTED = "reconstructed"
94
+ DEPRECATED = "deprecated"
95
+
96
+
97
+ class FailureSeverity(str, Enum):
98
+ """Severity of a failure mode in the Failure-Mode Graph."""
99
+
100
+ CRITICAL = "critical"
101
+ HIGH = "high"
102
+ MEDIUM = "medium"
103
+ LOW = "low"
104
+
105
+
106
+ @dataclass
107
+ class FailureMode:
108
+ """A single failure mode discovered by the StressTester.
109
+
110
+ Represents a stress-test → breakpoint relation in the Failure-Mode Graph G.
111
+ """
112
+
113
+ artifact_id: str
114
+ challenge: str
115
+ breakpoint: str
116
+ severity: FailureSeverity = FailureSeverity.MEDIUM
117
+ recovery_hint: str = ""
118
+ resolved: bool = False
119
+
120
+
121
+ @dataclass
122
+ class BeliefArtifact:
123
+ """A codable, testable, deployable unit of knowledge.
124
+
125
+ The fundamental unit of AEE. Can represent:
126
+ - A software requirement (REQ-CLI-001)
127
+ - A research hypothesis (HYP-IND-001)
128
+ - An architectural decision (DEC-001)
129
+ - A compliance claim (COMP-GDPR-001)
130
+ - Any structured belief that must be Observable and Falsifiable
131
+
132
+ Examples
133
+ --------
134
+ Software requirement::
135
+
136
+ req = BeliefArtifact(
137
+ artifact_id="REQ-API-001",
138
+ propositions=["The API must return HTTP 200 for valid requests"],
139
+ epistemic_boundary=["Platform: all", "Auth: JWT required"],
140
+ confidence=ConfidenceLevel.ACCEPTED,
141
+ status=BeliefStatus.ACCEPTED,
142
+ )
143
+
144
+ Research hypothesis::
145
+
146
+ hyp = BeliefArtifact(
147
+ artifact_id="HYP-IND-001",
148
+ propositions=["Indus script uses logosyllabic encoding",
149
+ "Sign frequency distribution follows Zipf's law"],
150
+ epistemic_boundary=["Corpus: Mahadevan 2977 inscriptions, 1977"],
151
+ confidence=ConfidenceLevel.LOW,
152
+ status=BeliefStatus.ACCEPTED,
153
+ )
154
+ """
155
+
156
+ artifact_id: str
157
+ propositions: list[str] = field(default_factory=list)
158
+ epistemic_boundary: list[str] = field(default_factory=list)
159
+ inferential_links: list[str] = field(default_factory=list)
160
+ confidence: ConfidenceLevel = ConfidenceLevel.UNKNOWN
161
+ status: BeliefStatus = BeliefStatus.DRAFT
162
+ failure_modes: list[FailureMode] = field(default_factory=list)
163
+ source_text: str = ""
164
+ component: str = ""
165
+ priority: str = ""
166
+ # Optional metadata for non-specsmith uses
167
+ domain: str = "" # e.g., "linguistics", "software", "policy"
168
+ evidence: list[str] = field(default_factory=list) # evidence citations
169
+
170
+ @property
171
+ def is_accepted(self) -> bool:
172
+ return self.status in (
173
+ BeliefStatus.ACCEPTED,
174
+ BeliefStatus.STRESS_TESTED,
175
+ BeliefStatus.RECONSTRUCTED,
176
+ )
177
+
178
+ @property
179
+ def has_failures(self) -> bool:
180
+ return any(not fm.resolved for fm in self.failure_modes)
181
+
182
+ @property
183
+ def unresolved_failures(self) -> list[FailureMode]:
184
+ return [fm for fm in self.failure_modes if not fm.resolved]
185
+
186
+ @property
187
+ def critical_failures(self) -> list[FailureMode]:
188
+ return [
189
+ fm
190
+ for fm in self.failure_modes
191
+ if not fm.resolved and fm.severity == FailureSeverity.CRITICAL
192
+ ]
193
+
194
+ def add_evidence(self, citation: str) -> BeliefArtifact:
195
+ """Add an evidence citation and elevate confidence if appropriate.
196
+
197
+ Returns self for chaining.
198
+ """
199
+ self.evidence.append(citation)
200
+ if self.confidence == ConfidenceLevel.UNKNOWN and self.evidence:
201
+ self.confidence = ConfidenceLevel.LOW
202
+ return self
203
+
204
+ def to_dict(self) -> dict[str, object]:
205
+ """Serialise to a plain dict (JSON-compatible)."""
206
+ return {
207
+ "artifact_id": self.artifact_id,
208
+ "propositions": self.propositions,
209
+ "epistemic_boundary": self.epistemic_boundary,
210
+ "inferential_links": self.inferential_links,
211
+ "confidence": self.confidence.value,
212
+ "status": self.status.value,
213
+ "source_text": self.source_text,
214
+ "component": self.component,
215
+ "priority": self.priority,
216
+ "domain": self.domain,
217
+ "evidence": self.evidence,
218
+ "failure_count": len(self.failure_modes),
219
+ "critical_count": len(self.critical_failures),
220
+ }
221
+
222
+
223
+ # ---------------------------------------------------------------------------
224
+ # Markdown parser — converts REQUIREMENTS.md entries to BeliefArtifacts
225
+ # ---------------------------------------------------------------------------
226
+
227
+ _REQ_HEADING = re.compile(r"^#{1,3}\s+(REQ-([A-Z0-9_]+)-(\d+))\s*(?:—\s*(.+))?$")
228
+ _FIELD_LINE = re.compile(r"^\s*-\s+\*\*(.+?)\*\*:\s*(.+)$")
229
+
230
+
231
+ def parse_requirements_as_beliefs(path: Path) -> list[BeliefArtifact]:
232
+ """Parse a REQUIREMENTS.md file and return a list of BeliefArtifacts.
233
+
234
+ Works with both specsmith-generated REQUIREMENTS.md files and
235
+ any markdown file that uses REQ-COMPONENT-NNN heading format.
236
+ """
237
+ if not path.exists():
238
+ return []
239
+
240
+ content = path.read_text(encoding="utf-8")
241
+ artifacts: list[BeliefArtifact] = []
242
+ current: BeliefArtifact | None = None
243
+ current_desc = ""
244
+
245
+ for line in content.splitlines():
246
+ m = _REQ_HEADING.match(line)
247
+ if m:
248
+ if current is not None:
249
+ _finalise(current, current_desc)
250
+ artifacts.append(current)
251
+ req_id = m.group(1)
252
+ component = m.group(2)
253
+ desc_inline = m.group(4) or ""
254
+ current = BeliefArtifact(
255
+ artifact_id=req_id,
256
+ component=component,
257
+ source_text=desc_inline,
258
+ )
259
+ current_desc = desc_inline
260
+ continue
261
+
262
+ if current is None:
263
+ continue
264
+
265
+ fm = _FIELD_LINE.match(line)
266
+ if fm:
267
+ key = fm.group(1).lower()
268
+ val = fm.group(2).strip()
269
+ if key in ("description", "desc"):
270
+ current_desc = val
271
+ current.source_text = val
272
+ elif key == "priority":
273
+ current.priority = val
274
+ elif key == "status":
275
+ current.status = _map_status(val)
276
+ current.confidence = _infer_confidence(current.status)
277
+ elif key in ("platform", "platforms", "boundary"):
278
+ current.epistemic_boundary.append(f"{key.capitalize()}: {val}")
279
+ elif key == "domain":
280
+ current.domain = val
281
+ elif key == "evidence":
282
+ current.evidence.append(val)
283
+ else:
284
+ stripped = line.strip().lstrip("-").strip()
285
+ if stripped and not stripped.startswith("#") and not current_desc:
286
+ current_desc = stripped
287
+
288
+ if current is not None:
289
+ _finalise(current, current_desc)
290
+ artifacts.append(current)
291
+
292
+ return artifacts
293
+
294
+
295
+ def beliefs_from_dicts(data: list[dict[str, object]]) -> list[BeliefArtifact]:
296
+ """Construct BeliefArtifacts from a list of plain dicts.
297
+
298
+ Useful when loading from JSON, YAML, or a database::
299
+
300
+ import yaml
301
+ data = yaml.safe_load(open("hypotheses.yml"))
302
+ artifacts = beliefs_from_dicts(data["beliefs"])
303
+ """
304
+ artifacts = []
305
+ for d in data:
306
+ a = BeliefArtifact(
307
+ artifact_id=str(d.get("artifact_id", "")),
308
+ propositions=list(d.get("propositions", [])), # type: ignore[arg-type]
309
+ epistemic_boundary=list(d.get("epistemic_boundary", [])), # type: ignore[arg-type]
310
+ inferential_links=list(d.get("inferential_links", [])), # type: ignore[arg-type]
311
+ confidence=ConfidenceLevel(d.get("confidence", "unknown")),
312
+ status=BeliefStatus(d.get("status", "draft")),
313
+ source_text=str(d.get("source_text", "")),
314
+ component=str(d.get("component", "")),
315
+ priority=str(d.get("priority", "")),
316
+ domain=str(d.get("domain", "")),
317
+ evidence=list(d.get("evidence", [])), # type: ignore[arg-type]
318
+ )
319
+ artifacts.append(a)
320
+ return artifacts
321
+
322
+
323
+ def _finalise(artifact: BeliefArtifact, desc: str) -> None:
324
+ if not desc:
325
+ return
326
+ artifact.source_text = artifact.source_text or desc
327
+ parts = re.split(r";|\band\b(?=\s+[A-Z])", desc)
328
+ artifact.propositions = artifact.propositions or [p.strip() for p in parts if p.strip()]
329
+ if not artifact.epistemic_boundary:
330
+ artifact.epistemic_boundary = ["Assumed correct project environment"]
331
+
332
+
333
+ def _map_status(val: str) -> BeliefStatus:
334
+ val_lower = val.lower()
335
+ if "accept" in val_lower:
336
+ return BeliefStatus.ACCEPTED
337
+ if "stress" in val_lower:
338
+ return BeliefStatus.STRESS_TESTED
339
+ if "reconstruct" in val_lower:
340
+ return BeliefStatus.RECONSTRUCTED
341
+ if "deprecat" in val_lower:
342
+ return BeliefStatus.DEPRECATED
343
+ return BeliefStatus.DRAFT
344
+
345
+
346
+ def _infer_confidence(status: BeliefStatus) -> ConfidenceLevel:
347
+ return {
348
+ BeliefStatus.DRAFT: ConfidenceLevel.UNKNOWN,
349
+ BeliefStatus.ACCEPTED: ConfidenceLevel.LOW,
350
+ BeliefStatus.STRESS_TESTED: ConfidenceLevel.MEDIUM,
351
+ BeliefStatus.RECONSTRUCTED: ConfidenceLevel.HIGH,
352
+ BeliefStatus.DEPRECATED: ConfidenceLevel.LOW,
353
+ }.get(status, ConfidenceLevel.UNKNOWN)
354
+
355
+
356
+ __all__ = [
357
+ "BeliefArtifact",
358
+ "BeliefStatus",
359
+ "ConfidenceLevel",
360
+ "FailureMode",
361
+ "FailureSeverity",
362
+ "parse_requirements_as_beliefs",
363
+ "beliefs_from_dicts",
364
+ ]