topos-mcp 0.3.4__tar.gz

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 (229) hide show
  1. topos_mcp-0.3.4/.agents/AGENTS.md +419 -0
  2. topos_mcp-0.3.4/.claude/CLAUDE.md +15 -0
  3. topos_mcp-0.3.4/.gemini/GEMINI.md +9 -0
  4. topos_mcp-0.3.4/.github/workflows/ci.yml +115 -0
  5. topos_mcp-0.3.4/.github/workflows/docs.yml +40 -0
  6. topos_mcp-0.3.4/.github/workflows/release.yml +267 -0
  7. topos_mcp-0.3.4/.gitignore +254 -0
  8. topos_mcp-0.3.4/CHANGELOG.md +69 -0
  9. topos_mcp-0.3.4/Cargo.lock +403 -0
  10. topos_mcp-0.3.4/Cargo.toml +17 -0
  11. topos_mcp-0.3.4/LICENSE +28 -0
  12. topos_mcp-0.3.4/PKG-INFO +240 -0
  13. topos_mcp-0.3.4/README.md +211 -0
  14. topos_mcp-0.3.4/docs/source/Makefile +20 -0
  15. topos_mcp-0.3.4/docs/source/_static/custom.css +291 -0
  16. topos_mcp-0.3.4/docs/source/_static/logo-dark.png +0 -0
  17. topos_mcp-0.3.4/docs/source/_static/logo-link.js +2 -0
  18. topos_mcp-0.3.4/docs/source/_static/logo.png +0 -0
  19. topos_mcp-0.3.4/docs/source/_static/topos-logo-dark.svg +164 -0
  20. topos_mcp-0.3.4/docs/source/_static/topos-logo.svg +157 -0
  21. topos_mcp-0.3.4/docs/source/agents.rst +214 -0
  22. topos_mcp-0.3.4/docs/source/cli.rst +272 -0
  23. topos_mcp-0.3.4/docs/source/concepts.rst +74 -0
  24. topos_mcp-0.3.4/docs/source/conf.py +73 -0
  25. topos_mcp-0.3.4/docs/source/index.rst +185 -0
  26. topos_mcp-0.3.4/docs/source/installation.rst +96 -0
  27. topos_mcp-0.3.4/docs/source/make.bat +35 -0
  28. topos_mcp-0.3.4/docs/source/measures.rst +207 -0
  29. topos_mcp-0.3.4/docs/structural-test-coverage.md +75 -0
  30. topos_mcp-0.3.4/docs/uast-industry-standards.md +417 -0
  31. topos_mcp-0.3.4/extensions/vscode/.eslintrc.json +22 -0
  32. topos_mcp-0.3.4/extensions/vscode/.gitignore +12 -0
  33. topos_mcp-0.3.4/extensions/vscode/.vscode-test.mjs +9 -0
  34. topos_mcp-0.3.4/extensions/vscode/.vscodeignore +17 -0
  35. topos_mcp-0.3.4/extensions/vscode/LICENSE +28 -0
  36. topos_mcp-0.3.4/extensions/vscode/README.md +117 -0
  37. topos_mcp-0.3.4/extensions/vscode/esbuild.js +63 -0
  38. topos_mcp-0.3.4/extensions/vscode/icon.png +0 -0
  39. topos_mcp-0.3.4/extensions/vscode/package-lock.json +3872 -0
  40. topos_mcp-0.3.4/extensions/vscode/package.json +147 -0
  41. topos_mcp-0.3.4/extensions/vscode/scripts/check-vsix-size.js +25 -0
  42. topos_mcp-0.3.4/extensions/vscode/scripts/stage-binary.js +116 -0
  43. topos_mcp-0.3.4/extensions/vscode/scripts/test-stage-binary.js +40 -0
  44. topos_mcp-0.3.4/extensions/vscode/src/extension.ts +615 -0
  45. topos_mcp-0.3.4/extensions/vscode/src/runtime.ts +345 -0
  46. topos_mcp-0.3.4/extensions/vscode/src/test/integration/extension.test.ts +39 -0
  47. topos_mcp-0.3.4/extensions/vscode/src/test/unit/runtime.test.ts +286 -0
  48. topos_mcp-0.3.4/extensions/vscode/tsconfig.json +16 -0
  49. topos_mcp-0.3.4/extensions/vscode/tsconfig.test.json +10 -0
  50. topos_mcp-0.3.4/extensions/vscode/workflow/publishing.md +714 -0
  51. topos_mcp-0.3.4/install.sh +503 -0
  52. topos_mcp-0.3.4/packaging/macos-entitlements.plist +10 -0
  53. topos_mcp-0.3.4/pyproject.toml +75 -0
  54. topos_mcp-0.3.4/src/cfg.rs +304 -0
  55. topos_mcp-0.3.4/src/lib.rs +29 -0
  56. topos_mcp-0.3.4/src/probes_ast.rs +148 -0
  57. topos_mcp-0.3.4/src/profunctors.rs +129 -0
  58. topos_mcp-0.3.4/src/uast.rs +142 -0
  59. topos_mcp-0.3.4/tests/__init__.py +0 -0
  60. topos_mcp-0.3.4/tests/cli/__init__.py +1 -0
  61. topos_mcp-0.3.4/tests/cli/test_coverage.py +85 -0
  62. topos_mcp-0.3.4/tests/cli/test_helpers.py +85 -0
  63. topos_mcp-0.3.4/tests/cli/test_main.py +26 -0
  64. topos_mcp-0.3.4/tests/cli/test_quality.py +188 -0
  65. topos_mcp-0.3.4/tests/cli/test_system.py +94 -0
  66. topos_mcp-0.3.4/tests/core/__init__.py +0 -0
  67. topos_mcp-0.3.4/tests/core/test_core.py +86 -0
  68. topos_mcp-0.3.4/tests/evaluation/__init__.py +0 -0
  69. topos_mcp-0.3.4/tests/evaluation/test_calibration.py +143 -0
  70. topos_mcp-0.3.4/tests/evaluation/test_logic.py +338 -0
  71. topos_mcp-0.3.4/tests/evaluation/test_policies_base.py +157 -0
  72. topos_mcp-0.3.4/tests/evaluation/test_preferences.py +126 -0
  73. topos_mcp-0.3.4/tests/fixtures/binarytrees/binarytrees.cpp +81 -0
  74. topos_mcp-0.3.4/tests/fixtures/binarytrees/binarytrees.js +61 -0
  75. topos_mcp-0.3.4/tests/fixtures/binarytrees/binarytrees.py +75 -0
  76. topos_mcp-0.3.4/tests/fixtures/binarytrees/binarytrees.rs +88 -0
  77. topos_mcp-0.3.4/tests/functors/__init__.py +0 -0
  78. topos_mcp-0.3.4/tests/functors/probes/__init__.py +0 -0
  79. topos_mcp-0.3.4/tests/functors/probes/ast/__init__.py +0 -0
  80. topos_mcp-0.3.4/tests/functors/probes/ast/test_ast_metrics.py +157 -0
  81. topos_mcp-0.3.4/tests/functors/probes/mdg/__init__.py +0 -0
  82. topos_mcp-0.3.4/tests/functors/probes/mdg/test_pdg_metrics.py +842 -0
  83. topos_mcp-0.3.4/tests/functors/profunctors/__init__.py +0 -0
  84. topos_mcp-0.3.4/tests/functors/profunctors/test_profunctors.py +181 -0
  85. topos_mcp-0.3.4/tests/functors/profunctors/uast/__init__.py +0 -0
  86. topos_mcp-0.3.4/tests/functors/profunctors/uast/test_structural_test_coverage.py +232 -0
  87. topos_mcp-0.3.4/tests/functors/profunctors/uast/test_uast_comparison.py +146 -0
  88. topos_mcp-0.3.4/tests/graphs/__init__.py +0 -0
  89. topos_mcp-0.3.4/tests/graphs/ast/__init__.py +0 -0
  90. topos_mcp-0.3.4/tests/graphs/ast/test_multilang_ast.py +149 -0
  91. topos_mcp-0.3.4/tests/graphs/cfg/__init__.py +0 -0
  92. topos_mcp-0.3.4/tests/graphs/cfg/test_cfg.py +149 -0
  93. topos_mcp-0.3.4/tests/graphs/cpg/__init__.py +0 -0
  94. topos_mcp-0.3.4/tests/graphs/cpg/test_cpg.py +69 -0
  95. topos_mcp-0.3.4/tests/graphs/pdg/__init__.py +0 -0
  96. topos_mcp-0.3.4/tests/graphs/pdg/test_pdg_program_dependence.py +45 -0
  97. topos_mcp-0.3.4/tests/graphs/test_graphs.py +375 -0
  98. topos_mcp-0.3.4/tests/mcp/__init__.py +0 -0
  99. topos_mcp-0.3.4/tests/mcp/conftest.py +20 -0
  100. topos_mcp-0.3.4/tests/mcp/test_assess.py +154 -0
  101. topos_mcp-0.3.4/tests/mcp/test_compare.py +47 -0
  102. topos_mcp-0.3.4/tests/mcp/test_docs.py +56 -0
  103. topos_mcp-0.3.4/tests/mcp/test_evaluate.py +264 -0
  104. topos_mcp-0.3.4/tests/mcp/test_inspect.py +68 -0
  105. topos_mcp-0.3.4/tests/mcp/test_preference_walk_tool.py +112 -0
  106. topos_mcp-0.3.4/tests/mcp/test_prompts.py +26 -0
  107. topos_mcp-0.3.4/tests/mcp/test_resources.py +36 -0
  108. topos_mcp-0.3.4/tests/mcp/test_security.py +66 -0
  109. topos_mcp-0.3.4/tests/parity/__init__.py +0 -0
  110. topos_mcp-0.3.4/tests/parity/baseline_v1.py +160 -0
  111. topos_mcp-0.3.4/tests/parity/test_rust_parity.py +103 -0
  112. topos_mcp-0.3.4/tests/utils/__init__.py +0 -0
  113. topos_mcp-0.3.4/tests/utils/test_discovery.py +68 -0
  114. topos_mcp-0.3.4/tests/utils/test_utils.py +53 -0
  115. topos_mcp-0.3.4/topos/__init__.py +53 -0
  116. topos_mcp-0.3.4/topos/cli/__init__.py +5 -0
  117. topos_mcp-0.3.4/topos/cli/commands/__init__.py +1 -0
  118. topos_mcp-0.3.4/topos/cli/commands/coverage.py +152 -0
  119. topos_mcp-0.3.4/topos/cli/commands/quality.py +281 -0
  120. topos_mcp-0.3.4/topos/cli/commands/system.py +158 -0
  121. topos_mcp-0.3.4/topos/cli/evaluation.py +404 -0
  122. topos_mcp-0.3.4/topos/cli/installation.py +129 -0
  123. topos_mcp-0.3.4/topos/cli/main.py +34 -0
  124. topos_mcp-0.3.4/topos/core/__init__.py +58 -0
  125. topos_mcp-0.3.4/topos/core/category.py +163 -0
  126. topos_mcp-0.3.4/topos/core/morphism.py +211 -0
  127. topos_mcp-0.3.4/topos/core/object.py +122 -0
  128. topos_mcp-0.3.4/topos/core/omega.py +409 -0
  129. topos_mcp-0.3.4/topos/evaluation/__init__.py +54 -0
  130. topos_mcp-0.3.4/topos/evaluation/characteristic_morphism.py +335 -0
  131. topos_mcp-0.3.4/topos/evaluation/policies/__init__.py +62 -0
  132. topos_mcp-0.3.4/topos/evaluation/policies/base.py +253 -0
  133. topos_mcp-0.3.4/topos/evaluation/policies/calibration.py +105 -0
  134. topos_mcp-0.3.4/topos/evaluation/policies/clones.py +42 -0
  135. topos_mcp-0.3.4/topos/evaluation/policies/composable.py +135 -0
  136. topos_mcp-0.3.4/topos/evaluation/policies/coverage.py +97 -0
  137. topos_mcp-0.3.4/topos/evaluation/policies/secure.py +100 -0
  138. topos_mcp-0.3.4/topos/evaluation/policies/simple.py +142 -0
  139. topos_mcp-0.3.4/topos/evaluation/preferences.py +243 -0
  140. topos_mcp-0.3.4/topos/functors/probes/__init__.py +21 -0
  141. topos_mcp-0.3.4/topos/functors/probes/ast/__init__.py +12 -0
  142. topos_mcp-0.3.4/topos/functors/probes/ast/complexity.py +127 -0
  143. topos_mcp-0.3.4/topos/functors/probes/ast/entropy.py +70 -0
  144. topos_mcp-0.3.4/topos/functors/probes/cfg/__init__.py +5 -0
  145. topos_mcp-0.3.4/topos/functors/probes/cfg/complexity.py +140 -0
  146. topos_mcp-0.3.4/topos/functors/probes/cfg/paths.py +23 -0
  147. topos_mcp-0.3.4/topos/functors/probes/cpg/__init__.py +5 -0
  148. topos_mcp-0.3.4/topos/functors/probes/cpg/danger.py +109 -0
  149. topos_mcp-0.3.4/topos/functors/probes/cpg/taint.py +121 -0
  150. topos_mcp-0.3.4/topos/functors/probes/mdg/__init__.py +24 -0
  151. topos_mcp-0.3.4/topos/functors/probes/mdg/coupling.py +165 -0
  152. topos_mcp-0.3.4/topos/functors/probes/mdg/fan.py +82 -0
  153. topos_mcp-0.3.4/topos/functors/probes/uast/__init__.py +25 -0
  154. topos_mcp-0.3.4/topos/functors/probes/uast/compare.py +147 -0
  155. topos_mcp-0.3.4/topos/functors/probes/uast/signature.py +127 -0
  156. topos_mcp-0.3.4/topos/functors/profunctors/__init__.py +18 -0
  157. topos_mcp-0.3.4/topos/functors/profunctors/ast/__init__.py +19 -0
  158. topos_mcp-0.3.4/topos/functors/profunctors/ast/compare.py +486 -0
  159. topos_mcp-0.3.4/topos/functors/profunctors/cfg/__init__.py +19 -0
  160. topos_mcp-0.3.4/topos/functors/profunctors/cfg/compare.py +97 -0
  161. topos_mcp-0.3.4/topos/functors/profunctors/cpg/__init__.py +19 -0
  162. topos_mcp-0.3.4/topos/functors/profunctors/cpg/compare.py +105 -0
  163. topos_mcp-0.3.4/topos/functors/profunctors/mdg/__init__.py +21 -0
  164. topos_mcp-0.3.4/topos/functors/profunctors/mdg/compare.py +110 -0
  165. topos_mcp-0.3.4/topos/functors/profunctors/pdg/__init__.py +19 -0
  166. topos_mcp-0.3.4/topos/functors/profunctors/pdg/compare.py +115 -0
  167. topos_mcp-0.3.4/topos/functors/profunctors/uast/__init__.py +21 -0
  168. topos_mcp-0.3.4/topos/functors/profunctors/uast/compare.py +147 -0
  169. topos_mcp-0.3.4/topos/functors/profunctors/uast/structural_test_coverage.py +308 -0
  170. topos_mcp-0.3.4/topos/graphs/__init__.py +40 -0
  171. topos_mcp-0.3.4/topos/graphs/ast/__init__.py +15 -0
  172. topos_mcp-0.3.4/topos/graphs/ast/dispatch.py +101 -0
  173. topos_mcp-0.3.4/topos/graphs/ast/object.py +62 -0
  174. topos_mcp-0.3.4/topos/graphs/ast/providers/__init__.py +4 -0
  175. topos_mcp-0.3.4/topos/graphs/ast/providers/base.py +17 -0
  176. topos_mcp-0.3.4/topos/graphs/ast/providers/native_provider.py +57 -0
  177. topos_mcp-0.3.4/topos/graphs/ast/providers/tree_sitter_provider.py +67 -0
  178. topos_mcp-0.3.4/topos/graphs/ast/types.py +28 -0
  179. topos_mcp-0.3.4/topos/graphs/base.py +62 -0
  180. topos_mcp-0.3.4/topos/graphs/cfg/__init__.py +19 -0
  181. topos_mcp-0.3.4/topos/graphs/cfg/builder.py +450 -0
  182. topos_mcp-0.3.4/topos/graphs/cfg/models.py +74 -0
  183. topos_mcp-0.3.4/topos/graphs/cfg/object.py +81 -0
  184. topos_mcp-0.3.4/topos/graphs/cpg/__init__.py +27 -0
  185. topos_mcp-0.3.4/topos/graphs/cpg/builder.py +116 -0
  186. topos_mcp-0.3.4/topos/graphs/cpg/models.py +55 -0
  187. topos_mcp-0.3.4/topos/graphs/cpg/object.py +75 -0
  188. topos_mcp-0.3.4/topos/graphs/mdg/__init__.py +25 -0
  189. topos_mcp-0.3.4/topos/graphs/mdg/object.py +410 -0
  190. topos_mcp-0.3.4/topos/graphs/pdg/__init__.py +25 -0
  191. topos_mcp-0.3.4/topos/graphs/pdg/object.py +219 -0
  192. topos_mcp-0.3.4/topos/graphs/uast/__init__.py +25 -0
  193. topos_mcp-0.3.4/topos/graphs/uast/mapper_common.py +136 -0
  194. topos_mcp-0.3.4/topos/graphs/uast/mapper_cpp.py +68 -0
  195. topos_mcp-0.3.4/topos/graphs/uast/mapper_javascript.py +73 -0
  196. topos_mcp-0.3.4/topos/graphs/uast/mapper_python.py +69 -0
  197. topos_mcp-0.3.4/topos/graphs/uast/mapper_rust.py +80 -0
  198. topos_mcp-0.3.4/topos/graphs/uast/mapper_typescript.py +41 -0
  199. topos_mcp-0.3.4/topos/graphs/uast/models.py +64 -0
  200. topos_mcp-0.3.4/topos/mcp/__init__.py +12 -0
  201. topos_mcp-0.3.4/topos/mcp/cache.py +70 -0
  202. topos_mcp-0.3.4/topos/mcp/evaluation.py +257 -0
  203. topos_mcp-0.3.4/topos/mcp/formatting.py +262 -0
  204. topos_mcp-0.3.4/topos/mcp/prompts/__init__.py +3 -0
  205. topos_mcp-0.3.4/topos/mcp/prompts/refactor.py +102 -0
  206. topos_mcp-0.3.4/topos/mcp/resources/__init__.py +3 -0
  207. topos_mcp-0.3.4/topos/mcp/resources/content/lattice.md +78 -0
  208. topos_mcp-0.3.4/topos/mcp/resources/content/metrics.md +103 -0
  209. topos_mcp-0.3.4/topos/mcp/resources/content/preferences.md +117 -0
  210. topos_mcp-0.3.4/topos/mcp/resources/content/priority.md +50 -0
  211. topos_mcp-0.3.4/topos/mcp/resources/content/workflows.md +155 -0
  212. topos_mcp-0.3.4/topos/mcp/resources/docs.py +73 -0
  213. topos_mcp-0.3.4/topos/mcp/schemas.py +798 -0
  214. topos_mcp-0.3.4/topos/mcp/security.py +133 -0
  215. topos_mcp-0.3.4/topos/mcp/security_findings.py +142 -0
  216. topos_mcp-0.3.4/topos/mcp/server.py +71 -0
  217. topos_mcp-0.3.4/topos/mcp/tools/__init__.py +11 -0
  218. topos_mcp-0.3.4/topos/mcp/tools/assess.py +294 -0
  219. topos_mcp-0.3.4/topos/mcp/tools/compare.py +127 -0
  220. topos_mcp-0.3.4/topos/mcp/tools/coverage.py +232 -0
  221. topos_mcp-0.3.4/topos/mcp/tools/docs.py +52 -0
  222. topos_mcp-0.3.4/topos/mcp/tools/evaluate.py +456 -0
  223. topos_mcp-0.3.4/topos/mcp/tools/inspect.py +165 -0
  224. topos_mcp-0.3.4/topos/mcp/tools/preferences.py +165 -0
  225. topos_mcp-0.3.4/topos/utils/__init__.py +45 -0
  226. topos_mcp-0.3.4/topos/utils/ast/__init__.py +10 -0
  227. topos_mcp-0.3.4/topos/utils/discovery.py +231 -0
  228. topos_mcp-0.3.4/topos/utils/tree_sitter.py +358 -0
  229. topos_mcp-0.3.4/topos-logo.svg +164 -0
