codegraph-ir 0.2.1__tar.gz → 0.4.0__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 (154) hide show
  1. codegraph_ir-0.4.0/.pre-commit-hooks.yaml +19 -0
  2. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/Code-IR.md +3 -1
  3. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/PKG-INFO +31 -3
  4. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/README.md +26 -1
  5. codegraph_ir-0.4.0/docs/agents.md +70 -0
  6. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/docs/architecture-rules.md +20 -0
  7. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/docs/gate-noise.md +9 -1
  8. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/docs/languages.md +44 -0
  9. codegraph_ir-0.4.0/docs/lsp.md +40 -0
  10. codegraph_ir-0.4.0/docs/plan-0.3.md +109 -0
  11. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/docs/roadmap.md +1 -1
  12. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/docs/status.md +12 -1
  13. codegraph_ir-0.4.0/docs/writing-an-adapter.md +309 -0
  14. codegraph_ir-0.4.0/examples/rust-adapter/NOTES.md +145 -0
  15. codegraph_ir-0.4.0/examples/rust-adapter/README.md +15 -0
  16. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/pyproject.toml +12 -5
  17. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/schemas/component_spec.schema.json +6 -0
  18. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/__init__.py +1 -1
  19. codegraph_ir-0.4.0/src/cgir/analyses/coverage_link.py +152 -0
  20. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/analyses/effects.py +49 -15
  21. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/analyses/symbols.py +46 -1
  22. codegraph_ir-0.4.0/src/cgir/api/lsp_server.py +168 -0
  23. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/api/mcp_server.py +10 -14
  24. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/cli.py +53 -1
  25. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/hooks.py +4 -1
  26. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/ir/component_spec.py +3 -0
  27. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/languages/base.py +20 -1
  28. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/languages/go.py +20 -10
  29. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/languages/python.py +53 -27
  30. codegraph_ir-0.4.0/src/cgir/languages/registry.py +104 -0
  31. codegraph_ir-0.4.0/src/cgir/languages/rust.py +982 -0
  32. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/languages/typescript.py +32 -15
  33. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/pipeline.py +40 -2
  34. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/report/diff.py +29 -1
  35. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/report/lint.py +90 -0
  36. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/report/pack.py +6 -1
  37. codegraph_ir-0.4.0/src/cgir/report/search.py +182 -0
  38. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/slicing/slicer.py +8 -1
  39. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/sources/tree_sitter_source.py +20 -1
  40. codegraph_ir-0.4.0/tests/unit/test_confidence.py +122 -0
  41. codegraph_ir-0.4.0/tests/unit/test_coverage_link.py +128 -0
  42. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_go_adapter.py +44 -0
  43. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_lint.py +72 -0
  44. codegraph_ir-0.4.0/tests/unit/test_lsp.py +112 -0
  45. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_pins.py +18 -0
  46. codegraph_ir-0.4.0/tests/unit/test_plugin_registry.py +117 -0
  47. codegraph_ir-0.4.0/tests/unit/test_rust_adapter.py +634 -0
  48. codegraph_ir-0.4.0/tests/unit/test_search.py +136 -0
  49. codegraph_ir-0.2.1/src/cgir/languages/registry.py +0 -27
  50. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/.github/workflows/ci.yml +0 -0
  51. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/.github/workflows/release.yml +0 -0
  52. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/.gitignore +0 -0
  53. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/.pre-commit-config.yaml +0 -0
  54. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/.python-version +0 -0
  55. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/CLAUDE.md +0 -0
  56. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/LICENSE +0 -0
  57. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/RELEASING.md +0 -0
  58. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/action.yml +0 -0
  59. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/docs/README.md +0 -0
  60. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/docs/architecture.md +0 -0
  61. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/docs/development.md +0 -0
  62. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/docs/experiment-log.md +0 -0
  63. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/docs/feature-research.md +0 -0
  64. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/docs/github-action.md +0 -0
  65. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/docs/plan-0.2-0.4.md +0 -0
  66. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/docs/strategy.md +0 -0
  67. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/examples/agent-pr-contract-drift.md +0 -0
  68. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/analyses/__init__.py +0 -0
  69. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/analyses/call_graph.py +0 -0
  70. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/analyses/cfg.py +0 -0
  71. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/analyses/entrypoints.py +0 -0
  72. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/analyses/param_flow.py +0 -0
  73. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/analyses/pdg.py +0 -0
  74. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/analyses/purity.py +0 -0
  75. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/analyses/reaching_defs.py +0 -0
  76. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/api/__init__.py +0 -0
  77. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/api/server.py +0 -0
  78. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/config.py +0 -0
  79. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/export/__init__.py +0 -0
  80. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/export/graphml.py +0 -0
  81. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/export/html_viz.py +0 -0
  82. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/export/json_export.py +0 -0
  83. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/export/mermaid.py +0 -0
  84. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/export/neo4j.py +0 -0
  85. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/ir/__init__.py +0 -0
  86. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/ir/edges.py +0 -0
  87. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/ir/graph.py +0 -0
  88. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/ir/nodes.py +0 -0
  89. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/languages/__init__.py +0 -0
  90. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/languages/cache.py +0 -0
  91. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/manifest.py +0 -0
  92. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/py.typed +0 -0
  93. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/regenerate/__init__.py +0 -0
  94. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/regenerate/prompt_pack.py +0 -0
  95. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/regenerate/regenerator.py +0 -0
  96. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/report/__init__.py +0 -0
  97. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/report/flow.py +0 -0
  98. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/report/impact.py +0 -0
  99. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/report/pins.py +0 -0
  100. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/report/stats.py +0 -0
  101. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/slicing/__init__.py +0 -0
  102. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/sources/__init__.py +0 -0
  103. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/sources/base.py +0 -0
  104. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/sources/codeql_source.py +0 -0
  105. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/sources/joern_source.py +0 -0
  106. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/trace/__init__.py +0 -0
  107. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/trace/trace_map.py +0 -0
  108. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/verify.py +0 -0
  109. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/src/cgir/watch.py +0 -0
  110. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/__init__.py +0 -0
  111. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/conftest.py +0 -0
  112. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/fixtures/python_sample/orchestrator.py +0 -0
  113. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/fixtures/python_sample/pricing.py +0 -0
  114. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/fixtures/ts_sample/api/service.ts +0 -0
  115. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/fixtures/ts_sample/util.ts +0 -0
  116. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/integration/__init__.py +0 -0
  117. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/integration/test_api.py +0 -0
  118. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/integration/test_cli_scan.py +0 -0
  119. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/integration/test_hook.py +0 -0
  120. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/integration/test_init.py +0 -0
  121. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/__init__.py +0 -0
  122. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_action.py +0 -0
  123. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_call_graph.py +0 -0
  124. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_cfg.py +0 -0
  125. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_component_spec.py +0 -0
  126. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_diff.py +0 -0
  127. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_effects.py +0 -0
  128. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_entrypoints.py +0 -0
  129. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_graphml.py +0 -0
  130. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_html_viz.py +0 -0
  131. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_impact.py +0 -0
  132. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_impact_run.py +0 -0
  133. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_ir_graph.py +0 -0
  134. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_language_adapter.py +0 -0
  135. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_manifest.py +0 -0
  136. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_mcp_tools.py +0 -0
  137. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_mermaid.py +0 -0
  138. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_pack.py +0 -0
  139. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_param_flow.py +0 -0
  140. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_pdg.py +0 -0
  141. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_purity.py +0 -0
  142. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_python_di.py +0 -0
  143. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_reaching_defs.py +0 -0
  144. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_regenerator.py +0 -0
  145. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_shapes.py +0 -0
  146. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_slicer.py +0 -0
  147. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_source_cache.py +0 -0
  148. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_stats.py +0 -0
  149. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_symbols.py +0 -0
  150. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_trace_map.py +0 -0
  151. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_tree_sitter_source.py +0 -0
  152. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_typescript_adapter.py +0 -0
  153. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_verify.py +0 -0
  154. {codegraph_ir-0.2.1 → codegraph_ir-0.4.0}/tests/unit/test_watch.py +0 -0
