graphite-code 1.0.0__tar.gz → 1.1.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 (290) hide show
  1. {graphite_code-1.0.0 → graphite_code-1.1.0}/ARCHITECTURE.md +6 -5
  2. {graphite_code-1.0.0 → graphite_code-1.1.0}/CHANGELOG.md +309 -0
  3. {graphite_code-1.0.0 → graphite_code-1.1.0}/CONTRIBUTING.md +11 -6
  4. {graphite_code-1.0.0 → graphite_code-1.1.0}/PKG-INFO +50 -22
  5. {graphite_code-1.0.0 → graphite_code-1.1.0}/README.md +49 -21
  6. {graphite_code-1.0.0 → graphite_code-1.1.0}/RELEASING.md +9 -0
  7. {graphite_code-1.0.0 → graphite_code-1.1.0}/pyproject.toml +35 -5
  8. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/__init__.py +1 -1
  9. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/cache.py +93 -3
  10. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/channel.py +282 -29
  11. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/cli.py +154 -53
  12. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/doctor_probes.py +28 -2
  13. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/graph.py +1 -1
  14. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/mcp_server.py +29 -9
  15. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/probe_process.py +16 -1
  16. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/replacement_audit.py +42 -37
  17. graphite_code-1.1.0/tests/test__win32_ctypes.py +38 -0
  18. graphite_code-1.1.0/tests/test_cache_entry_pruning.py +245 -0
  19. graphite_code-1.1.0/tests/test_channel_attribution.py +254 -0
  20. graphite_code-1.1.0/tests/test_channel_log_integrity.py +216 -0
  21. graphite_code-1.1.0/tests/test_cli_channel_attribution.py +84 -0
  22. graphite_code-1.1.0/tests/test_cli_reference.py +182 -0
  23. graphite_code-1.1.0/tests/test_cluster.py +123 -0
  24. graphite_code-1.1.0/tests/test_context_builder.py +312 -0
  25. graphite_code-1.1.0/tests/test_dependency_install.py +242 -0
  26. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_doctor.py +81 -15
  27. graphite_code-1.1.0/tests/test_doctor_probes.py +390 -0
  28. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_documentation.py +1 -1
  29. graphite_code-1.1.0/tests/test_llm_probe.py +261 -0
  30. graphite_code-1.1.0/tests/test_mcp_server.py +205 -0
  31. graphite_code-1.1.0/tests/test_mcp_server_channel_attribution.py +191 -0
  32. graphite_code-1.1.0/tests/test_mutation_tests.py +382 -0
  33. graphite_code-1.1.0/tests/test_ollama_executor.py +272 -0
  34. graphite_code-1.1.0/tests/test_probe_process.py +207 -0
  35. graphite_code-1.1.0/tests/test_process_contracts.py +74 -0
  36. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_replacement_audit.py +6 -6
  37. graphite_code-1.1.0/tests/test_route_pool_execution.py +363 -0
  38. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_synthetic_repo.py +36 -0
  39. graphite_code-1.1.0/tests/test_ts_bridge.py +241 -0
  40. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_typescript_activation.py +38 -0
  41. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_verify_published_release.py +13 -0
  42. graphite_code-1.0.0/tests/test_cli_reference.py +0 -66
  43. {graphite_code-1.0.0 → graphite_code-1.1.0}/.gitignore +0 -0
  44. {graphite_code-1.0.0 → graphite_code-1.1.0}/LICENSE +0 -0
  45. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/__main__.py +0 -0
  46. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/_cleanup_worker.py +0 -0
  47. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/_win32_ctypes.py +0 -0
  48. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/activation.py +0 -0
  49. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/agent_hooks.py +0 -0
  50. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/agent_settings.py +0 -0
  51. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/analyze.py +0 -0
  52. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/answer_contract.py +0 -0
  53. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/bootstrap.py +0 -0
  54. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/buildlock.py +0 -0
  55. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/cluster.py +0 -0
  56. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/config.py +0 -0
  57. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/context.py +0 -0
  58. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/daemon.py +0 -0
  59. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/daemon_health.py +0 -0
  60. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/daemon_launch.py +0 -0
  61. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/debt.py +0 -0
  62. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/dependency_install.py +0 -0
  63. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/detach.py +0 -0
  64. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/doctor.py +0 -0
  65. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/engine_identity.py +0 -0
  66. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/export/__init__.py +0 -0
  67. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/export/html.py +0 -0
  68. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/export/json.py +0 -0
  69. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/export/md.py +0 -0
  70. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/extract/__init__.py +0 -0
  71. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/extract/ast.py +0 -0
  72. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/freshness.py +0 -0
  73. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/git.py +0 -0
  74. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/graph_io.py +0 -0
  75. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/health.py +0 -0
  76. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/hook_entry.py +0 -0
  77. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/hookinstall.py +0 -0
  78. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/hookshim.py +0 -0
  79. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/incident_ledger.py +0 -0
  80. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/ingest.py +0 -0
  81. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/init.py +0 -0
  82. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/io.py +0 -0
  83. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/launchd_agent.py +0 -0
  84. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/listing.py +0 -0
  85. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/llm.py +0 -0
  86. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/llm_probe.py +0 -0
  87. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/mcp.py +0 -0
  88. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/natural_query.py +0 -0
  89. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/overlays.py +0 -0
  90. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/probe_workspace.py +0 -0
  91. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/process_contracts.py +0 -0
  92. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/provider_observer.py +0 -0
  93. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/py.typed +0 -0
  94. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/query.py +0 -0
  95. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/query_plan.py +0 -0
  96. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/resolve.py +0 -0
  97. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/review.py +0 -0
  98. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/__init__.py +0 -0
  99. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/approval.py +0 -0
  100. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/classifier.py +0 -0
  101. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/claude_executor.py +0 -0
  102. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/claude_probe.py +0 -0
  103. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/cli_identity.py +0 -0
  104. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/codex_executor.py +0 -0
  105. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/codex_probe.py +0 -0
  106. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/context_builder.py +0 -0
  107. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/contracts.py +0 -0
  108. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/diff_policy.py +0 -0
  109. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/edit_apply.py +0 -0
  110. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/effort.py +0 -0
  111. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/lifecycle.py +0 -0
  112. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/lifecycle_operator.py +0 -0
  113. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/lifecycle_service.py +0 -0
  114. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/lifecycle_storage.py +0 -0
  115. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/ollama_executor.py +0 -0
  116. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/ollama_probe.py +0 -0
  117. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/openrouter_executor.py +0 -0
  118. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/openrouter_probe.py +0 -0
  119. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/policy.py +0 -0
  120. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/probe_runner.py +0 -0
  121. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/process_runner.py +0 -0
  122. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/profiles.py +0 -0
  123. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/prompt.py +0 -0
  124. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/registry.py +0 -0
  125. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/route_pool.py +0 -0
  126. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/route_pool_execution.py +0 -0
  127. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/schema_validation.py +0 -0
  128. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/service.py +0 -0
  129. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/settings.py +0 -0
  130. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/shadow.py +0 -0
  131. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/storage.py +0 -0
  132. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/telemetry.py +0 -0
  133. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/worktree.py +0 -0
  134. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/zai_edit.py +0 -0
  135. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/zai_executor.py +0 -0
  136. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/routing/zai_probe.py +0 -0
  137. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/savings.py +0 -0
  138. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/systemd_unit.py +0 -0
  139. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/ts_bridge.py +0 -0
  140. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/ts_resolver.mjs +0 -0
  141. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/typescript_activation.py +0 -0
  142. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/usage_ledger.py +0 -0
  143. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/validation.py +0 -0
  144. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/watch.py +0 -0
  145. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/windows_job.py +0 -0
  146. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/windows_startup.py +0 -0
  147. {graphite_code-1.0.0 → graphite_code-1.1.0}/src/graphite/windows_task.py +0 -0
  148. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/conftest.py +0 -0
  149. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/data/sqloracle/blindspots.py.txt +0 -0
  150. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/data/sqloracle/round69.py.txt +0 -0
  151. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/fake_clis/fake_cli.py +0 -0
  152. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/fixtures/provider_lifecycle_schema_v1.sql +0 -0
  153. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/fixtures/routing_schema_v2_ca77600.sql +0 -0
  154. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/fixtures/routing_schema_v3_94eb333.sql +0 -0
  155. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/fixtures/routing_schema_v4_lifecycle_migration.sql +0 -0
  156. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_activation.py +0 -0
  157. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_activation_backstop.py +0 -0
  158. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_agent_hook_activation.py +0 -0
  159. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_agent_hooks.py +0 -0
  160. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_agent_settings.py +0 -0
  161. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_answer_contract.py +0 -0
  162. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_arrow_and_constructor_binding.py +0 -0
  163. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_atomic_write.py +0 -0
  164. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_bootstrap.py +0 -0
  165. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_build_detach.py +0 -0
  166. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_buildlock.py +0 -0
  167. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_cache_engine_identity.py +0 -0
  168. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_cache_file_set.py +0 -0
  169. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_cache_pruning.py +0 -0
  170. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_call_graph.py +0 -0
  171. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_channel_cli.py +0 -0
  172. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_channel_core.py +0 -0
  173. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_channel_lock.py +0 -0
  174. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_channel_mcp.py +0 -0
  175. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_channel_register.py +0 -0
  176. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_channel_report.py +0 -0
  177. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_channel_status.py +0 -0
  178. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_class_field_arrow_binding.py +0 -0
  179. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_cli_channel.py +0 -0
  180. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_cli_version.py +0 -0
  181. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_compare_dist.py +0 -0
  182. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_configuration_reference.py +0 -0
  183. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_context.py +0 -0
  184. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_cwd_relative_config.py +0 -0
  185. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_cycle_search.py +0 -0
  186. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_daemon.py +0 -0
  187. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_daemon_activation.py +0 -0
  188. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_daemon_build_lock.py +0 -0
  189. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_daemon_child_lock.py +0 -0
  190. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_daemon_engine_staleness.py +0 -0
  191. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_daemon_health.py +0 -0
  192. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_daemon_health_activation.py +0 -0
  193. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_daemon_launch.py +0 -0
  194. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_daemon_service_cli.py +0 -0
  195. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_debt.py +0 -0
  196. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_destructured_binding.py +0 -0
  197. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_determinism.py +0 -0
  198. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_dispatch_evidence.py +0 -0
  199. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_doctor_agent_hooks.py +0 -0
  200. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_doctor_hooks.py +0 -0
  201. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_doctor_managed_docs.py +0 -0
  202. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_edit_apply.py +0 -0
  203. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_engine_identity.py +0 -0
  204. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_exit_codes_reference.py +0 -0
  205. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_external_calls.py +0 -0
  206. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_git_security.py +0 -0
  207. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_go_rust.py +0 -0
  208. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_graph_io.py +0 -0
  209. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_graph_provider_isolation.py +0 -0
  210. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_hardening.py +0 -0
  211. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_health.py +0 -0
  212. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_hook_entry.py +0 -0
  213. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_hook_template.py +0 -0
  214. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_hookinstall.py +0 -0
  215. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_hookshim.py +0 -0
  216. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_html_security.py +0 -0
  217. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_incident_ledger.py +0 -0
  218. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_init.py +0 -0
  219. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_init_activation_doctrine.py +0 -0
  220. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_init_hooks.py +0 -0
  221. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_init_validation_exit.py +0 -0
  222. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_launchd_agent.py +0 -0
  223. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_lifecycle_operator.py +0 -0
  224. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_listing.py +0 -0
  225. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_listing_surfaces.py +0 -0
  226. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_llm.py +0 -0
  227. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_mcp.py +0 -0
  228. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_method_dispatch_scope.py +0 -0
  229. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_monorepo.py +0 -0
  230. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_natural_query.py +0 -0
  231. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_node_identity.py +0 -0
  232. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_overlays.py +0 -0
  233. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_path_leak_fixture.py +0 -0
  234. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_probe_diagnostics.py +0 -0
  235. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_probe_workspace.py +0 -0
  236. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_provider_claude_probe.py +0 -0
  237. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_provider_codex_probe.py +0 -0
  238. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_provider_lifecycle.py +0 -0
  239. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_provider_lifecycle_service.py +0 -0
  240. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_provider_lifecycle_storage.py +0 -0
  241. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_provider_observer.py +0 -0
  242. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_provider_ollama_probe.py +0 -0
  243. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_provider_openrouter_probe.py +0 -0
  244. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_provider_probe_runner.py +0 -0
  245. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_public_surface.py +0 -0
  246. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_published_schemas.py +0 -0
  247. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_python_resolver.py +0 -0
  248. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_query_plan.py +0 -0
  249. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_reliability.py +0 -0
  250. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_resolve.py +0 -0
  251. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_review.py +0 -0
  252. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_route_pool.py +0 -0
  253. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_approval.py +0 -0
  254. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_benchmark.py +0 -0
  255. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_classifier.py +0 -0
  256. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_claude_executor.py +0 -0
  257. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_cli.py +0 -0
  258. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_cli_contracts.py +0 -0
  259. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_cli_recovery.py +0 -0
  260. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_codex_executor.py +0 -0
  261. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_context.py +0 -0
  262. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_contracts.py +0 -0
  263. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_diff_policy.py +0 -0
  264. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_executor.py +0 -0
  265. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_openrouter_executor.py +0 -0
  266. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_policy.py +0 -0
  267. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_process_runner.py +0 -0
  268. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_profiles.py +0 -0
  269. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_registry.py +0 -0
  270. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_schema_validation.py +0 -0
  271. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_security.py +0 -0
  272. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_service.py +0 -0
  273. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_shadow.py +0 -0
  274. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_storage.py +0 -0
  275. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_telemetry.py +0 -0
  276. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_worktree.py +0 -0
  277. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_zai_executor.py +0 -0
  278. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_routing_zai_probe.py +0 -0
  279. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_savings.py +0 -0
  280. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_search.py +0 -0
  281. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_smoke.py +0 -0
  282. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_sqloracle.py +0 -0
  283. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_systemd_unit.py +0 -0
  284. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_typescript_resolver.py +0 -0
  285. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_usage_ledger.py +0 -0
  286. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_verify_artifact.py +0 -0
  287. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_watch.py +0 -0
  288. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_windows_startup.py +0 -0
  289. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_windows_task.py +0 -0
  290. {graphite_code-1.0.0 → graphite_code-1.1.0}/tests/test_zai_edit.py +0 -0
