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.
- topos_mcp-0.3.4/.agents/AGENTS.md +419 -0
- topos_mcp-0.3.4/.claude/CLAUDE.md +15 -0
- topos_mcp-0.3.4/.gemini/GEMINI.md +9 -0
- topos_mcp-0.3.4/.github/workflows/ci.yml +115 -0
- topos_mcp-0.3.4/.github/workflows/docs.yml +40 -0
- topos_mcp-0.3.4/.github/workflows/release.yml +267 -0
- topos_mcp-0.3.4/.gitignore +254 -0
- topos_mcp-0.3.4/CHANGELOG.md +69 -0
- topos_mcp-0.3.4/Cargo.lock +403 -0
- topos_mcp-0.3.4/Cargo.toml +17 -0
- topos_mcp-0.3.4/LICENSE +28 -0
- topos_mcp-0.3.4/PKG-INFO +240 -0
- topos_mcp-0.3.4/README.md +211 -0
- topos_mcp-0.3.4/docs/source/Makefile +20 -0
- topos_mcp-0.3.4/docs/source/_static/custom.css +291 -0
- topos_mcp-0.3.4/docs/source/_static/logo-dark.png +0 -0
- topos_mcp-0.3.4/docs/source/_static/logo-link.js +2 -0
- topos_mcp-0.3.4/docs/source/_static/logo.png +0 -0
- topos_mcp-0.3.4/docs/source/_static/topos-logo-dark.svg +164 -0
- topos_mcp-0.3.4/docs/source/_static/topos-logo.svg +157 -0
- topos_mcp-0.3.4/docs/source/agents.rst +214 -0
- topos_mcp-0.3.4/docs/source/cli.rst +272 -0
- topos_mcp-0.3.4/docs/source/concepts.rst +74 -0
- topos_mcp-0.3.4/docs/source/conf.py +73 -0
- topos_mcp-0.3.4/docs/source/index.rst +185 -0
- topos_mcp-0.3.4/docs/source/installation.rst +96 -0
- topos_mcp-0.3.4/docs/source/make.bat +35 -0
- topos_mcp-0.3.4/docs/source/measures.rst +207 -0
- topos_mcp-0.3.4/docs/structural-test-coverage.md +75 -0
- topos_mcp-0.3.4/docs/uast-industry-standards.md +417 -0
- topos_mcp-0.3.4/extensions/vscode/.eslintrc.json +22 -0
- topos_mcp-0.3.4/extensions/vscode/.gitignore +12 -0
- topos_mcp-0.3.4/extensions/vscode/.vscode-test.mjs +9 -0
- topos_mcp-0.3.4/extensions/vscode/.vscodeignore +17 -0
- topos_mcp-0.3.4/extensions/vscode/LICENSE +28 -0
- topos_mcp-0.3.4/extensions/vscode/README.md +117 -0
- topos_mcp-0.3.4/extensions/vscode/esbuild.js +63 -0
- topos_mcp-0.3.4/extensions/vscode/icon.png +0 -0
- topos_mcp-0.3.4/extensions/vscode/package-lock.json +3872 -0
- topos_mcp-0.3.4/extensions/vscode/package.json +147 -0
- topos_mcp-0.3.4/extensions/vscode/scripts/check-vsix-size.js +25 -0
- topos_mcp-0.3.4/extensions/vscode/scripts/stage-binary.js +116 -0
- topos_mcp-0.3.4/extensions/vscode/scripts/test-stage-binary.js +40 -0
- topos_mcp-0.3.4/extensions/vscode/src/extension.ts +615 -0
- topos_mcp-0.3.4/extensions/vscode/src/runtime.ts +345 -0
- topos_mcp-0.3.4/extensions/vscode/src/test/integration/extension.test.ts +39 -0
- topos_mcp-0.3.4/extensions/vscode/src/test/unit/runtime.test.ts +286 -0
- topos_mcp-0.3.4/extensions/vscode/tsconfig.json +16 -0
- topos_mcp-0.3.4/extensions/vscode/tsconfig.test.json +10 -0
- topos_mcp-0.3.4/extensions/vscode/workflow/publishing.md +714 -0
- topos_mcp-0.3.4/install.sh +503 -0
- topos_mcp-0.3.4/packaging/macos-entitlements.plist +10 -0
- topos_mcp-0.3.4/pyproject.toml +75 -0
- topos_mcp-0.3.4/src/cfg.rs +304 -0
- topos_mcp-0.3.4/src/lib.rs +29 -0
- topos_mcp-0.3.4/src/probes_ast.rs +148 -0
- topos_mcp-0.3.4/src/profunctors.rs +129 -0
- topos_mcp-0.3.4/src/uast.rs +142 -0
- topos_mcp-0.3.4/tests/__init__.py +0 -0
- topos_mcp-0.3.4/tests/cli/__init__.py +1 -0
- topos_mcp-0.3.4/tests/cli/test_coverage.py +85 -0
- topos_mcp-0.3.4/tests/cli/test_helpers.py +85 -0
- topos_mcp-0.3.4/tests/cli/test_main.py +26 -0
- topos_mcp-0.3.4/tests/cli/test_quality.py +188 -0
- topos_mcp-0.3.4/tests/cli/test_system.py +94 -0
- topos_mcp-0.3.4/tests/core/__init__.py +0 -0
- topos_mcp-0.3.4/tests/core/test_core.py +86 -0
- topos_mcp-0.3.4/tests/evaluation/__init__.py +0 -0
- topos_mcp-0.3.4/tests/evaluation/test_calibration.py +143 -0
- topos_mcp-0.3.4/tests/evaluation/test_logic.py +338 -0
- topos_mcp-0.3.4/tests/evaluation/test_policies_base.py +157 -0
- topos_mcp-0.3.4/tests/evaluation/test_preferences.py +126 -0
- topos_mcp-0.3.4/tests/fixtures/binarytrees/binarytrees.cpp +81 -0
- topos_mcp-0.3.4/tests/fixtures/binarytrees/binarytrees.js +61 -0
- topos_mcp-0.3.4/tests/fixtures/binarytrees/binarytrees.py +75 -0
- topos_mcp-0.3.4/tests/fixtures/binarytrees/binarytrees.rs +88 -0
- topos_mcp-0.3.4/tests/functors/__init__.py +0 -0
- topos_mcp-0.3.4/tests/functors/probes/__init__.py +0 -0
- topos_mcp-0.3.4/tests/functors/probes/ast/__init__.py +0 -0
- topos_mcp-0.3.4/tests/functors/probes/ast/test_ast_metrics.py +157 -0
- topos_mcp-0.3.4/tests/functors/probes/mdg/__init__.py +0 -0
- topos_mcp-0.3.4/tests/functors/probes/mdg/test_pdg_metrics.py +842 -0
- topos_mcp-0.3.4/tests/functors/profunctors/__init__.py +0 -0
- topos_mcp-0.3.4/tests/functors/profunctors/test_profunctors.py +181 -0
- topos_mcp-0.3.4/tests/functors/profunctors/uast/__init__.py +0 -0
- topos_mcp-0.3.4/tests/functors/profunctors/uast/test_structural_test_coverage.py +232 -0
- topos_mcp-0.3.4/tests/functors/profunctors/uast/test_uast_comparison.py +146 -0
- topos_mcp-0.3.4/tests/graphs/__init__.py +0 -0
- topos_mcp-0.3.4/tests/graphs/ast/__init__.py +0 -0
- topos_mcp-0.3.4/tests/graphs/ast/test_multilang_ast.py +149 -0
- topos_mcp-0.3.4/tests/graphs/cfg/__init__.py +0 -0
- topos_mcp-0.3.4/tests/graphs/cfg/test_cfg.py +149 -0
- topos_mcp-0.3.4/tests/graphs/cpg/__init__.py +0 -0
- topos_mcp-0.3.4/tests/graphs/cpg/test_cpg.py +69 -0
- topos_mcp-0.3.4/tests/graphs/pdg/__init__.py +0 -0
- topos_mcp-0.3.4/tests/graphs/pdg/test_pdg_program_dependence.py +45 -0
- topos_mcp-0.3.4/tests/graphs/test_graphs.py +375 -0
- topos_mcp-0.3.4/tests/mcp/__init__.py +0 -0
- topos_mcp-0.3.4/tests/mcp/conftest.py +20 -0
- topos_mcp-0.3.4/tests/mcp/test_assess.py +154 -0
- topos_mcp-0.3.4/tests/mcp/test_compare.py +47 -0
- topos_mcp-0.3.4/tests/mcp/test_docs.py +56 -0
- topos_mcp-0.3.4/tests/mcp/test_evaluate.py +264 -0
- topos_mcp-0.3.4/tests/mcp/test_inspect.py +68 -0
- topos_mcp-0.3.4/tests/mcp/test_preference_walk_tool.py +112 -0
- topos_mcp-0.3.4/tests/mcp/test_prompts.py +26 -0
- topos_mcp-0.3.4/tests/mcp/test_resources.py +36 -0
- topos_mcp-0.3.4/tests/mcp/test_security.py +66 -0
- topos_mcp-0.3.4/tests/parity/__init__.py +0 -0
- topos_mcp-0.3.4/tests/parity/baseline_v1.py +160 -0
- topos_mcp-0.3.4/tests/parity/test_rust_parity.py +103 -0
- topos_mcp-0.3.4/tests/utils/__init__.py +0 -0
- topos_mcp-0.3.4/tests/utils/test_discovery.py +68 -0
- topos_mcp-0.3.4/tests/utils/test_utils.py +53 -0
- topos_mcp-0.3.4/topos/__init__.py +53 -0
- topos_mcp-0.3.4/topos/cli/__init__.py +5 -0
- topos_mcp-0.3.4/topos/cli/commands/__init__.py +1 -0
- topos_mcp-0.3.4/topos/cli/commands/coverage.py +152 -0
- topos_mcp-0.3.4/topos/cli/commands/quality.py +281 -0
- topos_mcp-0.3.4/topos/cli/commands/system.py +158 -0
- topos_mcp-0.3.4/topos/cli/evaluation.py +404 -0
- topos_mcp-0.3.4/topos/cli/installation.py +129 -0
- topos_mcp-0.3.4/topos/cli/main.py +34 -0
- topos_mcp-0.3.4/topos/core/__init__.py +58 -0
- topos_mcp-0.3.4/topos/core/category.py +163 -0
- topos_mcp-0.3.4/topos/core/morphism.py +211 -0
- topos_mcp-0.3.4/topos/core/object.py +122 -0
- topos_mcp-0.3.4/topos/core/omega.py +409 -0
- topos_mcp-0.3.4/topos/evaluation/__init__.py +54 -0
- topos_mcp-0.3.4/topos/evaluation/characteristic_morphism.py +335 -0
- topos_mcp-0.3.4/topos/evaluation/policies/__init__.py +62 -0
- topos_mcp-0.3.4/topos/evaluation/policies/base.py +253 -0
- topos_mcp-0.3.4/topos/evaluation/policies/calibration.py +105 -0
- topos_mcp-0.3.4/topos/evaluation/policies/clones.py +42 -0
- topos_mcp-0.3.4/topos/evaluation/policies/composable.py +135 -0
- topos_mcp-0.3.4/topos/evaluation/policies/coverage.py +97 -0
- topos_mcp-0.3.4/topos/evaluation/policies/secure.py +100 -0
- topos_mcp-0.3.4/topos/evaluation/policies/simple.py +142 -0
- topos_mcp-0.3.4/topos/evaluation/preferences.py +243 -0
- topos_mcp-0.3.4/topos/functors/probes/__init__.py +21 -0
- topos_mcp-0.3.4/topos/functors/probes/ast/__init__.py +12 -0
- topos_mcp-0.3.4/topos/functors/probes/ast/complexity.py +127 -0
- topos_mcp-0.3.4/topos/functors/probes/ast/entropy.py +70 -0
- topos_mcp-0.3.4/topos/functors/probes/cfg/__init__.py +5 -0
- topos_mcp-0.3.4/topos/functors/probes/cfg/complexity.py +140 -0
- topos_mcp-0.3.4/topos/functors/probes/cfg/paths.py +23 -0
- topos_mcp-0.3.4/topos/functors/probes/cpg/__init__.py +5 -0
- topos_mcp-0.3.4/topos/functors/probes/cpg/danger.py +109 -0
- topos_mcp-0.3.4/topos/functors/probes/cpg/taint.py +121 -0
- topos_mcp-0.3.4/topos/functors/probes/mdg/__init__.py +24 -0
- topos_mcp-0.3.4/topos/functors/probes/mdg/coupling.py +165 -0
- topos_mcp-0.3.4/topos/functors/probes/mdg/fan.py +82 -0
- topos_mcp-0.3.4/topos/functors/probes/uast/__init__.py +25 -0
- topos_mcp-0.3.4/topos/functors/probes/uast/compare.py +147 -0
- topos_mcp-0.3.4/topos/functors/probes/uast/signature.py +127 -0
- topos_mcp-0.3.4/topos/functors/profunctors/__init__.py +18 -0
- topos_mcp-0.3.4/topos/functors/profunctors/ast/__init__.py +19 -0
- topos_mcp-0.3.4/topos/functors/profunctors/ast/compare.py +486 -0
- topos_mcp-0.3.4/topos/functors/profunctors/cfg/__init__.py +19 -0
- topos_mcp-0.3.4/topos/functors/profunctors/cfg/compare.py +97 -0
- topos_mcp-0.3.4/topos/functors/profunctors/cpg/__init__.py +19 -0
- topos_mcp-0.3.4/topos/functors/profunctors/cpg/compare.py +105 -0
- topos_mcp-0.3.4/topos/functors/profunctors/mdg/__init__.py +21 -0
- topos_mcp-0.3.4/topos/functors/profunctors/mdg/compare.py +110 -0
- topos_mcp-0.3.4/topos/functors/profunctors/pdg/__init__.py +19 -0
- topos_mcp-0.3.4/topos/functors/profunctors/pdg/compare.py +115 -0
- topos_mcp-0.3.4/topos/functors/profunctors/uast/__init__.py +21 -0
- topos_mcp-0.3.4/topos/functors/profunctors/uast/compare.py +147 -0
- topos_mcp-0.3.4/topos/functors/profunctors/uast/structural_test_coverage.py +308 -0
- topos_mcp-0.3.4/topos/graphs/__init__.py +40 -0
- topos_mcp-0.3.4/topos/graphs/ast/__init__.py +15 -0
- topos_mcp-0.3.4/topos/graphs/ast/dispatch.py +101 -0
- topos_mcp-0.3.4/topos/graphs/ast/object.py +62 -0
- topos_mcp-0.3.4/topos/graphs/ast/providers/__init__.py +4 -0
- topos_mcp-0.3.4/topos/graphs/ast/providers/base.py +17 -0
- topos_mcp-0.3.4/topos/graphs/ast/providers/native_provider.py +57 -0
- topos_mcp-0.3.4/topos/graphs/ast/providers/tree_sitter_provider.py +67 -0
- topos_mcp-0.3.4/topos/graphs/ast/types.py +28 -0
- topos_mcp-0.3.4/topos/graphs/base.py +62 -0
- topos_mcp-0.3.4/topos/graphs/cfg/__init__.py +19 -0
- topos_mcp-0.3.4/topos/graphs/cfg/builder.py +450 -0
- topos_mcp-0.3.4/topos/graphs/cfg/models.py +74 -0
- topos_mcp-0.3.4/topos/graphs/cfg/object.py +81 -0
- topos_mcp-0.3.4/topos/graphs/cpg/__init__.py +27 -0
- topos_mcp-0.3.4/topos/graphs/cpg/builder.py +116 -0
- topos_mcp-0.3.4/topos/graphs/cpg/models.py +55 -0
- topos_mcp-0.3.4/topos/graphs/cpg/object.py +75 -0
- topos_mcp-0.3.4/topos/graphs/mdg/__init__.py +25 -0
- topos_mcp-0.3.4/topos/graphs/mdg/object.py +410 -0
- topos_mcp-0.3.4/topos/graphs/pdg/__init__.py +25 -0
- topos_mcp-0.3.4/topos/graphs/pdg/object.py +219 -0
- topos_mcp-0.3.4/topos/graphs/uast/__init__.py +25 -0
- topos_mcp-0.3.4/topos/graphs/uast/mapper_common.py +136 -0
- topos_mcp-0.3.4/topos/graphs/uast/mapper_cpp.py +68 -0
- topos_mcp-0.3.4/topos/graphs/uast/mapper_javascript.py +73 -0
- topos_mcp-0.3.4/topos/graphs/uast/mapper_python.py +69 -0
- topos_mcp-0.3.4/topos/graphs/uast/mapper_rust.py +80 -0
- topos_mcp-0.3.4/topos/graphs/uast/mapper_typescript.py +41 -0
- topos_mcp-0.3.4/topos/graphs/uast/models.py +64 -0
- topos_mcp-0.3.4/topos/mcp/__init__.py +12 -0
- topos_mcp-0.3.4/topos/mcp/cache.py +70 -0
- topos_mcp-0.3.4/topos/mcp/evaluation.py +257 -0
- topos_mcp-0.3.4/topos/mcp/formatting.py +262 -0
- topos_mcp-0.3.4/topos/mcp/prompts/__init__.py +3 -0
- topos_mcp-0.3.4/topos/mcp/prompts/refactor.py +102 -0
- topos_mcp-0.3.4/topos/mcp/resources/__init__.py +3 -0
- topos_mcp-0.3.4/topos/mcp/resources/content/lattice.md +78 -0
- topos_mcp-0.3.4/topos/mcp/resources/content/metrics.md +103 -0
- topos_mcp-0.3.4/topos/mcp/resources/content/preferences.md +117 -0
- topos_mcp-0.3.4/topos/mcp/resources/content/priority.md +50 -0
- topos_mcp-0.3.4/topos/mcp/resources/content/workflows.md +155 -0
- topos_mcp-0.3.4/topos/mcp/resources/docs.py +73 -0
- topos_mcp-0.3.4/topos/mcp/schemas.py +798 -0
- topos_mcp-0.3.4/topos/mcp/security.py +133 -0
- topos_mcp-0.3.4/topos/mcp/security_findings.py +142 -0
- topos_mcp-0.3.4/topos/mcp/server.py +71 -0
- topos_mcp-0.3.4/topos/mcp/tools/__init__.py +11 -0
- topos_mcp-0.3.4/topos/mcp/tools/assess.py +294 -0
- topos_mcp-0.3.4/topos/mcp/tools/compare.py +127 -0
- topos_mcp-0.3.4/topos/mcp/tools/coverage.py +232 -0
- topos_mcp-0.3.4/topos/mcp/tools/docs.py +52 -0
- topos_mcp-0.3.4/topos/mcp/tools/evaluate.py +456 -0
- topos_mcp-0.3.4/topos/mcp/tools/inspect.py +165 -0
- topos_mcp-0.3.4/topos/mcp/tools/preferences.py +165 -0
- topos_mcp-0.3.4/topos/utils/__init__.py +45 -0
- topos_mcp-0.3.4/topos/utils/ast/__init__.py +10 -0
- topos_mcp-0.3.4/topos/utils/discovery.py +231 -0
- topos_mcp-0.3.4/topos/utils/tree_sitter.py +358 -0
- 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
|