@@ -0,0 +1,19 @@
1
+ # CGIR contract seatbelt for the pre-commit framework (https://pre-commit.com).
2
+ # In your repo's .pre-commit-config.yaml:
3
+ #
4
+ # repos:
5
+ # - repo: https://github.com/asonkiya/llm-semantic-compilers
6
+ # rev: v0.2.1
7
+ # hooks:
8
+ # - id: cgir-contract-check
9
+ #
10
+ - id: cgir-contract-check
11
+ name: cgir contract seatbelt
12
+ description: >-
13
+ Block commits that alter component contracts: effect gain/loss on
14
+ net/fs/db, pin violations (cgir: pure / stable-signature / frozen),
15
+ and referenced type-shape drift.
16
+ entry: cgir hook run
17
+ language: python
18
+ pass_filenames: false
19
+ types_or: [python, ts, tsx, go]
@@ -60,7 +60,9 @@ H --> I[LLM regenerate / test / trace]
60
60
  "trace":{"type":"array","items":{"type":"string"}}}}
61
61
  ```
62
62
 
63
- `pins` are developer-declared invariants extracted from `cgir:` comment
63
+ `lexical_effects` marks the subset of `effects` backed only by lexical
64
+ (suffix/receiver-name) evidence — lower confidence, reported but not
65
+ build-failing by default. `pins` are developer-declared invariants extracted from `cgir:` comment
64
66
  pragmas (`pure`, `no-<tag>`, `stable-signature`, `frozen`): state pins hold on
65
67
  every scan; change pins hold across scan pairs and are always enforced.
66
68
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codegraph-ir
3
- Version: 0.2.1
3
+ Version: 0.4.0
4
4
  Summary: The deterministic contract layer for AI-modified codebases — effects, purity, contracts, and blast radius, with zero LLM calls.
5
5
  Project-URL: Homepage, https://github.com/asonkiya/llm-semantic-compilers
6
6
  Project-URL: Repository, https://github.com/asonkiya/llm-semantic-compilers
@@ -43,8 +43,9 @@ Requires-Python: >=3.11
43
43
  Requires-Dist: jsonschema>=4.21
44
44
  Requires-Dist: networkx>=3.2
45
45
  Requires-Dist: pydantic>=2.6
46
- Requires-Dist: tree-sitter-go<0.25,>=0.23
46
+ Requires-Dist: tree-sitter-go<0.24,>=0.23
47
47
  Requires-Dist: tree-sitter-python<0.24,>=0.23
48
+ Requires-Dist: tree-sitter-rust<0.23.3,>=0.23
48
49
  Requires-Dist: tree-sitter-typescript<0.24,>=0.23
49
50
  Requires-Dist: tree-sitter<0.25,>=0.23
50
51
  Requires-Dist: typer>=0.12
@@ -61,6 +62,8 @@ Requires-Dist: ruff>=0.5; extra == 'dev'
61
62
  Requires-Dist: types-jsonschema; extra == 'dev'
62
63
  Provides-Extra: llm
63
64
  Requires-Dist: anthropic>=0.40; extra == 'llm'
65
+ Provides-Extra: lsp
66
+ Requires-Dist: pygls>=1.3; extra == 'lsp'
64
67
  Provides-Extra: mcp
65
68
  Requires-Dist: mcp>=1.2; extra == 'mcp'
66
69
  Description-Content-Type: text/markdown
@@ -90,13 +93,14 @@ For library/agent use in a project: `uv pip install codegraph-ir`
90
93
  ## The local loop
91
94
 
92
95
  ```bash