@@ -0,0 +1,419 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Writing Style
6
+
7
+ Always use **American English spelling** — "optimize" not "optimise", "analyze" not "analyse", "modeling" not "modelling", etc.
8
+
9
+ ## Overview
10
+
11
+ **Topos** is a code quality evaluation framework that applies category theory (specifically Heyting Algebra) to classify Python code. Programs are evaluated across three independent *generators* (SIMPLE, COMPOSABLE, SECURE) and mapped to an 8-element lattice (free Heyting algebra on 3 generators) rather than a single numeric score.
12
+
13
+ The project consists of:
14
+ - A CLI tool (`topos`) for evaluating and comparing code
15
+ - An MCP server (`topos-mcp`) exposing code analysis as tools for Claude and other MCP clients
16
+ - Category-theoretic abstractions in `topos.core`: `ProgramObject`, `ProgramMorphism`, `ProgramCategory`, `Omega`
17
+ - The decision layer in `topos.evaluation`: `CharacteristicMorphism` (χ_S : P → Ω) plus the per-generator policy translators
18
+ - Graph representations in `topos.graphs`: `ASTRepresentation`, `ControlFlowGraph`, `ModuleDependencyGraph`, `ProgramDependenceGraph`, `CodePropertyGraph`, and the UAST substrate
19
+
20
+ ## Development Commands
21
+
22
+ ```bash
23
+ uv pip install -e ".[dev]" # Install with dev dependencies
24
+ uv run maturin develop # Build Rust backend in development mode
25
+
26
+ pytest # Run all tests
27
+ pytest tests/parity/ # Run implementation parity tests
28
+ pytest tests/test_file.py::test_name # Run a specific test
29
+
30
+ ruff check topos/ --fix && ruff format topos/ # Lint and format
31
+ ```
32
+
33
+ ## CLI Usage
34
+
35
+ ```bash
36
+ topos evaluate path/to/code.py
37
+ topos evaluate topos/ -r --priority simple
38
+ topos evaluate topos/ -r --gitnexus-dir .gitnexus --priority composable
39
+ topos evaluate topos/ -r --gitnexus-dir .gitnexus --priority secure
40
+ ...
41
+ ```
42
+
43
+ ## Architecture
44
+
45
+ ### Hybrid Rust/Python Model
46
+ Topos uses a high-performance hybrid architecture. Performance-critical logic is implemented in Rust (`topos-functors`), while orchestration and evaluation policies remain in Python for readability.
47
+
48
+ - **`topos/core/`** — the program topos's defining structure.
49
+ - **`topos/graphs/`** — translational functors `R : Lang → E`. Documented Python wrappers that delegate graph construction to the Rust backend.
50
+ - **`topos/evaluation/`** — the decision layer: how raw measurements become Ω verdicts.
51
+ - **`topos/functors/`** — probes and profunctors. Documented wrappers delegating heavy metrics (CFG, entropy, edit distance) to Rust.
52
+ - **`src/`** — houses the Rust `topos-functors` core.
53
+
54
+ The `Representation` protocol (`graphs/base.py`) requires:
55
+ - `name: str` — identifies the representation type (e.g. `"ast"`, `"cfg"`, `"mdg"`, `"cpg"`).
56
+ - `dimension: str` — the generator this representation feeds (`"simple"`, `"composable"`, or `"secure"`).
57
+ - `metrics() -> dict[str, float]` — namespaced metric values.
58
+
59
+ New representations go in `topos.graphs/`; new probes in `topos.functors.probes/`; new pairwise comparisons in `topos.functors.profunctors/`.
60
+
61
+ ### Evaluation Flow
62
+
63
+ ```
64
+ ProgramMorphism.from_file(path)
65
+ → ASTRepresentation(morphism.ast) [always built by the classifier; entropy → SIMPLE]
66
+ → morphism.build_cfg() [always built; feeds SIMPLE]
67
+ → morphism.build_pdg() [always built; diagnostic]
68
+ → morphism.build_cpg() [always built; feeds SECURE]
69
+ → ModuleDependencyGraph.from_gitnexus_dir(...) [optional; requires .gitnexus/; feeds COMPOSABLE]
70
+
71
+ CharacteristicMorphism.classify_detailed(morphism, representations=[cfg, pdg, cpg, mdg], priority=...)
72
+ → Group representations by their `dimension` (= generator)
73
+ → Call rep.metrics() → raw floats
74
+ → score_simple(cfg.cyclomatic, ast.entropy, ...) → ScoredDecision [SIMPLE]
75
+ → score_coupling(mdg.coupling, mdg.instability, ...) → ScoredDecision [COMPOSABLE]
76
+ → score_secure(cpg.dangerous_calls, cpg.taint_flows, ...) → ScoredDecision [SECURE]
77
+ → score ≥ 0.6 → generator satisfied
78
+ → verdict = verdict_from_generators(simple, composable, secure) → Ω element
79
+ → Return ClassificationResult
80
+ ```
81
+
82
+ ### The 8-Element Lattice (Ω)
83
+
84
+ `Omega` (in `topos.core.omega`) is the free Heyting algebra on the three generators `{SIMPLE, COMPOSABLE, SECURE}` — one element per subset of generators a program satisfies:
85
+
86
+ ```
87
+ IDEAL (⊤ — all three generators satisfied)
88
+ / | \
89
+ SIMPLE_COMPOSABLE SIMPLE_SECURE COMPOSABLE_SECURE
90
+ | \ / \ / |
91
+ SIMPLE COMPOSABLE SECURE
92
+ \ | /
93
+ SLOP (⊥ — no generator satisfied)
94
+ ```
95
+
96
+ Key property: **The three generators are pairwise incomparable.** Each can be achieved independently; the algebraic meet of two incomparable atoms is `SLOP`. Multi-file rollup is the pointwise lattice meet `⋀_f χ_S(f)`: a generator is satisfied across the codebase iff it is satisfied on every file (minimum per-generator score ≥ threshold).
97
+
98
+ ### The Three Pillars
99
+
100
+ | Generator | Representation | Metrics | Scoring |
101
+ |-----------|---------------|---------|---------|
102
+ | SIMPLE | `ControlFlowGraph` (+ `ASTRepresentation` entropy) | `cfg.cyclomatic`, `cfg.essential`, `cfg.nesting_depth`, `cfg.longest_path`, `ast.entropy` | Weighted: `1 - cyclomatic/40` + entropy bell-curve (peak at 0.5). Threshold: **0.40**. |
103
+ | COMPOSABLE | `ModuleDependencyGraph` | `mdg.coupling`, `mdg.instability`, `mdg.fan_in`, `mdg.fan_out`, `mdg.dep_depth` | Weighted: `1 - coupling/35` + instability flat-top tent over [0.3, 0.7]. Threshold: **0.80**. |
104
+ | SECURE | `CodePropertyGraph` | `cpg.dangerous_calls`, `cpg.taint_flows` | Weighted exp-decay: `exp(-count / scale)` for each metric. Threshold: **0.70** (higher — security false-negatives are asymmetrically costly). |
105
+
106
+ **Diagnostic (not counted toward verdict):**
107
+ - `ProgramDependenceGraph`: `pdg.data_deps`, `pdg.control_deps`, `pdg.density` — intra-procedural dependence analysis.
108
+
109
+ **`--priority`** shifts metric weights *within* each generator (via `Priority` enum: `SIMPLE`, `COMPOSABLE`, `SECURE`). It does not change the lattice structure or which generators score. `Priority` is the single-knob shorthand; for a full agent loop use `UserPreferences` (see [Priority & Preferences](#priority--preferences)).
110
+
111
+ ### Key Non-Obvious Behaviors
112
+
113
+ - **SIMPLE and SECURE always run.** CFG and CPG are derived from the UAST built during parsing — no external tooling required. Parse failures collapse the whole verdict to `SLOP`.
114
+ - **COMPOSABLE is unreachable without `.gitnexus/`.** Module dependency evaluation only runs when a `ModuleDependencyGraph` is loaded from a `.gitnexus/` directory (`--gitnexus-dir`). Any verdict containing COMPOSABLE (including `IDEAL`) is then unreachable.
115
+ - **GitNexus is an external npm tool.** Run `topos depgraph generate` (which calls `gitnexus analyze`) to produce the `.gitnexus/` directory consumed by `--gitnexus-dir`.
116
+ - **Parse failures kill the verdict.** `is_parseable=False` → `lattice_element = SLOP`; in `combine_dimensions()` they inject a `0.0` score on SIMPLE, pulling multi-file aggregation down.
117
+ - **Mixed representations within a generator** are scored independently and combined via `min()` (conservative). E.g. AST entropy and CFG cyclomatic both feed SIMPLE; the generator passes iff both individual decisions pass.
118
+ - **Three generators are orthogonal.** A file can be SIMPLE without being COMPOSABLE, COMPOSABLE without being SECURE, etc. The 8-element Ω encodes every combination.
119
+
120
+ ## Priority & Preferences
121
+
122
+ There are **two complementary knobs** for controlling how the scoring pipeline weights quality axes. Every evaluation call must supply at least one.
123
+
124
+ ### `Priority` — single-knob (top-generator emphasis)
125
+
126
+ `Priority` is a `StrEnum` with three members: `SIMPLE`, `COMPOSABLE`, `SECURE`. It selects a `WeightProfile` that upweights the primary metric for that generator inside the matching `Φᵢ`:
127
+
128
+ | Priority | `w_complexity` (Φ_SIMPLE) | `w_coupling` (Φ_COMPOSABLE) | `w_taint` (Φ_SECURE) |
129
+ |---|---|---|---|
130
+ | `simple` | 0.7 | 0.3 | 0.3 |
131
+ | `composable` | 0.3 | 0.7 | 0.3 |
132
+ | `secure` (default) | 0.3 | 0.3 | 0.7 |
133
+
134
+ `Priority` is the CLI shorthand — use it when you only need to name the *top-ranked* generator. It does **not** produce a relaxation walk on Ω.
135
+
136
+ **There is no `balanced` mode.** Every evaluation pins a priority; the codebase default is `Priority.SECURE` (most conservative).
137
+
138
+ ### `UserPreferences` — full strict ordering (agent loop)
139
+
140
+ `UserPreferences` captures a **strict total order** over all three generators, e.g. `[COMPOSABLE, SECURE, SIMPLE]`. This induces a total order on all 8 Ω elements, enables two-stage targeting, and drives the relaxation walk.
141
+
142
+ ```python
143
+ from topos.evaluation.preferences import UserPreferences, Generator
144
+
145
+ prefs = UserPreferences(ranking=(Generator.COMPOSABLE, Generator.SECURE, Generator.SIMPLE))
146
+ ```
147
+
148
+ #### How the induced order works
149
+
150
+ Each verdict is scored by its satisfied-generator bitmask weighted 4 / 2 / 1 in preference order:
151
+
152
+ ```
153
+ score(v) = 4·⟦g₁ satisfied⟧ + 2·⟦g₂ satisfied⟧ + 1·⟦g₃ satisfied⟧
154
+ ```
155
+
156
+ For ranking `[COMPOSABLE, SECURE, SIMPLE]` this yields:
157
+ `IDEAL (7) > COMPOSABLE_SECURE (6) > COMPOSABLE_SIMPLE (5) > COMPOSABLE (4) > SECURE_SIMPLE (3) > SECURE (2) > SIMPLE (1) > SLOP (0)`.
158
+
159
+ #### Two-stage targeting
160
+
161
+ | Stage | Target | Trigger to advance |
162
+ |---|---|---|
163
+ | 1 | `IDEAL` (aspirational) | Attempt for all iterations first |
164
+ | 2 | `fallback_target` — meet of the top-two generators | When IDEAL plateaus (no lattice movement) |
165
+
166
+ For ranking `[COMPOSABLE, SECURE, SIMPLE]` the fallback is `COMPOSABLE_SECURE`.
167
+
168
+ #### Relaxation walk & next_step
169
+
170
+ `prefs.relaxation_walk(current)` returns the descending verdict sequence from the aspirational target down to (but not including) `current`. `prefs.next_step(current)` is the **smallest** improvement above the current verdict — the safest immediate goal for the agent.
171
+
172
+ #### `WeightProfile` from a full ranking
173
+
174
+ When `UserPreferences` is supplied to the classifier, `WeightProfile.from_ranking(ranking)` is called to derive intra-policy weights. The top-ranked generator's `Φᵢ` is the most decisive (0.7), the middle is balanced (0.5), the bottom is conservative (0.3):
175
+
176
+ ```
177
+ ranking[0] (top) → primary-metric weight 0.7
178
+ ranking[1] (middle) → primary-metric weight 0.5
179
+ ranking[2] (bottom) → primary-metric weight 0.3
180
+ ```
181
+
182
+ This means supplying a full `UserPreferences` ranking is strictly more informative than a bare `Priority`: it both linearizes Ω for the relaxation walk *and* sets a richer weight profile for all three policy translators simultaneously.
183
+
184
+ #### MCP usage
185
+
186
+ Pass `preferences` alongside `priority` to any evaluate or assess tool:
187
+
188
+ ```json
189
+ {
190
+ "filepath": "src/server.py",
191
+ "priority": "composable",
192
+ "preferences": { "ranking": ["composable", "secure", "simple"] }
193
+ }
194
+ ```
195
+
196
+ The response includes a `preference_walk` block with `target`, `fallback_target`, `walk`, `next_step`, and `progress` (fraction from SLOP to IDEAL in [0, 1]). Use `topos_preference_walk` to get this walk without re-evaluating the file.
197
+
198
+ ## Classification Result
199
+
200
+ ```python
201
+ from topos import CharacteristicMorphism, ModuleDependencyGraph, ProgramMorphism
202
+
203
+ morphism = ProgramMorphism.from_file("my_code.py")
204
+ mdg = ModuleDependencyGraph.from_gitnexus_dir(".gitnexus", "my_code.py") # optional; enables COMPOSABLE
205
+
206
+ # CFG / PDG / CPG are derived intrinsically from the morphism's UAST:
207
+ cfg = morphism.build_cfg()
208
+ cpg = morphism.build_cpg()
209
+
210
+ chi = CharacteristicMorphism()
211
+ result = chi.classify_detailed(morphism, representations=[cfg, cpg, mdg])
212
+
213
+ result.dimensions # {"simple": EvaluationValue.SIMPLE, "composable": SLOP, "secure": SECURE}
214
+ result.scores # {"simple": 0.72, "composable": 0.45, "secure": 0.99}
215
+ result.summary() # EvaluationValue.SIMPLE_SECURE (bits: 0b101)
216
+ result.raw_metrics # {"cfg.cyclomatic": 8.0, "ast.entropy": 0.52, "cpg.dangerous_calls": 2, ...}
217
+ ```
218
+
219
+ `ProgramCategory.classify_detailed(morphism)` is a one-line wrapper around the above for callers that don't want to construct representations manually.
220
+
221
+ ## Adding a New Representation
222
+
223
+ 1. Create `graphs/<name>/object.py` implementing the `Representation` protocol:
224
+ - `name: str` — representation key (e.g., `"cfg"`, `"mdg"`).
225
+ - `dimension: str` — the generator it feeds (`"simple"`, `"composable"`, or `"secure"`).
226
+ - `metrics() -> dict[str, float]` — namespaced metric values (e.g., `{"cfg.cyclomatic": 8.0, ...}`).
227
+ 2. Add raw metric probes in `topos/functors/probes/<name>/` (`P : E → ℝ`).
228
+ 3. Register a score dispatcher in `topos/evaluation/characteristic_morphism.py`:
229
+ - Add `_score_<name>(raw, priority)` returning a `ScoredDecision`.
230
+ - Add to `_REPRESENTATION_SCORE_DISPATCHERS` keyed by `representation.name`.
231
+ 4. (Optional) Add pairwise comparison in `topos/functors/profunctors/<name>/compare.py` (`D : E × E^op → ℝ`).
232
+ 5. To introduce a new generator:
233
+ - Extend `EvaluationValue` in `topos/core/omega.py` (the enum is a bitmask; widening it changes Ω's cardinality from `2^n`).
234
+ - Extend `verdict_from_generators()` and add the new `Generator` member to `topos/evaluation/preferences.py`.
235
+ - Add a `WeightProfile` entry for the new `Priority` member in `policies/base.py::WEIGHT_PROFILES`, and extend `WeightProfile.from_ranking()` if it uses hard-coded positions.
236
+ - Add a policy translator `Φ_NEW` in `topos/evaluation/policies/`.
237
+ - Update MCP `LatticeElement` enum and docs.
238
+
239
+ ## MCP Server (`topos-mcp`)
240
+
241
+ Run with `topos-mcp` (stdio transport). Requires `TOPOS_MCP_FILE_ROOT` env var, or a project marker (`.git`/`pyproject.toml`) walking up from cwd — otherwise the server fails closed.
242
+
243
+ ### Tools
244
+
245
+ - `topos_evaluate_code(code, language, priority, preferences, response_format)` — classify a string. SIMPLE and SECURE are always scored; COMPOSABLE is unreachable from a bare string (no dependency graph). Pass `preferences` to receive a `preference_walk` block in the result.
246
+ - `topos_evaluate_file(filepath, priority, gitnexus_dir, preferences, response_format)` — classify a file. **Pass `gitnexus_dir` to enable the COMPOSABLE generator.** Pass `preferences` to receive a `preference_walk` block.
247
+ - `topos_evaluate_project(path, priority, gitnexus_dir, preferences, limit, offset, response_format)` — project-wide rollup with `ctx.report_progress`. Returns worst-scoring files first. Scores are per-generator; lattice value is the meet across files.
248
+ - `topos_compare_code(source_code, target_code, language, response_format)` — AST edit distance between two strings.
249
+ - `topos_compare_files(source, target, response_format)` — AST edit distance between two files.
250
+ - `topos_assess_improvement(proposed_code, filepath | current_code, priority, gitnexus_dir, preferences, response_format)` — agent refactor loop tool. Prefer `filepath` to enable COMPOSABLE scoring. Anti-gaming guardrail: returns `SUSPICIOUS_NO_STRUCTURAL_CHANGE` when scores move but AST edit distance is near zero. Pass `preferences` to drive the walk.
251
+ - `topos_inspect_code(code, language, priority, top_n_functions, response_format)` — detailed breakdown: top-N functions by CFG complexity, entropy details, full metric table.
252
+ - `topos_preference_walk(ranking, current, target, response_format)` — compute the induced total order on Ω and the relaxation walk for a given ranking, **without** re-evaluating any file. Pass `current` to get `next_step` and `progress` relative to a known verdict.
253
+
254
+ ### Resources
255
+
256
+ - `topos://docs/lattice` — the 8-element lattice (SLOP / SIMPLE / COMPOSABLE / SECURE / SIMPLE_COMPOSABLE / SIMPLE_SECURE / COMPOSABLE_SECURE / IDEAL).
257
+ - `topos://docs/metrics` — every metric key, generator, threshold, and priority weight table.
258
+ - `topos://docs/priority` — when to use `simple` vs `composable` vs `secure` priority.
259
+ - `topos://docs/preferences` — full strict-ordering preferences: induced Ω order, two-stage targeting, relaxation walk, `UserPreferences` vs `Priority`. **Read before building an agent loop.**
260
+ - `topos://docs/workflows` — canonical review→plan→refactor→re-measure agent loop. Verdict stop condition is `IDEAL`. Read first.
261
+
262
+ ### Prompts
263
+
264
+ - `topos_refactor_until_ideal(filepath, priority, max_iterations)` — scaffolds the full refactor loop.
265
+
266
+ ### Package layout
267
+
268
+ Code lives under `topos/mcp/`:
269
+ - `server.py` — FastMCP instance + stdio entry point.
270
+ - `schemas.py` — Pydantic input + structured return models.
271
+ - `security.py` — fail-closed file-root resolution.
272
+ - `cache.py` — LRU cache for `ModuleDependencyGraph` keyed on `.gitnexus` mtime.
273
+ - `evaluation.py` — shared classifier pipeline (attaches dep graph when available).
274
+ - `formatting.py` — response builders.
275
+ - `tools/` — one module per tool category: `evaluate.py`, `compare.py`, `assess.py`, `inspect.py`.
276
+ - `resources/docs.py` + `resources/content/*.md` — static documentation.
277
+ - `prompts/refactor.py` — the refactor prompt template.
278
+
279
+ ### Calibration & Benchmarking
280
+
281
+ - **Calibration Suite**: Located in `benchmarks/calibration/`. Contains infrastructure and data for validating Topos metric thresholds against real-world codebases. See `docs/calibration.md` for methodology.
282
+ - **Performance Benchmarks**: Located in `benchmarks/`. Side-by-side comparison scripts between Python and Rust implementations to verify speedups and algorithmic parity.
283
+
284
+ # Topos Agent Workflows
285
+
286
+ Per CodeScene's 2026 best-practice research ("agent-first tools need
287
+ AGENTS.md-style orchestration"), this document is the canonical recipe for
288
+ using Topos tools in a closed-loop refactor. Agents should read this on
289
+ first encounter with the server.
290
+
291
+ ## The canonical loop: review → plan → refactor → re-measure
292
+
293
+ ```
294
+ ┌────────────┐ ┌────────────┐ ┌──────────────┐ ┌────────────┐
295
+ │ 1. MEASURE │ ───► │ 2. PLAN │ ───► │ 3. PROPOSE │ ───► │ 4. VERIFY │
296
+ │ (evaluate)│ │ (identify │ │ (refactor) │ │ (assess) │
297
+ │ │ │ weakest) │ │ │ │ │
298
+ └────────────┘ └────────────┘ └──────────────┘ └─────┬──────┘
299
+
300
+ ┌─────────────────────────────────────────┘
301
+
302
+ ┌──────────────┐
303
+ │ 5. DECIDE │
304
+ │ accept / try │
305
+ │ again / stop │
306
+ └──────────────┘
307
+ ```
308
+
309
+ ### 1. Measure
310
+
311
+ - Single file: `topos_evaluate_file(filepath, gitnexus_dir)` — `gitnexus_dir`
312
+ is required for the COMPOSABLE generator. Without it, any verdict
313
+ containing COMPOSABLE (including 🥇 **GOLD**) is unreachable.
314
+ - Whole project: `topos_evaluate_project(path, gitnexus_dir)` — rollup +
315
+ worst-N file list. Start here to pick a target and "Go for Gold".
316
+
317
+ ### 2. Plan
318
+
319
+ Read the `guidance` field of the evaluation result. It's priority-aware and
320
+ tells you which dimension to work on. If `guidance` says "provide
321
+ gitnexus_dir" you must run `topos depgraph generate` first.
322
+
323
+ For deep analysis of a specific file, call `topos_inspect_code` — it returns
324
+ top-N functions by complexity, entropy details, and the full metric table.
325
+
326
+ ### 3. Propose
327
+
328
+ Write a refactor. Keep the change focused on one dimension at a time.
329
+ Submit via `topos_assess_improvement(filepath=..., proposed_code=...)`.
330
+
331
+ ### 4. Verify
332
+
333
+ `topos_assess_improvement` returns one of:
334
+
335
+ - `IMPROVEMENT` — lattice moved up (e.g. ❌ SLOP → 🥉 BRONZE, or 🥉 BRONZE → 🥈 SILVER). Commit.
336
+ - `IMPROVEMENT_SCORE` — lattice unchanged but per-dim scores improved.
337
+ Progress, but not a medal jump yet.
338
+ - `LATERAL_MOVE` — neither improved nor regressed. Try a different angle.
339
+ - `REGRESSION` / `REGRESSION_SCORE` — revert and re-plan.
340
+ - **`SUSPICIOUS_NO_STRUCTURAL_CHANGE`** — ⚠️ scores moved but AST barely
341
+ changed. The refactor is probably cosmetic (whitespace / comments /
342
+ renames). Make a structural change, not a textual one. **Do not commit.**
343
+
344
+ ### 5. Decide
345
+
346
+ Stop when:
347
+ - Verdict = 🥇 **GOLD** (all three generators satisfied), OR
348
+ - Priority-specific generator satisfied (`simple` → SIMPLE bit set,
349
+ `composable` → COMPOSABLE bit set, `secure` → SECURE bit set), OR
350
+ - `max_iterations` exhausted — report partial progress honestly rather than
351
+ gaming one more iteration.
352
+
353
+ ## Escape hatches — when the loop stalls
354
+
355
+ ### Stall #1: Every generator score plateaus below 60%
356
+
357
+ Often a sign the file needs to be **split**, not refactored. Use
358
+ `topos_inspect_code` to find the top-complexity functions; consider
359
+ extracting them into a separate module. Re-run `topos_evaluate_project` to
360
+ check the rollup doesn't regress as a result.
361
+
362
+ ### Stall #2: `SUSPICIOUS_NO_STRUCTURAL_CHANGE` repeatedly
363
+
364
+ You're iterating on presentation. Step back: what is the *structural*
365
+ problem? Rename → not a refactor. Whitespace → not a refactor. Loop
366
+ unrolling, extracted helpers, collapsed conditionals → real refactors.
367
+
368
+ ### Stall #3: SIMPLE improves, COMPOSABLE regresses
369
+
370
+ Classic "moved complexity elsewhere" anti-pattern. Re-run
371
+ `topos_evaluate_project` — did the other file's score drop? If so, the
372
+ refactor didn't reduce total system complexity, it just relocated it.
373
+ Consider if the abstraction is actually an improvement or just a shuffle.
374
+
375
+ ## Priority selection cheat sheet
376
+
377
+ - Leaf module (few callers) → `simple`
378
+ - Library surface (many importers) → `composable`
379
+ - File handling untrusted input → `secure`
380
+ - Unknown / general cleanup → `secure` (default scorer emphasis)
381
+
382
+ See `topos://docs/priority` for more.
383
+
384
+ ## Preference-driven targeting
385
+
386
+ For agent loops that need a concrete *next-best* verdict to aim for —
387
+ not just an upweighted generator — pass `preferences` alongside
388
+ `priority`. A `preferences.ranking` like `["composable", "secure",
389
+ "simple"]` induces a total order on Ω and produces a **two-stage**
390
+ target:
391
+
392
+ 1. **`target`** — aspirational, default 🥇 **GOLD**. Try to beat the
393
+ thresholds for all three generators first.
394
+ 2. **`fallback_target`** — the **"ideal intersection"**, i.e. the meet
395
+ of the top-two ranked generators (🥈 **SILVER**). When 🥇 **GOLD** plateaus, divert here.
396
+
397
+ The result also returns a **`walk`** (descending verdicts from GOLD
398
+ down) and a **`next_step`** (the smallest improvement above the
399
+ current verdict).
400
+
401
+ Concretely: aim for 🥇 **GOLD** for the first few iterations; if the lattice
402
+ verdict won't move, switch to `fallback_target` (🥈 **SILVER**) and try to satisfy
403
+ only the top-two generators. See `topos://docs/preferences`.
404
+
405
+ ## What Topos does NOT measure
406
+
407
+ - **Test coverage.** A refactor that improves the score but breaks tests
408
+ is a regression. Topos cannot see this; run the test suite separately.
409
+ - **Functional correctness.** AST edit distance measures *change*, not
410
+ *preservation of behavior*. Always verify behavior with tests.
411
+ - **Runtime performance.** Orthogonal to all Topos metrics.
412
+ - **Beyond-syntactic security.** The SECURE generator catches obvious
413
+ footguns (dangerous-API call sites, source→sink taint paths) via
414
+ textual / structural pattern matching on the CPG. It is not a full
415
+ SAST / pen-test — pair with dedicated security tooling for high-stakes
416
+ code.
417
+
418
+ Topos is one signal in a multi-signal loop. Pair it with test coverage and
419
+ type checks for the full picture.
@@ -0,0 +1,15 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ > **Note:** The core project architecture, rules, v1.0.0 evaluation model (SIMPLE, COMPOSABLE, SECURE), and refactoring workflows have been centralized.
6
+ >
7
+ > **You MUST read `AGENTS.md` at the repository root (or `.agents/AGENTS.md`) for the canonical project rules.**
8
+
9
+ ## Writing Style
10
+
11
+ Always use **American English spelling** — "optimize" not "optimise", "analyze" not "analyse", "modeling" not "modelling", etc.
12
+
13
+ ## Claude Tools & MCP Configuration
14
+ - You are configured to use the Topos MCP tools. Use `topos_get_doc` or similar MCP endpoints for dynamic help if `AGENTS.md` lacks specific details.
15
+ - Always follow the closed-loop refactoring recipe documented in the centralized `AGENTS.md`.
@@ -0,0 +1,9 @@
1
+ # Gemini CLI Specific Instructions
2
+
3
+ > **Note:** The core project architecture, rules, v1.0.0 evaluation model (SIMPLE, COMPOSABLE, SECURE), and refactoring workflows have been centralized.
4
+ >
5
+ > **You MUST read `AGENTS.md` at the repository root (or `.agents/AGENTS.md`) for the canonical project rules.**
6
+
7
+ ## Gemini Tools & MCP Configuration
8
+ - You are configured to use the Topos MCP tools. Use `topos_get_doc` or similar MCP endpoints for dynamic help if `AGENTS.md` lacks specific details.
9
+ - Always follow the closed-loop refactoring recipe documented in the centralized `AGENTS.md`.
@@ -0,0 +1,115 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ python-version: ["3.11", "3.12", "3.13"]
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Install uv
20
+ uses: astral-sh/setup-uv@v5
21
+ with:
22
+ enable-cache: true
23
+
24
+ - name: Set up Python
25
+ uses: actions/setup-python@v5
26
+ with:
27
+ python-version: ${{ matrix.python-version }}
28
+
29
+ - name: Check package version matches __init__ and VS Code extension
30
+ run: |
31
+ python - <<'PY'
32
+ from pathlib import Path
33
+ import json
34
+ import re
35
+ import sys
36
+ import tomllib
37
+
38
+ pyproject_version = tomllib.loads(Path("pyproject.toml").read_text())["project"]["version"]
39
+ init_text = Path("topos/__init__.py").read_text()
40
+ init_version = re.search(r'^__version__ = "([^"]+)"$', init_text, re.MULTILINE)
41
+
42
+ if init_version is None:
43
+ sys.exit("Could not find __version__ in topos/__init__.py")
44
+
45
+ if pyproject_version != init_version.group(1):
46
+ sys.exit(
47
+ f"Version mismatch: pyproject.toml has {pyproject_version}, "
48
+ f"topos/__init__.py has {init_version.group(1)}"
49
+ )
50
+
51
+ extension_version = json.loads(Path("extensions/vscode/package.json").read_text())["version"]
52
+ if pyproject_version != extension_version:
53
+ sys.exit(
54
+ f"Version mismatch: pyproject.toml has {pyproject_version}, "
55
+ f"extensions/vscode/package.json has {extension_version}. "
56
+ "The VS Code extension version is locked to the Topos version."
57
+ )
58
+ PY
59
+
60
+ - name: Install Rust toolchain
61
+ uses: dtolnay/rust-toolchain@stable
62
+ with:
63
+ components: clippy, rustfmt
64
+
65
+ - name: Install dependencies
66
+ run: uv sync --group dev --python ${{ matrix.python-version }}
67
+
68
+ - name: Run Python tests
69
+ run: uv run pytest -v
70
+
71
+ - name: Run Rust tests
72
+ run: uv run --python ${{ matrix.python-version }} cargo test
73
+
74
+ - name: Run Rust clippy
75
+ if: matrix.python-version == '3.13'
76
+ run: uv run --python 3.13 cargo clippy -- -D warnings
77
+
78
+ - name: Run Rust fmt
79
+ if: matrix.python-version == '3.13'
80
+ run: cargo fmt --check
81
+
82
+ - name: Run Ruff lint checks
83
+ if: matrix.python-version == '3.13'
84
+ run: uv run ruff check topos tests
85
+
86
+ - name: Run Ruff formatting checks
87
+ if: matrix.python-version == '3.13'
88
+ run: uv run ruff format --check topos tests
89
+
90
+ extension:
91
+ runs-on: ubuntu-latest
92
+ steps:
93
+ - uses: actions/checkout@v4
94
+
95
+ - uses: actions/setup-node@v4
96
+ with:
97
+ node-version: "20"
98
+ cache: npm
99
+ cache-dependency-path: extensions/vscode/package-lock.json
100
+
101
+ - name: Install extension dependencies
102
+ working-directory: extensions/vscode
103
+ run: npm ci
104
+
105
+ - name: Type-check
106
+ working-directory: extensions/vscode
107
+ run: npm run check-types
108
+
109
+ - name: Lint
110
+ working-directory: extensions/vscode
111
+ run: npm run lint
112
+
113
+ - name: Unit tests
114
+ working-directory: extensions/vscode
115
+ run: npm run test:unit
@@ -0,0 +1,40 @@
1
+ name: Docs
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ pull_request:
6
+ branches: [main]
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ build:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: actions/setup-python@v5
17
+ with:
18
+ python-version: "3.12"
19
+ - uses: astral-sh/setup-uv@v4
20
+ - name: Install dependencies
21
+ run: uv sync --group docs --no-sources
22
+ - name: Build docs
23
+ run: uv run --no-sync sphinx-build -M html docs/source docs/build
24
+ - uses: actions/upload-pages-artifact@v3
25
+ with:
26
+ path: docs/build/html
27
+
28
+ deploy:
29
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
30
+ needs: build
31
+ runs-on: ubuntu-latest
32
+ permissions:
33
+ pages: write
34
+ id-token: write
35
+ environment:
36
+ name: github-pages
37
+ url: ${{ steps.deployment.outputs.page_url }}
38
+ steps:
39
+ - id: deployment
40
+ uses: actions/deploy-pages@v4