@@ -24,10 +24,10 @@ The implementation stages are:
24
24
  1. **Collection — `ingest.py`, `config.py`, `git.py`.** Configuration establishes file-count and file-size limits and the cache and output locations. Ingestion normalizes project-relative paths, resolves candidates beneath the repository root, skips unsafe or ineligible files, and sorts accepted entries. Git-backed enumeration uses `GitRunner` with a trusted executable outside the repository, an argument vector, no shell, a filtered Git environment, a timeout, and a stdout cap. The filesystem fallback is also capped by configured file limits, but it does not provide every process control because no child process is involved.
25
25
  2. **Extraction — `extract/ast.py`, `cache.py`, `ts_bridge.py`, `ts_resolver.mjs`.** Per-language Tree-sitter extractors handle JavaScript/TypeScript, Python, Go, and Rust, while unsupported or unavailable parsers degrade to a file-level record. Extraction emits symbols, imports, calls, and containment edges in deterministic order. The content-addressed cache avoids repeated AST work. TypeScript/JavaScript may additionally use the Node compiler bridge for compiler-backed module resolution; bridge failure falls back to heuristic resolution rather than making Node mandatory.
26
26
  3. **Resolution — `resolve.py`.** `SourceIndex` builds the set of normalized, project-relative source identities, reads TypeScript aliases and workspace package entry points, prefers compiler-backed TypeScript results when available, and otherwise applies bounded heuristics. Global merge logic also resolves supported call identities and deterministically removes duplicates.