93
- cgir scan . # build the .cgir index (Python, TypeScript, Go)
96
+ cgir scan . # build the .cgir index (Python, TypeScript, Go, Rust)
94
97
  cgir watch . # keep it live: re-scan + show contract drift on save
95
98
  cgir pack app.service.charge --repo . # minimal context bundle for one component
96
99
  cgir impact app.service.charge # blast radius: affected callers, entrypoints, tests
97
100
  cgir impact app.service.charge --candidate new.py --repo . # radius narrowed by the real delta
98
101
  cgir verify app.service.charge --candidate new.py --repo . # contract-check an edit
99
102
  cgir hook install # pre-commit seatbelt: block contract-breaking commits
103
+ cgir lsp # editor squiggles for pin violations + drift (cgir[lsp])
100
104
  ```
101
105
 
102
106
  `pack` → edit → `impact` → `verify` → `hook`, with `watch` keeping the index
@@ -127,6 +131,30 @@ change touches, and `verify` / `impact_of_change` to contract-check its own
127
131
  edit before proposing it. See [`examples/`](./examples) for a worked
128
132
  agent-PR case study.
129
133
 
134
+ Setup guide for agents (MCP config + CLAUDE.md snippet): [`docs/agents.md`](./docs/agents.md).
135
+ Or via the [pre-commit framework](https://pre-commit.com): hook id `cgir-contract-check`.
136
+
137
+ ## How it compares (honestly)
138
+
139
+ | | **CGIR** | CodeGraph-style MCP graphs | Greptile / CodeRabbit | import-linter / ArchUnit | oasdiff / Pact |
140
+ |---|---|---|---|---|---|
141
+ | Effects & purity contracts per function | ✅ | ❌ | ❌ LLM judgment | ❌ | ❌ |
142
+ | Invariants declared in source (`# cgir: pure`) & enforced | ✅ | ❌ | ❌ | imports only | API boundary only |
143
+ | Deterministic (same input → same verdict, zero LLM) | ✅ | ✅ structure only | ❌ | ✅ | ✅ |
144
+ | Catches "service silently stopped calling the backend" | ✅ | ❌ | sometimes | ❌ | at spec boundaries |
145
+ | Blast radius + coverage-grounded test selection | ✅ | ❌ | ❌ | ❌ | ❌ |
146
+ | Agent context over MCP | ✅ contract packs | ✅ broader retrieval | ❌ | ❌ | ❌ |
147
+ | Finds logic bugs | ❌ | ❌ | ✅ | ❌ | ❌ |
148
+ | Languages | 4 builtin + [plugin API](./docs/languages.md) | 30+ | most | per-tool | spec-level |
149
+ | Cycle / layer rules | ✅ | ❌ | ❌ | ✅ mature | ❌ |
150
+ | Cost | free, local | mostly free | ~$24–30/user/mo | free | free |
151
+
152
+ Where CGIR loses, we say so: it does **not** find logic bugs (a subtly wrong
153
+ algorithm with unchanged effects passes the gate — pair it with tests or a
154
+ reviewer), supports 3 languages not 38, and its effect detection is static
155
+ analysis with [documented, measured limits](./docs/gate-noise.md) — every
156
+ tag carries a confidence tier so you know which claims are verified.
157
+
130
158
  ## Docs
131
159
 
132
160
  - [`docs/strategy.md`](./docs/strategy.md) — positioning: the deterministic contract layer
@@ -23,13 +23,14 @@ For library/agent use in a project: `uv pip install codegraph-ir`
23
23
  ## The local loop
24
24
 
25
25
  ```bash
26
- cgir scan . # build the .cgir index (Python, TypeScript, Go)
26
+ cgir scan . # build the .cgir index (Python, TypeScript, Go, Rust)
27
27
  cgir watch . # keep it live: re-scan + show contract drift on save
28
28
  cgir pack app.service.charge --repo . # minimal context bundle for one component
29
29
  cgir impact app.service.charge # blast radius: affected callers, entrypoints, tests
30
30
  cgir impact app.service.charge --candidate new.py --repo . # radius narrowed by the real delta
31
31
  cgir verify app.service.charge --candidate new.py --repo . # contract-check an edit
32
32
  cgir hook install # pre-commit seatbelt: block contract-breaking commits
33
+ cgir lsp # editor squiggles for pin violations + drift (cgir[lsp])
33
34
  ```
34
35
 
35
36
  `pack` → edit → `impact` → `verify` → `hook`, with `watch` keeping the index
@@ -60,6 +61,30 @@ change touches, and `verify` / `impact_of_change` to contract-check its own
60
61
  edit before proposing it. See [`examples/`](./examples) for a worked
61
62
  agent-PR case study.
62
63
 
64
+ Setup guide for agents (MCP config + CLAUDE.md snippet): [`docs/agents.md`](./docs/agents.md).
65
+ Or via the [pre-commit framework](https://pre-commit.com): hook id `cgir-contract-check`.
66
+
67
+ ## How it compares (honestly)
68
+
69
+ | | **CGIR** | CodeGraph-style MCP graphs | Greptile / CodeRabbit | import-linter / ArchUnit | oasdiff / Pact |
70
+ |---|---|---|---|---|---|
71
+ | Effects & purity contracts per function | ✅ | ❌ | ❌ LLM judgment | ❌ | ❌ |
72
+ | Invariants declared in source (`# cgir: pure`) & enforced | ✅ | ❌ | ❌ | imports only | API boundary only |
73
+ | Deterministic (same input → same verdict, zero LLM) | ✅ | ✅ structure only | ❌ | ✅ | ✅ |
74
+ | Catches "service silently stopped calling the backend" | ✅ | ❌ | sometimes | ❌ | at spec boundaries |
75
+ | Blast radius + coverage-grounded test selection | ✅ | ❌ | ❌ | ❌ | ❌ |
76
+ | Agent context over MCP | ✅ contract packs | ✅ broader retrieval | ❌ | ❌ | ❌ |
77
+ | Finds logic bugs | ❌ | ❌ | ✅ | ❌ | ❌ |
78
+ | Languages | 4 builtin + [plugin API](./docs/languages.md) | 30+ | most | per-tool | spec-level |
79
+ | Cycle / layer rules | ✅ | ❌ | ❌ | ✅ mature | ❌ |
80
+ | Cost | free, local | mostly free | ~$24–30/user/mo | free | free |
81
+
82
+ Where CGIR loses, we say so: it does **not** find logic bugs (a subtly wrong
83
+ algorithm with unchanged effects passes the gate — pair it with tests or a
84
+ reviewer), supports 3 languages not 38, and its effect detection is static
85
+ analysis with [documented, measured limits](./docs/gate-noise.md) — every
86
+ tag carries a confidence tier so you know which claims are verified.
87
+
63
88
  ## Docs
64
89
 
65
90
  - [`docs/strategy.md`](./docs/strategy.md) — positioning: the deterministic contract layer
@@ -0,0 +1,70 @@
1
+ # CGIR for agents — setup and protocol
2
+
3
+ Agents are CGIR's first-class users: instead of grepping and reading whole
4
+ files, an agent queries the semantic index and contract-checks its own edits
5
+ before proposing them.
6
+
7
+ ## 1. Serve the index over MCP
8
+
9
+ Scan once (or run `cgir watch` to keep it live), then register the server:
10
+
11
+ ```jsonc
12
+ // .mcp.json at the repo root (Claude Code picks this up automatically)
13
+ {
14
+ "mcpServers": {
15
+ "cgir": {
16
+ "command": "cgir",
17
+ "args": ["mcp", "--index", ".cgir"]
18
+ }
19
+ }
20
+ }
21
+ ```
22
+
23
+ Requires `cgir` on PATH with the mcp extra (`uv tool install "codegraph-ir[mcp]"`
24
+ or `uv pip install "codegraph-ir[mcp]"`).
25
+
26
+ ## 2. Teach the agent the protocol (CLAUDE.md / AGENTS.md snippet)
27
+
28
+ Paste into the repo's agent instructions:
29
+
30
+ ```markdown
31
+ ## Semantic index (CGIR)
32
+
33
+ This repo has a semantic contract index served over MCP (server: `cgir`).
34
+ Prefer it over grepping:
35
+
36
+ - **Finding code**: `search(query)` — ranked free terms + contract
37
+ predicates: `kind:pure`, `effects:net`, `effects:none`, `lexical:false`,
38
+ `callers:>3`, `pins:pure`, `entrypoint:HTTP`, `lang:go`, `covered:false`.
39
+ E.g. "effects:net lexical:false callers:>2" = verified network code with
40
+ real fan-in — queries grep and vector search cannot answer.
41
+ - **Loading context to edit a component**: `pack(component_id)` — the
42
+ minimal contract bundle (signature, effects, pins, callee interfaces,
43
+ linked tests). Do NOT read whole files for context first.
44
+ - **Before changing a component**: `impact(component_id)` — blast radius:
45
+ affected callers, entrypoints at risk, exactly which tests to run.
46
+ - **After drafting an edit**: `impact_of_change(repo, component_id,
47
+ candidate)` — the radius narrowed by your edit's real contract delta;
48
+ `verify(repo, component_id, candidate)` — contract-check before proposing.
49
+
50
+ Respect `Pinned:` lines in packs — they are hard invariants (`pure`,
51
+ `no-net`, `stable-signature`, `frozen`), enforced by the pre-commit hook
52
+ and CI. A rewrite that violates a pin will be rejected; don't attempt it.
53
+ ```
54
+
55
+ ## 3. What the agent gets out of it
56
+
57
+ - **Fewer tokens, better context**: a pack is ~60–800 tokens vs ~400–20,000
58
+ for raw files, and benchmarks show it *beats* whole-file context as
59
+ rewrite input (`docs/experiment-log.md`).
60
+ - **Fewer rejected edits**: `verify`/`impact_of_change` catch contract
61
+ drift before the human (or the hook, or CI) does.
62
+ - **No hallucinated wiring**: packs name DI receivers (`self.svc.method` /
63
+ `this.svc.method`), the exact field names an edit must use.
64
+
65
+ ## 4. The enforcement backstop
66
+
67
+ The same contracts the agent consults are enforced deterministically:
68
+ `cgir hook install` (pre-commit), the [GitHub Action](./github-action.md)
69
+ (per-PR), and `cgir: ` pins in source. The agent can't drift from what the
70
+ index says without something noticing — that's the point.
@@ -58,3 +58,23 @@ component id (`aspen.zones.*` matches `aspen.zones.contains.point_in_polygon`).
58
58
  - **Kind-aware**: "handlers must be orchestrators, not effect adapters."
59
59
  - **Call-target-aware**: layer boundaries over *resolved calls*, catching
60
60
  a cross-layer call even when the import is indirect.
61
+
62
+ ## Cycles and layers
63
+
64
+ ```toml
65
+ [[rule]]
66
+ name = "core is acyclic"
67
+ in = "app.*"
68
+ forbid-cycle = true # Tarjan SCCs over resolved CALLS; self-recursion OK
69
+
70
+ [[rule]]
71
+ name = "layered architecture"
72
+ layers = ["app.api.*", "app.core.*", "app.db.*"] # top -> bottom
73
+ ```
74
+
75
+ `forbid-cycle` fires once per strongly-connected component (size >= 2),
76
+ naming the cycle. `layers` requires dependencies to point *downward*:
77
+ a `db` component calling an `api` component is a violation; same-layer and
78
+ layer-skipping downward calls are fine; components matching no layer are
79
+ ignored. Unlike import linters, these run over the resolved *call* graph —
80
+ and the same index tells you whether the violating edge also carries `net`.
@@ -88,7 +88,15 @@ the `io` / `nondeterm` effect tags (a `print`/timestamp shouldn't fail CI).
88
88
  Trade-off (documented in `diff.violations`): a true removal paired with a
89
89
  new unrelated effectful call is masked — the loss stays visible in the
90
90
  diff report, it just doesn't fail the build.
91
- 2. **Import-resolution stability** — kind/purity flipping on stub-vs-real
91
+ 2. **Confidence tiers** *(landed 0.3.0)* every effect tag now carries
92
+ provenance: `high` (exact/prefix table match) vs `lexical` (bare-suffix /
93
+ receiver-name heuristics — `self.now()`, `db.query()`). Gate rules fire
94
+ on high-confidence tags by default; `:any` opts in
95
+ (`effect-gain:db:any`). Measured on camera-tracking: fs evidence is 89%
96
+ lexical, and 48% of the `calls_effectful` taint tree rests on lexical
97
+ evidence — all still *reported*, no longer build-failing. Lexical means
98
+ unverified, not wrong.
99
+ 3. **Import-resolution stability** — kind/purity flipping on stub-vs-real
92
100
  imports is the dominant `kind-change`/`purity-drop` noise source. A
93
101
  scan run compares two checkouts in one environment (CI does this), so it
94
102
  only bites when the stub *source itself* changes — but it's worth
@@ -28,6 +28,7 @@ full test suite green.
28
28
  | 3 | ingest structural dispatch + attr extraction (normalized `Declaration`s) | ✅ done |
29
29
  | 4 | `TypeScriptAdapter` + per-file language dispatch | ✅ done |
30
30
  | 5 | `GoAdapter` (package=directory merge, struct-field DI, panic≙raise) | ✅ done |
31
+ | 6 | `RustAdapter` (agent-written from the docs, reviewed & promoted; structs/impl, use-trees, match CFG) | ✅ done |
31
32
 
32
33
  **Two languages ship (Python, TypeScript); a mixed repo scans both.**
33
34
  Zero grammar node-type strings remain outside `cgir/languages/`. The CFG builder is pure topology over statement
@@ -66,3 +67,46 @@ closure, symbol-table resolution, slicing, and all seven product surfaces.
66
67
  Register it in `cgir/languages/__init__.py:ADAPTERS`. The effect *taxonomy*
67
68
  (`io`/`net`/`fs`/`db`/`nondeterm`/`raise`) is language-neutral and fixed in
68
69
  `analyses/effects.py`; the adapter only decides which calls map to which tag.
70
+
71
+ **Full authoring guide: [`writing-an-adapter.md`](./writing-an-adapter.md)** — self-contained; written so an implementer (human or agent) needs no other source.
72
+
73
+ ## Packaging a language plugin
74
+
75
+ Adapters are discovered via the ``cgir.languages`` entry-point group — no
76
+ fork needed. A minimal plugin package:
77
+
78
+ ```toml
79
+ # pyproject.toml of cgir-rust
80
+ [project]
81
+ name = "cgir-rust"
82
+ dependencies = ["codegraph-ir", "tree-sitter-rust"]
83
+
84
+ [project.entry-points."cgir.languages"]
85
+ rust = "cgir_rust:RustAdapter"
86
+ ```
87
+
88
+ ```python
89
+ # cgir_rust/__init__.py
90
+ from cgir.languages.base import ADAPTER_API_VERSION, LanguageAdapter
91
+
92
+ class RustAdapter(LanguageAdapter):
93
+ name = "rust"
94
+ file_extensions = (".rs",)
95
+ api_version = ADAPTER_API_VERSION
96
+ # implement: parse, locate_function, direct_effects, call_sites,
97
+ # function_body, block_statements, describe_statement,
98
+ # module_declarations — see GoAdapter for the most recent template.
99
+ # Optional (defaults provided): direct_effects_confidence,
100
+ # global_declared_names.
101
+ ```
102
+
103
+ `pip install cgir-rust` and `cgir languages` shows it. Safety rules:
104
+ builtins win extension conflicts; a plugin that fails to load, isn't a
105
+ `LanguageAdapter`, or reuses a language name is skipped with a warning —
106
+ a broken plugin never crashes cgir. An `api_version` mismatch warns but
107
+ loads (new adapter methods get base-class defaults, so older plugins
108
+ usually keep working).
109
+
110
+ Start from `src/cgir/languages/go.py` — the newest adapter and the best
111
+ template — and mirror `tests/unit/test_go_adapter.py` for the expected
112
+ pipeline-level test coverage.
@@ -0,0 +1,40 @@
1
+ # LSP diagnostics — contract drift in your editor
2
+
3
+ `cgir lsp` is a diagnostics-only language server: on every save it rescans
4
+ the repo and publishes
5
+
6
+ - **errors** for pin violations (`# cgir: pure` that isn't) — visible the
7
+ moment you open the workspace;
8
+ - **warnings** for contract drift vs your previous save (the default
9
+ low-noise gate rules: effect gain/loss on net/fs/db, change pins).
10
+
11
+ Side effect worth having: every refresh rewrites `.cgir`, so MCP, `pack`,
12
+ and `impact` stay fresh while you edit.
13
+
14
+ ```bash
15
+ uv tool install "codegraph-ir[lsp]" # or add the lsp extra to your env
16
+ ```
17
+
18
+ ## Neovim
19
+
20
+ ```lua
21
+ vim.api.nvim_create_autocmd("FileType", {
22
+ pattern = { "python", "typescript", "go" },
23
+ callback = function()
24
+ vim.lsp.start({ name = "cgir", cmd = { "cgir", "lsp" },
25
+ root_dir = vim.fs.root(0, ".cgir") or vim.fn.getcwd() })
26
+ end,
27
+ })
28
+ ```
29
+
30
+ ## VS Code
31
+
32
+ Use any generic LSP client extension (e.g. "LSP Proxy"/"Generic LSP
33
+ Client") with command `cgir lsp`, or wire it into an existing extension's
34
+ `serverCommand` setting. A dedicated extension is on the roadmap.
35
+
36
+ ## Latency honesty
37
+
38
+ Each save triggers a full rescan (~0.5s small repos, ~3–5s large ones).
39
+ Diagnostics update when the scan lands; per-component incremental analysis
40
+ (docs/plan-0.3.md) is the planned fix.
@@ -0,0 +1,109 @@
1
+ # Plan: 0.3 and beyond — trust, reach, and the developer loop
2
+
3
+ Written 2026-07-12 as a durable handoff: any session (any model) should be
4
+ able to pick up any thread from here. Prior art: `plan-0.2-0.4.md` (all
5
+ landed), `gate-noise.md` (measured rule noise), `experiment-log.md`
6
+ (benchmarks), `status.md` (what runs).
7
+
8
+ ## Shipped in the 0.3 cook (see git log 2026-07-12)
9
+
10
+ 1. **Hook UX** — blocked-commit message explains the staged-state trap
11
+ (`git restore -S -W <file>`), found dogfooding.
12
+ 2. **pre-commit framework integration** — `.pre-commit-hooks.yaml` so
13
+ `repo: llm-semantic-compilers` works in anyone's `.pre-commit-config.yaml`.
14
+ 3. **Agent onboarding** — `docs/agents.md`: the MCP config + CLAUDE.md/
15
+ AGENTS.md snippet teaching agents the protocol (search → pack → edit →
16
+ impact_of_change → verify).
17
+ 4. **go.mod cross-package resolution** — ingest reads the `module` directive
18
+ and stashes it on the Repository node; symbols strips the prefix from Go
19
+ import targets and binds the alias to the target *package directory*
20
+ (any module in it — the directory merge exposes all package members).
21
+ 5. **Confidence tiers on effects** — every effect tag carries provenance:
22
+ `high` (exact/prefix table match, import-alias verified) vs `lexical`
23
+ (bare suffix / receiver-name heuristics: `.now`, db-receiver gating).
24
+ New spec field `lexical_effects` (subset of `effects`). Gate rules
25
+ (`effect-gain`/`effect-loss`) fire on high-confidence tags by default;
26
+ `:any` suffix opts into lexical (e.g. `effect-gain:db:any`). This is the
27
+ systematic fix for the `self.now()` false-positive class measured in
28
+ gate-noise.md.
29
+
30
+ ## In flight: market-driven quick strikes (designed 2026-07-12)
31
+
32
+ **A. Ranked, structured search** (`report/search.py`, pure over specs; MCP
33
+ `search` delegates; new `cgir search` CLI command).
34
+
35
+ - Free terms rank: exact name (100) > name-word (50) > id substring (20) >
36
+ entrypoint (15) > doc (5); sum across terms, sort desc, cap 25.
37
+ - Structured predicates (colon syntax, AND-combined, mixable with terms):
38
+ `kind:pure_function` (alias `kind:pure`), `effects:net`, `effects:none`,
39
+ `lexical:true|false`, `callers:>3|0`, `calls:>5`, `pins:pure`,
40
+ `pinned:true`, `lang:go`, `entrypoint:true|HTTP`, `covered:false`.
41
+ Caller counts computed from the spec set (reverse of `calls`).
42
+ - The moat angle: contract predicates no vector index can answer
43
+ ("effects:net lexical:false callers:>3").
44
+
45
+ **B. Cycles + layers in `cgir lint`** (existing `[[rule]]` config).
46
+
47
+ - `forbid-cycle = true` (+ `in` scope glob): Tarjan SCCs over the spec-level
48
+ call graph within scope; every SCC of size ≥2 is a violation naming the
49
+ cycle. Self-recursion (size-1) is NOT a violation — recursion is normal.
50
+ - `layers = ["app.api.*", "app.core.*", "app.db.*"]` (top→bottom): a
51
+ component in a lower layer calling one in a higher layer is a violation
52
+ (dependencies must point down; same-layer calls fine; unmatched
53
+ components ignored).
54
+ - Neutralizes the import-linter/ArchUnit objection using graph data we
55
+ already have — and our violations can say the edge *also* carries `net`.
56
+
57
+ **C. Coverage-grounded test linkage** *(landed)* — `.coverage` (sqlite,
58
+ numbits decoded via stdlib) / `coverage.json --show-contexts` per-test
59
+ contexts mapped onto component spans, unioned with static linkage. On CGIR
60
+ itself (pytest-cov --cov-context=test): components with test linkage went
61
+ 48 → 345 of 389 (12% → 89%). Hot components link many tests (integration
62
+ tests execute the pipeline — accurate); pack embeds at most 5 test sources,
63
+ impact --run keeps the full set. Staleness limit: an old .coverage may
64
+ reference moved lines; static linkage is the floor.
65
+
66
+ ## Not built — next up, in value order
67
+
68
+ 1. **Per-component incremental analysis** (the latency lever). Watch/hook
69
+ re-scan everything (~0.5–4.5s). Design sketch: content-hash manifest
70
+ identifies changed files → re-ingest only those modules → recompute
71
+ analyses for changed components + their upstream closure
72
+ (`compute_typed_impact` IS the scoping machinery) → splice into the
73
+ cached graph. Correctness traps: effect closure is global (a leaf change
74
+ can flip distant orchestrators — the closure must be re-run over the
75
+ merged spec set, which is cheap since it's pure over specs); symbol
76
+ tables must be rebuilt when imports/decls change, else only call-graph
77
+ edges from changed files. Validate: randomized edit-replay equivalence
78
+ (incremental result == full-scan result on N random historical edits).
79
+ 2. **vitest/jest execution for `impact --run`** — runner detection from
80
+ package.json; currently prints the command for TS.
81
+ 3. **Go: struct-in-one-file/methods-in-another field DI** — the class-stub
82
+ merge (registry of package-level types before method attachment).
83
+ 4. **Data-shape v1.1** — full type text for Python fields (needs a second,
84
+ non-DI fields channel), literal-key fingerprints for un-annotated dict
85
+ returns, base-class field inheritance.
86
+ 5. **LSP diagnostics** — watch already computes live drift; publish as
87
+ editor diagnostics (pygls; diagnostics-only server, no completion).
88
+ 6. **`cgir decompose`** — the spec's long-term flagship (PDG-sliced
89
+ functional-core/imperative-shell suggestions + verify loop).
90
+ 7. **Marketplace listing** for the Action; badges; a demo GIF in README.
91
+
92
+ ## The developer loop (what "useful for me" means, concretely)
93
+
94
+ The user's repos and their CGIR state:
95
+ - **camera-tracking** (Py+TS): pins live on branch `cgir-pins`; pre-commit
96
+ hook INSTALLED on the repo (uses PATH cgir = uv tool). `.mcp.json` +
97
+ CLAUDE.md snippet added in this cook so agents use the semantic index.
98
+ - **Indra** (Go): scans clean (301 components). Candidate for pins next.
99
+ - **novel-chrome-extension** (TS/Angular): benchmark corpus, index at
100
+ ~/.cgir-indexes/novel-frontend.
101
+
102
+ Daily loop: `cgir watch` in a terminal (live drift), agents route through
103
+ MCP (`pack`/`impact`/`verify` instead of grep), the hook gates commits, CI
104
+ gates PRs (Action), pins encode the invariants worth keeping.
105
+
106
+ ## Release discipline
107
+
108
+ Tag-triggered trusted publishing (RELEASING.md). Bump BOTH version spots.
109
+ The maintainer pushes tags — releases are a human decision.
@@ -1,6 +1,6 @@
1
1
  # Roadmap
2
2
 
3
- **Active plan: [`plan-0.2-0.4.md`](./plan-0.2-0.4.md)** — pins/init/impact-run (0.2.0), data-shape contracts (0.3.0), Go adapter (0.4.0).
3
+ **Active plan: [`plan-0.3.md`](./plan-0.3.md)** — trust (confidence tiers), reach (go.mod resolution, pre-commit, agents), and the incremental-analysis design. `plan-0.2-0.4.md` is fully landed.
4
4
 
5
5
  Forward-looking sequencing. The grouping mirrors `Code-IR.md` §Architecture: **P0** is "you can produce a `ComponentSpec`," **P1** is "you can trust it," **P2** is "you can scale it." Within each tier the order below reflects current dependencies, not strict chronology — feel free to interleave when dependencies allow.
6
6
 
@@ -71,6 +71,17 @@ cgir scan tests/fixtures/python_sample --out /tmp/cgir-out
71
71
  | `cgir impact --run` (execute the blast-radius tests via pytest node-ids) | done | `report/impact.py:runnable_selectors` |
72
72
  | Data-shape contracts (`types` diff section + opt-in `shape-change` rule; TS interfaces/type-aliases, Py TypedDict/dataclass) | done | `report/diff.py:_type_shape_changes`, `json_export.read_types` |
73
73
  | Go adapter (package=dir symbol merge, struct-field DI, panic≙raise, effects tables) | done | `src/cgir/languages/go.py` |
74
+ | Go cross-package resolution (go.mod module-prefix strip; unique dir-suffix fallback) | done | `analyses/symbols.py:_resolve_go_package` |
75
+ | Confidence tiers (`lexical_effects`; gates fire high-confidence by default, `:any` opts in; pack marks `tag?`) | done | `analyses/effects.py:classify_with_confidence` |
76
+ | Global/nonlocal rebinding as mutation (`global_declared_names` hook) | done | `languages/base.py`, `analyses/cfg.py` |
77
+ | pre-commit framework hook (`cgir-contract-check`) + agents guide | done | `.pre-commit-hooks.yaml`, `docs/agents.md` |
78
+ | Ranked/structured search (`cgir search`, MCP `search`): term ranking + contract predicates (kind/effects/lexical/callers/pins/entrypoint/lang/covered) | done | `src/cgir/report/search.py` |
79
+ | `forbid-cycle` (Tarjan SCCs) + `layers` rules in lint | done | `report/lint.py:_cycle_violations`, `_layer_violations` |
80
+ | Coverage-grounded test linkage (.coverage/coverage.json contexts ∪ static; pack test sources capped) | done | `analyses/coverage_link.py` |
81
+ | LSP diagnostics (`cgir lsp`, `cgir[lsp]` extra): pin violations as errors, gate drift as warnings, on save | done | `src/cgir/api/lsp_server.py`, `docs/lsp.md` |
82
+ | Language plugin registry (`cgir.languages` entry points, `cgir languages`, ADAPTER_API_VERSION; builtins win conflicts, broken plugins degrade to warnings) | done | `languages/registry.py:discover_adapters` |
83
+ | Adapter authoring guide, validated by a docs-only agent experiment (Rust adapter, 36/36, 9/9 bar; gaps folded back; PinIndex line_comment fix) | done | `docs/writing-an-adapter.md`, `examples/rust-adapter/` |
84
+ | Rust adapter (structs+impl methods w/ field DI, use-tree imports, match/loop CFG, effect tiers; agent-written, reviewed+promoted) | done | `src/cgir/languages/rust.py` |
74
85
  | Entrypoint recognition (HTTP/CLI/task decorators) | done | `src/cgir/analyses/entrypoints.py` |
75
86
  | Context packer (`cgir pack`, budget-aware, type closure + docstrings + raises) | done | `src/cgir/report/pack.py` |
76
87
  | Docstring / raises / module-variable extraction | done | `tree_sitter_source._docstring_text`, `_raised_names`, `_add_module_variables` |
@@ -90,7 +101,7 @@ cgir scan tests/fixtures/python_sample --out /tmp/cgir-out
90
101
 
91
102
  ## Test coverage
92
103
 
93
- `pytest -q` runs 444 tests, all green (the table below lists the load-bearing files; newer suites — `test_impact.py`, `test_watch.py`, `test_hook.py`, `test_python_di.py`, `test_typescript_adapter.py`, `test_diff.py` — are described by their docstrings):
104
+ `pytest -q` runs 541 tests, all green (the table below lists the load-bearing files; newer suites — `test_impact.py`, `test_watch.py`, `test_hook.py`, `test_python_di.py`, `test_typescript_adapter.py`, `test_diff.py` — are described by their docstrings):
94
105
 
95
106
  | File | Covers |
96
107
  |---|---|