27
- 4. **Graph and analysis — `graph.py`, `cluster.py`, `analyze.py`, `query.py`.** `graph.py` constructs a NetworkX `DiGraph` after sorting nodes and edges and normalizing IDs. Analysis filters to project nodes where appropriate and returns bounded, ordered results. Community detection constructs a deterministic undirected view and uses the configured seed (42 by default). JSON conversion preserves the graph's deterministic insertion order.
27
+ 4. **Graph and analysis — `graph.py`, `cluster.py`, `analyze.py`, `query.py`.** `graph.py` constructs a NetworkX `DiGraph` after sorting nodes and edges and normalizing IDs. Analysis filters to project nodes where appropriate and returns bounded, ordered results; the cycle report is a bounded search (per strongly connected component, one length level at a time up to length 8, under a 10 000-cycle budget) and `analysis.cycle_search` records whether the enumeration completed exactly or was truncated, so a repository with dense import cycles cannot make a build exhaust memory. Community detection constructs a deterministic undirected view and uses the configured seed (42 by default). JSON conversion preserves the graph's deterministic insertion order.
28
28
  5. **Validation and review — `validation.py`, `context.py`, `review.py`.** Bundle validation checks structural types, node and edge identity, referential integrity, metadata counts, and unsafe `source_file` paths. Review validates a supplied bundle before deriving graph impact evidence; its Git discovery also normalizes status records and rejects malformed or unsafe paths. By contrast, `cmd_query`, `cmd_impact`, `cmd_context`, and `GraphiteMCPServer._load` currently read JSON and call `graph_from_json` without `validate_graph_bundle` or a bounded-read helper. Those direct consumers therefore rely on callers to supply an already validated, reasonably sized artifact.
29
29
  6. **Export — `export/json.py`, `export/md.py`, `export/html.py`, `io.py`.** Exporters consume only canonical graph, cluster, analysis, and manifest data. JSON serialization provides the data encoding; HTML separately JSON-encodes script data, escapes `<`, `>`, and `&`, HTML-escapes the title, and uses text DOM APIs for runtime labels. Text and JSON outputs use temporary files, `fsync`, and `os.replace` for atomic replacement of each file.
30
- 7. **Operations — `watch.py`, `daemon.py`, `daemon_health.py`, `bootstrap.py`, `init.py`, `windows_task.py`, `windows_startup.py`.** These modules watch for changes, maintain multi-project daemon status, evaluate health, generate integration instructions, and manage platform startup. Watch and daemon canonicalize inherited configuration; daemon child builds use fixed `--llm none` argv and a provider-scrubbed environment.
30
+ 7. **Operations — `watch.py`, `daemon.py`, `daemon_health.py`, `bootstrap.py`, `init.py`, `daemon_launch.py`, `windows_task.py`, `windows_startup.py`, `systemd_unit.py`, `launchd_agent.py`.** These modules watch for changes, maintain multi-project daemon status, evaluate health, generate integration instructions, and manage platform supervision. `daemon_launch.py` holds the one daemon argument vector (`python -P -m graphite daemon <base> …`); the Windows scheduled task, the Windows Startup-folder launcher, the systemd user unit and the launchd agent are each rendered from it, so every supervisor starts the same bounded, zero-LLM daemon under `-P`. Watch and daemon canonicalize inherited configuration; daemon child builds use fixed `--llm none` argv and a provider-scrubbed environment.
31
31
  8. **Provider lifecycle — `routing/lifecycle.py`, `routing/lifecycle_storage.py`, `routing/lifecycle_service.py`, and `routing/lifecycle_operator.py`.** Runtime observations, compatibility decisions, lifecycle authority, and invalidations live in an isolated database. Operator reads use an existing-database, query-only connection and bounded public records. Policy and verification preparation create non-activating content-hashed candidates; neither surface invokes a provider.
32
32
  9. **Optional overlays — `overlays.py` and `llm.py`.** Provider adapters may consume an already-built, fresh, validated graph only through `graphite overlay build`. The overlay manifest binds the canonical bundle fingerprint, exact lifecycle/model/routing identities, limits, creation time, outcome, and schema. Identity-derived contained paths, restrictive permissions, content-addressed payloads, and manifest-last atomic replacement isolate the output below `graph-out/overlays/`. Overlay staleness is independent of canonical freshness and cannot grant graph or routing authority.
33
33
 
@@ -42,9 +42,10 @@ The implementation stages are:
42
42
  | Validation and evidence | `validation.py`, `context.py`, `review.py` | Validate bundles and derive review/context evidence | Graph structures; review may use the Git adapter |
43
43
  | Export | `export/json.py`, `export/md.py`, `export/html.py`, `io.py` | Encode and atomically replace individual artifacts | Validated graph, analysis, clusters, manifest |
44
44
  | Optional model overlays | `overlays.py`, `llm.py` | Validate identity and canonical freshness; produce fingerprint-bound, non-authoritative annotations outside canonical artifacts | Existing canonical graph, explicit provider configuration, network only when separately authorized |
45
- | Operations and integration | `watch.py`, `daemon.py`, `daemon_health.py`, `bootstrap.py`, `init.py`, `windows_task.py`, `windows_startup.py` | Freshness, daemon health, generated instructions, and OS startup | Public core operations and explicit platform/process boundaries |
45
+ | Operations and integration | `watch.py`, `daemon.py`, `daemon_health.py`, `bootstrap.py`, `init.py`, `daemon_launch.py`, `windows_task.py`, `windows_startup.py`, `systemd_unit.py`, `launchd_agent.py` | Freshness, daemon health, generated instructions, and OS supervision on Windows, Linux and macOS | Public core operations and explicit platform/process boundaries |
46
+ | Platform shims | `_win32_ctypes.py`, `_cleanup_worker.py` | Typed call-time views of platform-only stdlib surfaces (Win32 ctypes, POSIX-only `os` members) so the portable core type-checks on every OS without `sys.platform` guards | Standard library only |
46
47
 
47
- The intended dependency direction is observable in current imports but is not enforced by a dedicated architecture linter. CLI and MCP adapt inputs; portable core modules do not import those UI entry points. Export consumes canonical graph and analysis data; extraction does not depend on exporters. Canonical graph construction, validation, and read operations do not depend on model providers. Windows startup and task management remain outside the portable core. External execution crosses explicit subprocess boundaries; Git uses the hardened `GitRunner`, while the TypeScript and Windows adapters have their own, not necessarily identical, controls. Contributors must preserve these directions and add tests if a boundary becomes mechanically important.
48
+ The intended dependency direction is observable in current imports but is not enforced by a dedicated architecture linter. CLI and MCP adapt inputs; portable core modules do not import those UI entry points. Export consumes canonical graph and analysis data; extraction does not depend on exporters. Canonical graph construction, validation, and read operations do not depend on model providers. Platform supervision — the Windows scheduled task and Startup launcher, the systemd user unit, the launchd agent — remains outside the portable core. External execution crosses explicit subprocess boundaries; Git uses the hardened `GitRunner`, while the TypeScript and platform-supervisor adapters have their own, not necessarily identical, controls. Contributors must preserve these directions and add tests if a boundary becomes mechanically important.
48
49
 
49
50
  ## Trust boundaries
50
51
 
@@ -86,7 +87,7 @@ Treat paths, bytes, symlinks, encodings, file counts, and file sizes as hostile.
86
87
 
87
88
  **Process boundary.**
88
89
 
89
- Git, Node/TypeScript, and Windows task/startup integrations execute outside Python. `GitRunner` is the strongest current adapter: fixed executable discovery outside the repository, argv execution with `shell=False`, disabled stdin/stderr, filtered `GIT_*` environment, timeout, stdout cap, and typed sanitized failures. The TypeScript bridge uses argv, `subprocess.run`, captured output, a configured timeout, and a 500-character diagnostic cap, but it currently inherits the environment and does not impose an explicit output-byte cap. Windows adapters are platform-specific and must be reviewed on their own controls. Do not generalize Git's isolation guarantees to every subprocess.
90
+ Git, Node/TypeScript, and the platform supervisors (`schtasks`, `systemctl --user`, `launchctl`) execute outside Python. `GitRunner` is the strongest current adapter: fixed executable discovery outside the repository, argv execution with `shell=False`, disabled stdin/stderr, filtered `GIT_*` environment, timeout, stdout cap, and typed sanitized failures. The TypeScript bridge uses argv, `subprocess.run`, captured output, a configured timeout, and a 500-character diagnostic cap, but it currently inherits the environment and does not impose an explicit output-byte cap. The supervisor adapters are platform-specific, take an injected runner in tests, and must be reviewed on their own controls. Do not generalize Git's isolation guarantees to every subprocess.
90
91
 
91
92
  **Artifact and browser boundary.**
92
93
 
@@ -13,6 +13,315 @@ machine-checkable identity; the version is for humans.
13
13
 
14
14
  [Keep a Changelog]: https://keepachangelog.com/en/1.1.0/
15
15
 
16
+ ## [Unreleased]
17
+
18
+ ## [1.1.0] — 2026-09-27
19
+
20
+ A minor release. The agent channel's read surfaces gain fields, which is an
21
+ addition under `docs/compatibility.md`. The channel's audit trail also
22
+ closes three holes: status events taken on trust, deletions nobody saw, and
23
+ an audit gate that could fail open. The graph itself does not change. On
24
+ this repository, measured against the deployed 1.0.1 engine, 7659 of
25
+ 7659 node ids survive, 21788 of 21788 edge triples are unchanged, every
26
+ relation count is equal, and `analysis.cycles` is identical. Only the
27
+ engine fingerprint moves, because graphite's own bytes did.
28
+
29
+ ### Added
30
+
31
+ **Every channel status now says who recorded it, and where each recipient
32
+ stands.** `graphite_channel_list`, `graphite_channel_read` and `graphite
33
+ channel list --json` carry `status_actor`, `status_at` and
34
+ `status_verification` beside `status`, plus `recipients` (each recipient's
35
+ own latest status and its verification, or `null`) and `unreceipted`
36
+ (recipients with no event of any kind). `status` keeps its
37
+ meaning: the newest event by any agent. `graphite_channel_read` also returns
38
+ `history`, where every event carries its actor, time, commit and
39
+ verification grade. `graphite channel show N` prints the same history after
40
+ the body. Measured on the live channel on 2026-09-25: the one folded word
41
+ misstated at least one recipient in 39 of the 212 rounds that carry events.
42
+ Round 256 read `acknowledged` while one of its two recipients had recorded
43
+ nothing. The read tool's description now says that reading records nothing,
44
+ and that `graphite_channel_inbox` registers receipt.
45
+
46
+ ### Fixed
47
+
48
+ **The channel report now verifies status events against their commits, as it
49
+ already did for rounds.** A status event's `actor` was taken on trust. An
50
+ event written by hand under another agent's trailer, rewritten after its
51
+ commit, or edited and left uncommitted was invisible, because the forged
52
+ actor was a participant and the file was tracked. Each event is now graded
53
+ from one `git log` over `status/`. It is `verified` only when it has exactly
54
+ one commit, a clean working copy, and a trailer naming its actor.
55
+ `status_modified`, `status_discrepancy` and `status_uncommitted` are
56
+ anomalies and fail `ok`. On the live channel all 408 events verified, with
57
+ a planted-forgery control flagged. A comment had claimed this check existed.
58
+ The read surfaces carry the same grade, so an actor is never shown bare:
59
+ `status_verification` sits beside `status_actor`, each recipient's entry has
60
+ its own `verification`, and the human report marks a failing attribution
61
+ `UNVERIFIED`. This closes aramid llm-review finding `53368ee`.
62
+ One trust limit remains, and it is stated here because it bounds what
63
+ "verified" means. Every agent commits under the operator's git identity, so
64
+ a trailer proves which agent label was attached, not which process
65
+ committed. A hand-made commit under the claimed actor's own trailer still
66
+ grades `verified`. Closing that needs signed commits.
67
+
68
+ **The channel report now sees deletions, and a status event's number is
69
+ never reused.** The report graded only the events and rounds still on disk,
70
+ and a new event took `len(files) + 1`. An agent could therefore delete its
71
+ own `blocked` event, commit the deletion, write a `done` into the same slot,
72
+ and the report still read OK. A round file removed the same way vanished
73
+ from the audit view entirely. The next number is now one past the highest
74
+ the round has ever used, on disk or in history, and a history git cannot
75
+ read refuses the write instead of passing for an empty one. Any status file
76
+ or round that history or the index knows but the working tree lacks is
77
+ reported as `status_deleted` or `round_deleted`, and a hole in a round's
78
+ 1..N sequence is reported as `status_seq_gap`. All three fail `ok`. The live channel's history
79
+ holds no deletions or renames, so none fire there. This closes aramid
80
+ llm-review finding `5db5889`.
81
+
82
+ **Installing the channel's audit gate now fails closed.**
83
+ `ensure_channel_hook` set `core.hooksPath` with `check=False` and always
84
+ reported `changed: True`. A gate git never armed (a locked `.git/config`, or a
85
+ higher-precedence override) was therefore reported as in place, and later
86
+ commits would have passed with no agent trailer. The write now raises on
87
+ failure. The value git will actually use is read back, and anything other
88
+ than `.githooks` raises `hook_not_armed`. `changed` reports the observed
89
+ before-and-after state. This closes aramid llm-review finding `0e87805`.
90
+
91
+ **Stalling is judged per recipient.** A co-recipient's `done` no longer
92
+ hides a recipient that was handed the round and never followed up. A
93
+ withdrawn or superseded round never stalls. Rows gain `stalled_recipients`.
94
+ The report lists recipients with no receipt per agent under `unreceipted`.
95
+ They are visible but do not fail `ok`, because several registered agents
96
+ have never called inbox, and a check that is always red is one nobody
97
+ reads.
98
+
99
+ **Reading the channel no longer rewrites its git index.** The report's
100
+ `git status` refreshed stale stat data by rewriting `.git/index` under
101
+ `index.lock`, the lock a concurrent broker `git add` needs. Channel git now
102
+ runs with `GIT_OPTIONAL_LOCKS=0`. A test pins the index bytes across a
103
+ report and a read with stale stat data.
104
+
105
+ **A PEP 508 marker of the shape `(A) or (B)` was mis-parsed by the doctor's
106
+ MCP distribution walk.** The marker evaluator unwrapped outer parentheses by
107
+ checking only that the text started with `(` and ended with `)`, so
108
+ `(extra == "a") or (python_version == "3.14")` was stripped to
109
+ `extra == "a") or (python_version == "3.14"`. An `extra` marker of that shape
110
+ then failed closed to "does not apply"; a plain marker of that shape raised
111
+ and aborted the whole distribution walk, which is `probe_mcp`'s manifest
112
+ source. Outer parentheses are now unwrapped only when the first `(` is closed
113
+ by the last character, counting depth outside quoted values. Found by the
114
+ new `tests/test_doctor_probes.py`; no installed distribution on the reference
115
+ machine carried the shape, which is why the walk had never failed.
116
+
117
+ ### Changed
118
+
119
+ **`scripts/mutation_tests.py` targets `scripts/` changes too.** The
120
+ certifying launcher for aramid's mutation gate diffed `src/` only, so a
121
+ mutant in the launcher itself skipped the targeted stage and its confirm ran
122
+ the whole suite (465 s on the gate's first live drain, 2026-09-04, where the
123
+ targeted stage kills in seconds). A changed `scripts/<stem>.py` now targets
124
+ `tests/test_<stem>.py` and `tests/test_<stem>_*.py` like a changed source
125
+ module.
126
+
127
+ **Twelve modules gained a `tests/test_<stem>.py`.** aramid's mutation stage 1
128
+ runs `tests/test_<module>.py` when it exists and otherwise `pytest -k
129
+ <module>`; a run that selects nothing is booked a stage-1 survivor and every
130
+ such mutant goes to a full-suite confirm, of which an item gets three. Nine
131
+ modules had a `-k` fallback that collected zero tests (`doctor_probes`,
132
+ `dependency_install`, `probe_process`, `ts_bridge`, `_win32_ctypes`,
133
+ `process_contracts`, `routing/ollama_executor`, `routing/context_builder`,
134
+ `routing/route_pool_execution`) and three collected one (`mcp_server`,
135
+ `llm_probe`, `cluster`). Each now has a stage-1 file that pins its pure
136
+ helpers directly; every file was proven able to fail by a hand mutant of its
137
+ module. Three quirks were pinned as-is rather than changed: a cluster's
138
+ kind label is `f"{kind}s"` verbatim (`classs`), a blank path in a TypeScript
139
+ bridge edge normalises to `.` instead of being dropped, and an empty MCP
140
+ `node_id` fuzzy-matches the first node.
141
+
142
+ ## [1.0.1] — 2026-09-05
143
+
144
+ A patch release. Measured against the deployed 1.0.0 engine on this repository,
145
+ the tree produces the same graph: 7260 of 7260 node ids survive, 20700 of 20700
146
+ edge triples unchanged, every relation count equal, `analysis.cycles` identical.
147
+ Only the engine fingerprint moves, because graphite's own bytes did.
148
+
149
+ ### Changed
150
+
151
+ **Vendor-neutral wording throughout the replacement audit and docs.** The
152
+ README opening, the `audit-replacement` help and reference text,
153
+ `skill/SKILL.md` and the audit's own messages no longer name the legacy
154
+ graph tool, and the README's opening comparative against it — an unmeasured
155
+ claim dating to the initial commit (`fd58583`) — is removed rather than
156
+ defended. In the audit's report, which is not a `schema_version`-stable
157
+ surface, the `graphify` block is renamed `legacy` and the warning codes
158
+ `graphify_paths_exist`, `graphify_text_references` and
159
+ `graphify_gitignore_entries` become `legacy_paths_exist`,
160
+ `legacy_text_references` and `legacy_gitignore_entries`. Detection is
161
+ unchanged: the on-disk path patterns and the text-scan needle still match
162
+ the legacy tool's literal artifact names — the one place the name must
163
+ remain for the audit to find anything.
164
+
165
+ ### Fixed
166
+
167
+ **The extraction cache grew without bound inside a partition (#66).** #23
168
+ reclaims whole partitions a build can never read again and left the one it
169
+ uses alone. Entries are keyed on the file's content hash, so every edit that
170
+ triggers a rebuild wrote a new entry and orphaned the previous one, and
171
+ nothing ever removed an orphan: a consumer checkout rebuilt by the daemon on
172
+ every change reached 15,406 entries / 419 MB in a single partition, 11,592 of
173
+ them from four days, against 1,780 / 62 MB for graphite's own tree on the
174
+ same engine. Same rule as #23, one level down -- reachability, not age or
175
+ size: a `Cache` now records every key it read (and found) or wrote, and after
176
+ a build has extracted the tree and built its graph, `prune_unreachable_entries`
177
+ deletes every other entry in the partition, reporting
178
+ `[graphite] reclaimed N unreachable cache entries (X MB)` when it removed any.
179
+ Older content checked out later re-extracts once (performance, never
180
+ correctness). Safe without the build lock: `read` already treats a vanished
181
+ file as a miss, so a concurrent reader re-extracts rather than fails. A build
182
+ that touched nothing prunes nothing, only the exact `<2 hex>/<64 hex>.json`
183
+ shape the cache writes is eligible, and a per-entry failure (a Windows handle
184
+ race) is swallowed.
185
+
186
+ **The deferred stdin close never fired on Linux or macOS.** `run_bounded_process`
187
+ reads the child's pipes with a fixed-size read, and its two transports hand
188
+ that reader different objects: on Windows a raw `io.FileIO`, whose `read(n)`
189
+ returns whatever the child has written, and on POSIX a default `Popen` pipe,
190
+ a `BufferedReader` whose `read(n)` keeps reading until it has n bytes or EOF.
191
+ Off Windows the reader therefore saw nothing until the child exited, the
192
+ `stdin_close_when` predicate never saw its marker, and every deferred close
193
+ was the budget fallback -- so the #29 fix was inert there and the doctor's MCP
194
+ deep probe spent its whole budget on every POSIX run. The reader now uses
195
+ `read1` on buffered pipes; the Windows path is unchanged. The tests that
196
+ pinned the deferral could not fail (one matched a marker that text-mode
197
+ stdout writes as CRLF on Windows, the other's lower bound was satisfied by
198
+ the child's own sleep) and now kill a never-defer mutant; the interval test
199
+ that blocked the pre-push gate measures from the close instead of from
200
+ launch, against the child's own life rather than a 0.25s constant.
201
+
202
+ **The post-publication verifier's provenance arm asked an endpoint that
203
+ never carries provenance.** `scripts/verify_published_release.py` read
204
+ `urls[].provenance` from PyPI's legacy `/pypi/<project>/<version>/json`
205
+ API, which has no such key, so the arm reported "no provenance" for 1.0.0
206
+ while the Integrity API was already serving both GitHub attestation bundles
207
+ (`jared0565/graphite`, `publish.yml`). The arm now reads the PEP 691 Simple
208
+ JSON index (`Accept: application/vnd.pypi.simple.v1+json`), where PEP 740
209
+ publishes a `provenance` URL per file; a test pins the endpoint so a
210
+ regression to the legacy API fails. Re-run against 1.0.0: 6 of 6 arms.
211
+
212
+ **Three findings from aramid's `llm-review` tier on the CI workflow and the
213
+ benchmark, all confirmed and closed:**
214
+
215
+ - The `coverage_floor` dispatch input could *lower* the floor for one run,
216
+ so a dispatched run could report green against a weaker gate than the
217
+ committed one. The enforcing step now refuses any requested value that is
218
+ not an integer at or above the committed `COVERAGE_FLOOR`; a dispatch can
219
+ only raise it (the negative control is unchanged). Proven with a fake
220
+ coverage across six arms: empty, 84 and 83 pass; 80, `abc` and -5 fail
221
+ before coverage is read.
222
+ - `test_core_probe_cleanup_timeout_uses_single_global_slot_and_recovers`
223
+ raced its 0.5 s probe budget against runner load (#65): more than 0.4 s of
224
+ scheduling before the workspace lease is acquired yields a pure `timeout`,
225
+ after it a `cleanup_timeout` carrying `masked_*` keys — two CI failures in
226
+ three runs on one day, both shapes reproduced with a paced fake clock. The
227
+ test now freezes its injected `_clock` like the sibling probe tests (the
228
+ subject is the classification of a hung cleanup, not phase pacing; the
229
+ cleanup join still runs on real time), and the frozen-clock test still
230
+ kills both guarding mutants: a hung cleanup reported as `None`, and a
231
+ probe slot that never releases.
232
+ - The `security` job's self-check asserted only that gitleaks and semgrep
233
+ ran, while the step's name claimed typecheck and a dependency audit. The
234
+ labels were measured from the job's own `prepush.json` artifact
235
+ (`gitleaks, mypy, python.exe, semgrep, shadow`): the assertion now covers
236
+ every configured scanner, and the step name, `SECURITY.md` and
237
+ `CONTRIBUTING.md` no longer claim a dependency audit — no gate run on
238
+ either machine has ever emitted one, although `aramid doctor` lists
239
+ pip-audit as present (reported to aramid). Measuring this exposed two
240
+ worse things: under aramid 0.6.0 the CI typecheck slot had **never run**
241
+ in `--all` mode (no `mypy` in `tools_ran`, nothing degraded) and the old
242
+ assertion called that clean; under 0.6.1 it runs over every `.py`, and
243
+ the job passed with exit 0 while its own report carried 786 block-tier
244
+ findings that the same report fails with on the maintainer's machine
245
+ (exit 1). Discriminated on the first run under the 0.7.0 pin (33286250634):
246
+ still 786 block-tier findings and exit 0 with CI's empty ledger, so
247
+ aramid's exit depends on ledger presence — `.aramid/` is gitignored, so
248
+ a fresh checkout cannot be gated by exit status at all. aramid confirmed
249
+ the mechanism from its code (channel round 151): its **fresh-ledger
250
+ rule** — the first pre-push run on a ledger with no baseline writes one
251
+ and downgrades findings the ratchet alone escalated to exit 0, so every
252
+ CI checkout is "the first run" — and from 0.7.1 the report says so
253
+ (`fresh_ledger_baseline`, `grandfathered`; both gate steps print them).
254
+ Both CI gate steps now
255
+ read the verdict from the report itself: any block-tier finding from a
256
+ non-mypy scanner, or from mypy inside the project's type-gate scope
257
+ (`src/graphite`), fails the step regardless of the exit status; mypy
258
+ findings on the deliberately untyped `tests/`, `scripts/` and
259
+ `benchmarks/` trees are counted and printed.
260
+ - `benchmarks/build_benchmark.py` prepended the repository root to
261
+ `sys.path` to import its sibling — the shape the `-P` launch contract
262
+ forbids. It loads `synthetic_repo.py` by location now, CI runs it under
263
+ `-P`, and two tests pin the shape (no `sys.path` access in the module; the
264
+ script starts under `-P`).
265
+
266
+ ### Changed
267
+
268
+ - CI pins `aramid==0.7.2`, the tool this machine runs (channel round 157;
269
+ 0.7.0 and 0.7.1 were pinned briefly at rounds 148 and 152). 0.7.2 fixes
270
+ the tests-slot skip-streak false alarm this repository reported — the
271
+ registry key `tests` rode into `run_started.expected` beside the slot's
272
+ label, so `status` counted every pre-push run as a skipped suite (channel
273
+ rounds 155–157) — and stamps `run_id`/`recorded` in the check report, so
274
+ a `--no-record` measurement is distinguishable from a recorded one.
275
+ 0.7.1's typecheck slot honours
276
+ `[tool.mypy] files`, so the 683 mypy rows that a whole-tree measurement
277
+ under 0.7.0 had written into the maintainer's ledger for `tests/` (675)
278
+ and `scripts/` (8) are retired with `ledger resolve --out-of-scope`, each
279
+ carrying the reason.
280
+ The two `mypy:syntax` ledger rows that 0.6.1 left unresolvable were NOT
281
+ retired with `ledger resolve --out-of-scope` as first written here: it
282
+ refused them (`is not open (status=fixed)`), because aramid's gate had
283
+ already marked them `fixed` on a push that ran mypy over other files
284
+ without opening `ci.yml` — a defect in aramid 0.6.1's examined-set
285
+ stamping, fixed on aramid's `main` (`e65f296`) for 0.7.1. The rows stay
286
+ `fixed`, the log being append-only; channel round 151 is their record.
287
+ - mypy's configuration is back under `[tool.mypy]` in `pyproject.toml` and
288
+ `setup.cfg` is gone. It had moved there only because aramid 0.6.0's
289
+ typecheck runner armed on `[tool.mypy]` and then fed every changed file to
290
+ mypy; aramid 0.6.1 hands mypy only `.py`/`.pyi` (channel round 144), so
291
+ the CI pin is now `aramid==0.6.1` and the two `mypy:syntax` suppression
292
+ entries that bridged the defect are deleted rather than kept — a
293
+ workaround that outlives its root cause misdirects the next reader.
294
+
295
+ ### Documentation
296
+
297
+ - **The generated CLI reference now renders nested subcommands.**
298
+ `scripts/gen_cli_reference.py` walked only the top level of the parser,
299
+ so the twenty-four commands that live one level down — under `route`,
300
+ `lifecycle`, `channel`, `incidents` and `overlay` — and every option they
301
+ own (`route reconcile --attempt-id`, for one) were absent from
302
+ `docs/reference/cli.md`, and the lockstep test could not see them drift.
303
+ Each nested command now has its own section under its group, and
304
+ `tests/test_cli_reference.py` requires every nested command and every
305
+ option it declares to appear there.
306
+ - **Every CLI argument now declares help text.** Rendering the nested
307
+ commands exposed sixty-two arguments with none — every nested `--json`,
308
+ the `lifecycle policy prepare` and `verification prepare` inputs,
309
+ `route accept|reject|cleanup|review --task-id`, `record-outcome`'s
310
+ flags, `reconcile --attempt-id`, `policy --promote/--rollback`, the
311
+ `incidents ack|resolve` positionals and options — so both `--help` and
312
+ the reference were silent about them. Each now says what it takes and
313
+ what it binds to; a test walks the whole parser and fails on any
314
+ argument without help, so none can be added back.
315
+ - New [user guide](docs/user-guide.md) and [knowledge base](docs/knowledge-base.md).
316
+ - README: installation is `pip install graphite-code` (it had still said
317
+ `git clone` + `pip install -e .`, wrong since the 0.3.0 wheel), the `-P`
318
+ launch rule is explained where the command is introduced, the MCP
319
+ examples launch with `-P`, and a documentation index points at every
320
+ support page. CONTRIBUTING puts the dev environment outside the clone
321
+ and lists what CI enforces. ARCHITECTURE covers the platform
322
+ supervisors and the bounded cycle search. The Claude Code skill is
323
+ machine-neutral and versioned 1.0.0.
324
+
16
325
  ## [1.0.0] — 2026-08-29
17
326
 
18
327
  The production-grade release. Nothing here is a claim CI cannot turn red:
@@ -4,17 +4,19 @@ Contributions should be focused, reviewable changes that preserve Graphite's det
4
4
 
5
5
  ## Development setup
6
6
 
7
- Graphite requires Python 3.11 or newer, Git, and an isolated virtual environment. Clone and enter the repository using paths appropriate for your system:
7
+ Graphite requires Python 3.11–3.14, Git, and an isolated virtual environment **outside the clone**. Clone and enter the repository using paths appropriate for your system, then create the environment beside it rather than inside it:
8
8
 
9
9
  ```bash
10
10
  git clone https://github.com/jared0565/graphite.git
11
11
  cd graphite
12
- python -m venv .venv
12
+ python -m venv ../.venvs/graphite-dev
13
13
  ```
14
14
 
15
+ Two reasons for the location. An environment inside the checkout is read by the MCP trusted-source probe as an import shadow of the repository it was asked to trust, so `doctor --deep` reports it. And the release checks (`aramid.toml`, the CI `security` job) run the test suite through exactly that interpreter, `../.venvs/graphite-dev`, so that the suite tests the editable clone rather than whatever `graphite-code` wheel the machine interpreter has installed — a green run against the wrong distribution proves nothing.
16
+
15
17
  If you contribute from a fork, clone the fork's verified URL instead. Keep URLs and other environment-specific values outside shell metacharacters.
16
18
 
17
- Activate `.venv` using the command for your shell. Before installing already-declared extras, inspect `pyproject.toml` and confirm the exact extra and dependency names. Then install the declared development extra:
19
+ Activate the environment using the command for your shell. Before installing already-declared extras, inspect `pyproject.toml` and confirm the exact extra and dependency names. Then install the declared development extra:
18
20
 
19
21
  ```bash
20
22
  python -m pip install -e ".[dev]"
@@ -87,15 +89,18 @@ Run a focused test while iterating. Replace the placeholder path with the test m
87
89
  python -m pytest tests/test_relevant_area.py -q
88
90
  ```
89
91
 
90
- Before requesting review, run all required checks:
92
+ Before requesting review, run all required checks through the dev environment's interpreter:
91
93
 
92
94
  ```bash
93
95
  python -m ruff check .
94
- python -m pytest -q
96
+ python -m mypy # [tool.mypy] in pyproject.toml; 0 errors is the gate
97
+ python -m pytest -q -rs # -rs: a skip is visible, not a silent pass
95
98
  python -m graphite --help
96
99
  ```
97
100
 
98
- Focused tests shorten the feedback loop; they do not replace the full Ruff, pytest, and CLI smoke checks. Add behavior-level tests for new or changed contracts and ensure failures are meaningful rather than dependent on local machine state.
101
+ Focused tests shorten the feedback loop; they do not replace the full Ruff, mypy, pytest, and CLI smoke checks. Add behavior-level tests for new or changed contracts and ensure failures are meaningful rather than dependent on local machine state.
102
+
103
+ What CI enforces on every push and pull request (`.github/workflows/ci.yml`): a `lint` job (ruff, mypy, the generated CLI reference in sync); a `test` matrix of Windows, Linux and macOS × Python 3.11–3.14 in which **every cell gates**; a `security` job that runs the same aramid pre-commit and pre-push gates as the local hooks (gitleaks, semgrep, ruff security rules, the repo-root shadow check, mypy, the suite) and asserts from the gate's JSON report that each scanner actually ran; the `coverage` floor; a `benchmark` job against the synthetic corpus; and an `artifact` job that builds the distribution twice and requires byte-identical results. The local git hooks are installed by `aramid` from the committed `aramid.toml`; do not push with `--no-verify`.
99
104
 
100
105
  Doctor changes require focused tests for stable doctor JSON, deterministic ordering, redaction, end-to-end deadlines, process cleanup, missing tools, and optional semantics. Cover both successful and adversarial outcomes. Cross-platform behavior must have Windows and POSIX coverage; use narrowly scoped platform skips only when an operating-system primitive truly has no counterpart, and test the portable contract on every platform.
101
106
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: graphite-code
3
- Version: 1.0.0
3
+ Version: 1.1.0
4
4
  Summary: Local-first, zero-LLM knowledge graph extraction for codebases.
5
5
  Project-URL: Homepage, https://github.com/jared0565/graphite
6
6
  Project-URL: Repository, https://github.com/jared0565/graphite
@@ -46,7 +46,22 @@ Description-Content-Type: text/markdown
46
46
 
47
47
  # Graphite
48
48
 
49
- Local-first, deterministic knowledge graph extraction for codebases. A safer, faster, cheaper replacement for `graphify`.
49
+ Local-first, deterministic knowledge graph extraction for codebases — zero-LLM, daemon-maintained, agent-agnostic.
50
+
51
+ **Status: 1.1.0, production/stable.** Published on PyPI as
52
+ [`graphite-code`](https://pypi.org/project/graphite-code/) with PEP 740
53
+ attestations; supported on Windows, Linux and macOS with Python 3.11–3.14
54
+ (every cell gates CI). What 1.x promises — CLI, JSON outputs, `graph.json`,
55
+ configuration, exit codes, the launch contract — is in
56
+ [docs/compatibility.md](docs/compatibility.md).
57
+
58
+ ## Documentation
59
+
60
+ - [User guide](docs/user-guide.md) — install, build the first graph, ask questions, onboard a repository for agents, keep graphs fresh with the daemon, upgrade and verify a release.
61
+ - [Knowledge base](docs/knowledge-base.md) — symptoms, causes and remedies collected from operating graphite; read it before filing an issue.
62
+ - [Agent integration guide](docs/agent-integration.md) — how a coding agent or script drives the query interface and grades its answers.
63
+ - Reference, each page kept in lockstep with the code by a test: [CLI](docs/reference/cli.md), [configuration](docs/reference/configuration.md), [exit codes](docs/reference/exit-codes.md), [compatibility and support](docs/compatibility.md), [benchmarks](docs/benchmarks.md).
64
+ - [Security policy](SECURITY.md) — supported versions and the private reporting path.
50
65
 
51
66
  ## Principles
52
67
 
@@ -68,16 +83,26 @@ Local-first, deterministic knowledge graph extraction for codebases. A safer, fa
68
83
  ## Installation
69
84
 
70
85
  ```bash
71
- git clone https://github.com/jared0565/graphite
72
- cd graphite
73
- pip install -e .
74
- ```
75
-
76
- Requires Python 3.11 or newer. No model SDK or provider credential is required
77
- for canonical graph operation.
78
-
79
- An editable install is what makes `python -m graphite` work from any repository
80
- on the machine, which is how every onboarded project reaches it.
86
+ python -m pip install --user graphite-code
87
+ graphite --version # version, engine fingerprint, cache and schema versions
88
+ ```
89
+
90
+ The distribution is `graphite-code` (PyPI's `graphite` is another project);
91
+ the import package is `graphite` and the console scripts are `graphite` and
92
+ `graphite-mcp`. Requires Python 3.11–3.14. No model SDK or provider
93
+ credential is required for canonical graph operation. Every release is built
94
+ by CI from its tag, published with PEP 740 attestations, and verifiable from
95
+ the index — see "Upgrading and verifying a release" in the
96
+ [user guide](docs/user-guide.md).
97
+
98
+ Installing for one interpreter is what makes `python -P -m graphite` work
99
+ from any repository on the machine, which is how every onboarded project
100
+ reaches it. Keep the `-P`: without it Python puts the current directory
101
+ first on `sys.path`, so a `graphite.py` or `graphite/__init__.py` planted at
102
+ a repository root would be imported instead of the installed package. The
103
+ console scripts are immune by construction. Contributors install the clone
104
+ editable into a virtual environment **outside** the checkout — see
105
+ [CONTRIBUTING.md](CONTRIBUTING.md).
81
106
 
82
107
  ## System readiness and optional integrations
83
108
 
@@ -130,7 +155,7 @@ node "$GRAPHITE_PACKAGE_VALIDATOR" mcp || exit 1
130
155
  Only after the applicable validator command succeeds, enable the declared extra:
131
156
 
132
157
  ```bash
133
- python -m pip install -e ".[mcp]"
158
+ python -m pip install --user "graphite-code[mcp]"
134
159
  ```
135
160
 
136
161
  The deep MCP probe launches an isolated interpreter from a guarded distribution-record import manifest. It rejects current working directory, user-site, and attacker-controlled selected-root shadows. The exact origin-verified trusted Graphite source may be inside the selected repository, but it is accepted only when its expected lexical, canonical, filesystem-identity, and module-origin checks all match; overlapping MCP dependency or distribution-metadata roots and alternate Graphite origins remain rejected.
@@ -162,7 +187,7 @@ Canonical commands ignore ambient `GRAPHITE_LLM*` settings and never read `GRAPH
162
187
 
163
188
  ## Machine-wide usage
164
189
 
165
- Installed editable, `python -m graphite` works from any project in any shell. The `graphite` / `graphite-mcp` console-script shims are equivalent wherever they are on PATH, but a shim directory that PowerShell and cmd see is not always on Git Bash's PATH — prefer `python -m graphite` in scripts and agent instructions.
190
+ Installed for an interpreter, `python -P -m graphite` works from any project in any shell. The `graphite` / `graphite-mcp` console scripts are equivalent wherever they are on PATH — and shadow-proof by construction — but a scripts directory that PowerShell and cmd see is not always on Git Bash's PATH, so prefer `python -P -m graphite` in scripts, hooks and agent instructions. Everything `graphite init` generates (hooks, `.mcp.json`, editor tasks, daemon launchers) already carries the `-P`.
166
191
 
167
192
  To onboard a new or existing project, run one command from anywhere:
168
193
 
@@ -173,9 +198,9 @@ python -m graphite bootstrap /path/to/MyApp # minimal variant: gitignore + AGE
173
198
 
174
199
  The machine-wide daemon (`graphite daemon /path/to/projects`) auto-discovers any project with standard markers (`.git`, `package.json`, `pyproject.toml`, `wrangler.toml`, `go.mod`, `Cargo.toml`) and keeps its graph fresh, so `init` is about wiring agent instructions, not registration. To exclude a directory (and its whole subtree) from supervision — e.g. a third-party SDK checkout — drop a `.graphite-ignore` file in it; the daemon skips it at the next discovery cycle.
175
200
 
176
- Set `GRAPHITE_PROJECTS_ROOT` to change the default base folder used by `daemon`, `daemon-status`, `daemon-health`, the Windows startup installers, and init/bootstrap daemon-visibility checks (defaults to the current directory when unset).
201
+ Set `GRAPHITE_PROJECTS_ROOT` to change the default base folder used by `daemon`, `daemon-status`, `daemon-health`, the platform daemon installers, and init/bootstrap daemon-visibility checks (defaults to the current directory when unset).
177
202
 
178
- After upgrading graphite itself, restart the daemon: a long-running daemon keeps executing the code it loaded at start, and daemon state is in-memory only, so a restart both loads the new code and rebuilds every supervised graph — clearing `engine_changed` staleness across all managed projects in one pass.
203
+ After upgrading graphite itself, restart the daemon — stop it, install, then start — because a long-running daemon keeps executing the code it loaded at start. A daemon started under the new code detects the engine change itself and rebuilds every supervised graph, clearing `engine_changed` staleness across all managed projects in one pass.
179
204
 
180
205
  ## Usage
181
206
 
@@ -257,13 +282,13 @@ graphite bootstrap C:/Projects/MyApp
257
282
  # Check daemon health
258
283
  graphite daemon-health C:/Projects
259
284
 
260
- # Audit whether Graphite can replace Graphify for a project
285
+ # Audit whether Graphite can replace legacy graph tooling in a project
261
286
  graphite audit-replacement C:/Projects/MyApp
262
287
  ```
263
288
 
264
- ## Graphify replacement audit
289
+ ## Legacy replacement audit
265
290
 
266
- Use the replacement audit before removing legacy Graphify files or ignore entries:
291
+ Use the replacement audit before removing a legacy graph tool's files or ignore entries:
267
292
 
268
293
  ```bash
269
294
  graphite audit-replacement C:/Projects/MyApp
@@ -271,7 +296,7 @@ graphite audit-replacement . --json
271
296
  graphite audit-replacement . --fail-on-blocker
272
297
  ```
273
298
 
274
- The audit checks Graphite bootstrap state, graph freshness and validity, daemon visibility, daemon health, physical Graphify remnants, and Graphify text/config references. It reports recommendations but never deletes files automatically.
299
+ The audit checks Graphite bootstrap state, graph freshness and validity, daemon visibility, daemon health, physical legacy-tool remnants, and legacy text/config references. It reports recommendations but never deletes files automatically.
275
300
 
276
301
  ## Daemon health
277
302
 
@@ -750,14 +775,17 @@ Then use `/graphite [path]` inside Claude Code. The skill defaults to zero-LLM m
750
775
 
751
776
  Complete the mandatory package-validation policy and MCP activation steps in [System readiness and optional integrations](#system-readiness-and-optional-integrations). Do not bypass or reorder the validator and install steps.
752
777
 
753
- Then configure Claude Code (Desktop) to use the local server. Add this to your `claude_desktop_config.json`:
778
+ `graphite init` writes a project-local `.mcp.json` entry for Claude Code and
779
+ Codex (and rewrites its own entry on every run, preserving foreign ones). To
780
+ configure a client by hand, launch the server with `-P` — or use the
781
+ `graphite-mcp` console script, which needs no flag:
754
782
 
755
783
  ```json
756
784
  {
757
785
  "mcpServers": {
758
786
  "graphite": {
759
787
  "command": "python",
760
- "args": ["-m", "graphite.mcp"],
788
+ "args": ["-P", "-m", "graphite.mcp"],
761
789
  "cwd": "C:/Projects/YourProject"
762
790
  }
763
791
  }