graphite-code 0.5.3__tar.gz → 1.0.1__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 (275) hide show
  1. {graphite_code-0.5.3 → graphite_code-1.0.1}/ARCHITECTURE.md +6 -5
  2. {graphite_code-0.5.3 → graphite_code-1.0.1}/CHANGELOG.md +269 -1
  3. {graphite_code-0.5.3 → graphite_code-1.0.1}/CONTRIBUTING.md +20 -6
  4. graphite_code-0.5.3/README.md → graphite_code-1.0.1/PKG-INFO +800 -701
  5. graphite_code-0.5.3/PKG-INFO → graphite_code-1.0.1/README.md +72 -61
  6. {graphite_code-0.5.3 → graphite_code-1.0.1}/RELEASING.md +448 -379
  7. {graphite_code-0.5.3 → graphite_code-1.0.1}/pyproject.toml +262 -218
  8. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/__init__.py +41 -41
  9. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/_cleanup_worker.py +31 -14
  10. graphite_code-1.0.1/src/graphite/_win32_ctypes.py +36 -0
  11. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/agent_settings.py +226 -226
  12. graphite_code-1.0.1/src/graphite/analyze.py +234 -0
  13. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/answer_contract.py +2 -2
  14. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/cache.py +93 -3
  15. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/cli.py +373 -62
  16. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/config.py +209 -209
  17. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/daemon.py +9 -2
  18. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/daemon_health.py +39 -9
  19. graphite_code-1.0.1/src/graphite/daemon_launch.py +75 -0
  20. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/dependency_install.py +15 -12
  21. graphite_code-1.0.1/src/graphite/detach.py +37 -0
  22. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/doctor.py +687 -678
  23. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/doctor_probes.py +2147 -2134
  24. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/export/json.py +1 -1
  25. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/extract/ast.py +4 -2
  26. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/git.py +6 -1
  27. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/graph.py +1 -1
  28. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/hook_entry.py +68 -68
  29. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/hookshim.py +86 -86
  30. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/init.py +2 -2
  31. graphite_code-1.0.1/src/graphite/launchd_agent.py +139 -0
  32. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/mcp_server.py +463 -450
  33. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/probe_process.py +953 -879
  34. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/probe_workspace.py +31 -26
  35. graphite_code-1.0.1/src/graphite/py.typed +0 -0
  36. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/query.py +1 -1
  37. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/replacement_audit.py +42 -37
  38. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/resolve.py +663 -660
  39. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/approval.py +362 -362
  40. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/claude_executor.py +7 -2
  41. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/contracts.py +2 -0
  42. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/edit_apply.py +5 -2
  43. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/lifecycle.py +2 -0
  44. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/lifecycle_service.py +6 -2
  45. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/lifecycle_storage.py +992 -991
  46. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/openrouter_probe.py +5 -1
  47. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/policy.py +3 -3
  48. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/probe_runner.py +572 -543
  49. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/process_runner.py +17 -1
  50. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/profiles.py +1 -1
  51. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/route_pool.py +8 -5
  52. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/schema_validation.py +2 -1
  53. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/service.py +28 -12
  54. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/storage.py +4025 -4019
  55. graphite_code-1.0.1/src/graphite/systemd_unit.py +129 -0
  56. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/ts_bridge.py +5 -2
  57. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/ts_resolver.mjs +4 -4
  58. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/typescript_activation.py +18 -6
  59. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/validation.py +1 -1
  60. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/windows_job.py +27 -9
  61. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/windows_task.py +27 -37
  62. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_agent_settings.py +276 -276
  63. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_answer_contract.py +487 -487
  64. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_arrow_and_constructor_binding.py +172 -172
  65. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_bootstrap.py +304 -304
  66. graphite_code-1.0.1/tests/test_cache_entry_pruning.py +245 -0
  67. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_cache_file_set.py +110 -110
  68. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_class_field_arrow_binding.py +130 -130
  69. graphite_code-1.0.1/tests/test_cli_reference.py +182 -0
  70. graphite_code-1.0.1/tests/test_compare_dist.py +75 -0
  71. graphite_code-1.0.1/tests/test_configuration_reference.py +100 -0
  72. graphite_code-1.0.1/tests/test_cycle_search.py +128 -0
  73. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_daemon_health.py +1040 -1040
  74. graphite_code-1.0.1/tests/test_daemon_launch.py +42 -0
  75. graphite_code-1.0.1/tests/test_daemon_service_cli.py +102 -0
  76. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_destructured_binding.py +183 -183
  77. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_dispatch_evidence.py +277 -277
  78. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_doctor.py +81 -15
  79. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_documentation.py +45 -1
  80. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_engine_identity.py +262 -262
  81. graphite_code-1.0.1/tests/test_exit_codes_reference.py +36 -0
  82. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_external_calls.py +548 -548
  83. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_init.py +446 -446
  84. graphite_code-1.0.1/tests/test_launchd_agent.py +122 -0
  85. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_lifecycle_operator.py +253 -253
  86. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_mcp.py +3 -3
  87. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_method_dispatch_scope.py +294 -294
  88. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_monorepo.py +199 -199
  89. graphite_code-1.0.1/tests/test_mutation_tests.py +365 -0
  90. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_probe_workspace.py +443 -443
  91. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_provider_lifecycle_storage.py +576 -542
  92. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_reliability.py +153 -153
  93. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_replacement_audit.py +6 -6
  94. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_cli_recovery.py +283 -283
  95. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_contracts.py +246 -246
  96. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_profiles.py +685 -685
  97. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_service.py +469 -469
  98. graphite_code-1.0.1/tests/test_synthetic_repo.py +96 -0
  99. graphite_code-1.0.1/tests/test_systemd_unit.py +156 -0
  100. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_typescript_resolver.py +274 -274
  101. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_verify_artifact.py +13 -0
  102. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_verify_published_release.py +68 -3
  103. graphite_code-0.5.3/src/graphite/analyze.py +0 -146
  104. graphite_code-0.5.3/src/graphite/detach.py +0 -33
  105. {graphite_code-0.5.3 → graphite_code-1.0.1}/.gitignore +0 -0
  106. {graphite_code-0.5.3 → graphite_code-1.0.1}/LICENSE +0 -0
  107. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/__main__.py +0 -0
  108. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/activation.py +0 -0
  109. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/agent_hooks.py +0 -0
  110. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/bootstrap.py +0 -0
  111. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/buildlock.py +0 -0
  112. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/channel.py +0 -0
  113. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/cluster.py +0 -0
  114. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/context.py +0 -0
  115. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/debt.py +0 -0
  116. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/engine_identity.py +0 -0
  117. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/export/__init__.py +0 -0
  118. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/export/html.py +0 -0
  119. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/export/md.py +0 -0
  120. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/extract/__init__.py +0 -0
  121. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/freshness.py +0 -0
  122. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/graph_io.py +0 -0
  123. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/health.py +0 -0
  124. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/hookinstall.py +0 -0
  125. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/incident_ledger.py +0 -0
  126. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/ingest.py +0 -0
  127. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/io.py +0 -0
  128. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/listing.py +0 -0
  129. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/llm.py +0 -0
  130. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/llm_probe.py +0 -0
  131. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/mcp.py +0 -0
  132. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/natural_query.py +0 -0
  133. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/overlays.py +0 -0
  134. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/process_contracts.py +0 -0
  135. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/provider_observer.py +0 -0
  136. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/query_plan.py +0 -0
  137. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/review.py +0 -0
  138. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/__init__.py +0 -0
  139. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/classifier.py +0 -0
  140. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/claude_probe.py +0 -0
  141. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/cli_identity.py +0 -0
  142. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/codex_executor.py +0 -0
  143. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/codex_probe.py +0 -0
  144. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/context_builder.py +0 -0
  145. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/diff_policy.py +0 -0
  146. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/effort.py +0 -0
  147. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/lifecycle_operator.py +0 -0
  148. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/ollama_executor.py +0 -0
  149. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/ollama_probe.py +0 -0
  150. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/openrouter_executor.py +0 -0
  151. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/prompt.py +0 -0
  152. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/registry.py +0 -0
  153. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/route_pool_execution.py +0 -0
  154. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/settings.py +0 -0
  155. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/shadow.py +0 -0
  156. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/telemetry.py +0 -0
  157. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/worktree.py +0 -0
  158. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/zai_edit.py +0 -0
  159. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/zai_executor.py +0 -0
  160. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/routing/zai_probe.py +0 -0
  161. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/savings.py +0 -0
  162. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/usage_ledger.py +0 -0
  163. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/watch.py +0 -0
  164. {graphite_code-0.5.3 → graphite_code-1.0.1}/src/graphite/windows_startup.py +0 -0
  165. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/conftest.py +0 -0
  166. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/data/sqloracle/blindspots.py.txt +0 -0
  167. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/data/sqloracle/round69.py.txt +0 -0
  168. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/fake_clis/fake_cli.py +0 -0
  169. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/fixtures/provider_lifecycle_schema_v1.sql +0 -0
  170. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/fixtures/routing_schema_v2_ca77600.sql +0 -0
  171. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/fixtures/routing_schema_v3_94eb333.sql +0 -0
  172. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/fixtures/routing_schema_v4_lifecycle_migration.sql +0 -0
  173. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_activation.py +0 -0
  174. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_activation_backstop.py +0 -0
  175. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_agent_hook_activation.py +0 -0
  176. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_agent_hooks.py +0 -0
  177. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_atomic_write.py +0 -0
  178. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_build_detach.py +0 -0
  179. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_buildlock.py +0 -0
  180. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_cache_engine_identity.py +0 -0
  181. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_cache_pruning.py +0 -0
  182. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_call_graph.py +0 -0
  183. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_channel_cli.py +0 -0
  184. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_channel_core.py +0 -0
  185. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_channel_lock.py +0 -0
  186. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_channel_mcp.py +0 -0
  187. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_channel_register.py +0 -0
  188. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_channel_report.py +0 -0
  189. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_channel_status.py +0 -0
  190. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_cli_channel.py +0 -0
  191. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_cli_version.py +0 -0
  192. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_context.py +0 -0
  193. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_cwd_relative_config.py +0 -0
  194. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_daemon.py +0 -0
  195. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_daemon_activation.py +0 -0
  196. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_daemon_build_lock.py +0 -0
  197. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_daemon_child_lock.py +0 -0
  198. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_daemon_engine_staleness.py +0 -0
  199. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_daemon_health_activation.py +0 -0
  200. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_debt.py +0 -0
  201. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_determinism.py +0 -0
  202. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_doctor_agent_hooks.py +0 -0
  203. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_doctor_hooks.py +0 -0
  204. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_doctor_managed_docs.py +0 -0
  205. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_edit_apply.py +0 -0
  206. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_git_security.py +0 -0
  207. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_go_rust.py +0 -0
  208. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_graph_io.py +0 -0
  209. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_graph_provider_isolation.py +0 -0
  210. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_hardening.py +0 -0
  211. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_health.py +0 -0
  212. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_hook_entry.py +0 -0
  213. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_hook_template.py +0 -0
  214. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_hookinstall.py +0 -0
  215. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_hookshim.py +0 -0
  216. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_html_security.py +0 -0
  217. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_incident_ledger.py +0 -0
  218. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_init_activation_doctrine.py +0 -0
  219. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_init_hooks.py +0 -0
  220. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_init_validation_exit.py +0 -0
  221. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_listing.py +0 -0
  222. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_listing_surfaces.py +0 -0
  223. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_llm.py +0 -0
  224. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_natural_query.py +0 -0
  225. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_node_identity.py +0 -0
  226. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_overlays.py +0 -0
  227. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_path_leak_fixture.py +0 -0
  228. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_probe_diagnostics.py +0 -0
  229. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_provider_claude_probe.py +0 -0
  230. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_provider_codex_probe.py +0 -0
  231. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_provider_lifecycle.py +0 -0
  232. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_provider_lifecycle_service.py +0 -0
  233. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_provider_observer.py +0 -0
  234. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_provider_ollama_probe.py +0 -0
  235. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_provider_openrouter_probe.py +0 -0
  236. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_provider_probe_runner.py +0 -0
  237. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_public_surface.py +0 -0
  238. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_published_schemas.py +0 -0
  239. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_python_resolver.py +0 -0
  240. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_query_plan.py +0 -0
  241. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_resolve.py +0 -0
  242. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_review.py +0 -0
  243. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_route_pool.py +0 -0
  244. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_approval.py +0 -0
  245. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_benchmark.py +0 -0
  246. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_classifier.py +0 -0
  247. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_claude_executor.py +0 -0
  248. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_cli.py +0 -0
  249. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_cli_contracts.py +0 -0
  250. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_codex_executor.py +0 -0
  251. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_context.py +0 -0
  252. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_diff_policy.py +0 -0
  253. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_executor.py +0 -0
  254. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_openrouter_executor.py +0 -0
  255. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_policy.py +0 -0
  256. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_process_runner.py +0 -0
  257. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_registry.py +0 -0
  258. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_schema_validation.py +0 -0
  259. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_security.py +0 -0
  260. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_shadow.py +0 -0
  261. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_storage.py +0 -0
  262. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_telemetry.py +0 -0
  263. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_worktree.py +0 -0
  264. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_zai_executor.py +0 -0
  265. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_routing_zai_probe.py +0 -0
  266. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_savings.py +0 -0
  267. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_search.py +0 -0
  268. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_smoke.py +0 -0
  269. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_sqloracle.py +0 -0
  270. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_typescript_activation.py +0 -0
  271. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_usage_ledger.py +0 -0
  272. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_watch.py +0 -0
  273. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_windows_startup.py +0 -0
  274. {graphite_code-0.5.3 → graphite_code-1.0.1}/tests/test_windows_task.py +0 -0
  275. {graphite_code-0.5.3 → graphite_code-1.0.1}/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
 
@@ -1,7 +1,8 @@
1
1
  # Changelog
2
2
 
3
3
  Notable changes to graphite. Format follows [Keep a Changelog]; versioning is
4
- semver, pre-1.0 (minor versions may break things).
4
+ semantic. From 1.0.0 the surfaces listed in `docs/compatibility.md` are
5
+ stable: a breaking change to any of them is a major release.
5
6
 
6
7
  **A version number here is a coarse release label, not a fix marker.** Several
7
8
  behavioural fixes have shipped with no version change at all — the `-P`
@@ -12,6 +13,273 @@ machine-checkable identity; the version is for humans.
12
13
 
13
14
  [Keep a Changelog]: https://keepachangelog.com/en/1.1.0/
14
15
 
16
+ ## [1.0.1] — 2026-09-05
17
+
18
+ A patch release. Measured against the deployed 1.0.0 engine on this repository,
19
+ the tree produces the same graph: 7260 of 7260 node ids survive, 20700 of 20700
20
+ edge triples unchanged, every relation count equal, `analysis.cycles` identical.
21
+ Only the engine fingerprint moves, because graphite's own bytes did.
22
+
23
+ ### Changed
24
+
25
+ **Vendor-neutral wording throughout the replacement audit and docs.** The
26
+ README opening, the `audit-replacement` help and reference text,
27
+ `skill/SKILL.md` and the audit's own messages no longer name the legacy
28
+ graph tool, and the README's opening comparative against it — an unmeasured
29
+ claim dating to the initial commit (`fd58583`) — is removed rather than
30
+ defended. In the audit's report, which is not a `schema_version`-stable
31
+ surface, the `graphify` block is renamed `legacy` and the warning codes
32
+ `graphify_paths_exist`, `graphify_text_references` and
33
+ `graphify_gitignore_entries` become `legacy_paths_exist`,
34
+ `legacy_text_references` and `legacy_gitignore_entries`. Detection is
35
+ unchanged: the on-disk path patterns and the text-scan needle still match
36
+ the legacy tool's literal artifact names — the one place the name must
37
+ remain for the audit to find anything.
38
+
39
+ ### Fixed
40
+
41
+ **The extraction cache grew without bound inside a partition (#66).** #23
42
+ reclaims whole partitions a build can never read again and left the one it
43
+ uses alone. Entries are keyed on the file's content hash, so every edit that
44
+ triggers a rebuild wrote a new entry and orphaned the previous one, and
45
+ nothing ever removed an orphan: a consumer checkout rebuilt by the daemon on
46
+ every change reached 15,406 entries / 419 MB in a single partition, 11,592 of
47
+ them from four days, against 1,780 / 62 MB for graphite's own tree on the
48
+ same engine. Same rule as #23, one level down -- reachability, not age or
49
+ size: a `Cache` now records every key it read (and found) or wrote, and after
50
+ a build has extracted the tree and built its graph, `prune_unreachable_entries`
51
+ deletes every other entry in the partition, reporting
52
+ `[graphite] reclaimed N unreachable cache entries (X MB)` when it removed any.
53
+ Older content checked out later re-extracts once (performance, never
54
+ correctness). Safe without the build lock: `read` already treats a vanished
55
+ file as a miss, so a concurrent reader re-extracts rather than fails. A build
56
+ that touched nothing prunes nothing, only the exact `<2 hex>/<64 hex>.json`
57
+ shape the cache writes is eligible, and a per-entry failure (a Windows handle
58
+ race) is swallowed.
59
+
60
+ **The deferred stdin close never fired on Linux or macOS.** `run_bounded_process`
61
+ reads the child's pipes with a fixed-size read, and its two transports hand
62
+ that reader different objects: on Windows a raw `io.FileIO`, whose `read(n)`
63
+ returns whatever the child has written, and on POSIX a default `Popen` pipe,
64
+ a `BufferedReader` whose `read(n)` keeps reading until it has n bytes or EOF.
65
+ Off Windows the reader therefore saw nothing until the child exited, the
66
+ `stdin_close_when` predicate never saw its marker, and every deferred close
67
+ was the budget fallback -- so the #29 fix was inert there and the doctor's MCP
68
+ deep probe spent its whole budget on every POSIX run. The reader now uses
69
+ `read1` on buffered pipes; the Windows path is unchanged. The tests that
70
+ pinned the deferral could not fail (one matched a marker that text-mode
71
+ stdout writes as CRLF on Windows, the other's lower bound was satisfied by
72
+ the child's own sleep) and now kill a never-defer mutant; the interval test
73
+ that blocked the pre-push gate measures from the close instead of from
74
+ launch, against the child's own life rather than a 0.25s constant.
75
+
76
+ **The post-publication verifier's provenance arm asked an endpoint that
77
+ never carries provenance.** `scripts/verify_published_release.py` read
78
+ `urls[].provenance` from PyPI's legacy `/pypi/<project>/<version>/json`
79
+ API, which has no such key, so the arm reported "no provenance" for 1.0.0
80
+ while the Integrity API was already serving both GitHub attestation bundles
81
+ (`jared0565/graphite`, `publish.yml`). The arm now reads the PEP 691 Simple
82
+ JSON index (`Accept: application/vnd.pypi.simple.v1+json`), where PEP 740
83
+ publishes a `provenance` URL per file; a test pins the endpoint so a
84
+ regression to the legacy API fails. Re-run against 1.0.0: 6 of 6 arms.
85
+
86
+ **Three findings from aramid's `llm-review` tier on the CI workflow and the
87
+ benchmark, all confirmed and closed:**
88
+
89
+ - The `coverage_floor` dispatch input could *lower* the floor for one run,
90
+ so a dispatched run could report green against a weaker gate than the
91
+ committed one. The enforcing step now refuses any requested value that is
92
+ not an integer at or above the committed `COVERAGE_FLOOR`; a dispatch can
93
+ only raise it (the negative control is unchanged). Proven with a fake
94
+ coverage across six arms: empty, 84 and 83 pass; 80, `abc` and -5 fail
95
+ before coverage is read.
96
+ - `test_core_probe_cleanup_timeout_uses_single_global_slot_and_recovers`
97
+ raced its 0.5 s probe budget against runner load (#65): more than 0.4 s of
98
+ scheduling before the workspace lease is acquired yields a pure `timeout`,
99
+ after it a `cleanup_timeout` carrying `masked_*` keys — two CI failures in
100
+ three runs on one day, both shapes reproduced with a paced fake clock. The
101
+ test now freezes its injected `_clock` like the sibling probe tests (the
102
+ subject is the classification of a hung cleanup, not phase pacing; the
103
+ cleanup join still runs on real time), and the frozen-clock test still
104
+ kills both guarding mutants: a hung cleanup reported as `None`, and a
105
+ probe slot that never releases.
106
+ - The `security` job's self-check asserted only that gitleaks and semgrep
107
+ ran, while the step's name claimed typecheck and a dependency audit. The
108
+ labels were measured from the job's own `prepush.json` artifact
109
+ (`gitleaks, mypy, python.exe, semgrep, shadow`): the assertion now covers
110
+ every configured scanner, and the step name, `SECURITY.md` and
111
+ `CONTRIBUTING.md` no longer claim a dependency audit — no gate run on
112
+ either machine has ever emitted one, although `aramid doctor` lists
113
+ pip-audit as present (reported to aramid). Measuring this exposed two
114
+ worse things: under aramid 0.6.0 the CI typecheck slot had **never run**
115
+ in `--all` mode (no `mypy` in `tools_ran`, nothing degraded) and the old
116
+ assertion called that clean; under 0.6.1 it runs over every `.py`, and
117
+ the job passed with exit 0 while its own report carried 786 block-tier
118
+ findings that the same report fails with on the maintainer's machine
119
+ (exit 1). Discriminated on the first run under the 0.7.0 pin (33286250634):
120
+ still 786 block-tier findings and exit 0 with CI's empty ledger, so
121
+ aramid's exit depends on ledger presence — `.aramid/` is gitignored, so
122
+ a fresh checkout cannot be gated by exit status at all. aramid confirmed
123
+ the mechanism from its code (channel round 151): its **fresh-ledger
124
+ rule** — the first pre-push run on a ledger with no baseline writes one
125
+ and downgrades findings the ratchet alone escalated to exit 0, so every
126
+ CI checkout is "the first run" — and from 0.7.1 the report says so
127
+ (`fresh_ledger_baseline`, `grandfathered`; both gate steps print them).
128
+ Both CI gate steps now
129
+ read the verdict from the report itself: any block-tier finding from a
130
+ non-mypy scanner, or from mypy inside the project's type-gate scope
131
+ (`src/graphite`), fails the step regardless of the exit status; mypy
132
+ findings on the deliberately untyped `tests/`, `scripts/` and
133
+ `benchmarks/` trees are counted and printed.
134
+ - `benchmarks/build_benchmark.py` prepended the repository root to
135
+ `sys.path` to import its sibling — the shape the `-P` launch contract
136
+ forbids. It loads `synthetic_repo.py` by location now, CI runs it under
137
+ `-P`, and two tests pin the shape (no `sys.path` access in the module; the
138
+ script starts under `-P`).
139
+
140
+ ### Changed
141
+
142
+ - CI pins `aramid==0.7.2`, the tool this machine runs (channel round 157;
143
+ 0.7.0 and 0.7.1 were pinned briefly at rounds 148 and 152). 0.7.2 fixes
144
+ the tests-slot skip-streak false alarm this repository reported — the
145
+ registry key `tests` rode into `run_started.expected` beside the slot's
146
+ label, so `status` counted every pre-push run as a skipped suite (channel
147
+ rounds 155–157) — and stamps `run_id`/`recorded` in the check report, so
148
+ a `--no-record` measurement is distinguishable from a recorded one.
149
+ 0.7.1's typecheck slot honours
150
+ `[tool.mypy] files`, so the 683 mypy rows that a whole-tree measurement
151
+ under 0.7.0 had written into the maintainer's ledger for `tests/` (675)
152
+ and `scripts/` (8) are retired with `ledger resolve --out-of-scope`, each
153
+ carrying the reason.
154
+ The two `mypy:syntax` ledger rows that 0.6.1 left unresolvable were NOT
155
+ retired with `ledger resolve --out-of-scope` as first written here: it
156
+ refused them (`is not open (status=fixed)`), because aramid's gate had
157
+ already marked them `fixed` on a push that ran mypy over other files
158
+ without opening `ci.yml` — a defect in aramid 0.6.1's examined-set
159
+ stamping, fixed on aramid's `main` (`e65f296`) for 0.7.1. The rows stay
160
+ `fixed`, the log being append-only; channel round 151 is their record.
161
+ - mypy's configuration is back under `[tool.mypy]` in `pyproject.toml` and
162
+ `setup.cfg` is gone. It had moved there only because aramid 0.6.0's
163
+ typecheck runner armed on `[tool.mypy]` and then fed every changed file to
164
+ mypy; aramid 0.6.1 hands mypy only `.py`/`.pyi` (channel round 144), so
165
+ the CI pin is now `aramid==0.6.1` and the two `mypy:syntax` suppression
166
+ entries that bridged the defect are deleted rather than kept — a
167
+ workaround that outlives its root cause misdirects the next reader.
168
+
169
+ ### Documentation
170
+
171
+ - **The generated CLI reference now renders nested subcommands.**
172
+ `scripts/gen_cli_reference.py` walked only the top level of the parser,
173
+ so the twenty-four commands that live one level down — under `route`,
174
+ `lifecycle`, `channel`, `incidents` and `overlay` — and every option they
175
+ own (`route reconcile --attempt-id`, for one) were absent from
176
+ `docs/reference/cli.md`, and the lockstep test could not see them drift.
177
+ Each nested command now has its own section under its group, and
178
+ `tests/test_cli_reference.py` requires every nested command and every
179
+ option it declares to appear there.
180
+ - **Every CLI argument now declares help text.** Rendering the nested
181
+ commands exposed sixty-two arguments with none — every nested `--json`,
182
+ the `lifecycle policy prepare` and `verification prepare` inputs,
183
+ `route accept|reject|cleanup|review --task-id`, `record-outcome`'s
184
+ flags, `reconcile --attempt-id`, `policy --promote/--rollback`, the
185
+ `incidents ack|resolve` positionals and options — so both `--help` and
186
+ the reference were silent about them. Each now says what it takes and
187
+ what it binds to; a test walks the whole parser and fails on any
188
+ argument without help, so none can be added back.
189
+ - New [user guide](docs/user-guide.md) and [knowledge base](docs/knowledge-base.md).
190
+ - README: installation is `pip install graphite-code` (it had still said
191
+ `git clone` + `pip install -e .`, wrong since the 0.3.0 wheel), the `-P`
192
+ launch rule is explained where the command is introduced, the MCP
193
+ examples launch with `-P`, and a documentation index points at every
194
+ support page. CONTRIBUTING puts the dev environment outside the clone
195
+ and lists what CI enforces. ARCHITECTURE covers the platform
196
+ supervisors and the bounded cycle search. The Claude Code skill is
197
+ machine-neutral and versioned 1.0.0.
198
+
199
+ ## [1.0.0] — 2026-08-29
200
+
201
+ The production-grade release. Nothing here is a claim CI cannot turn red:
202
+ each item names the gate or test that enforces it. The declaration criteria
203
+ and their evidence are in `docs/superpowers/specs/2026-08-29-production-grade-design.md`.
204
+
205
+ ### Fixed
206
+
207
+ **The cycle report could not finish on a repository with dense import
208
+ cycles** (#64). `analyze()` enumerated every simple cycle of the project
209
+ graph before sorting and keeping twenty; on Django 5.2 (2 930 source files,
210
+ `tests/` and `django/` importing each other) that is exponential — 13 GB and
211
+ unfinished after thirty minutes, while each half alone reported cycles in
212
+ seconds. The search is now bounded: cycles are enumerated inside strongly
213
+ connected components one length level at a time up to length 8, under a
214
+ 10 000-cycle budget, and stop once a completed level holds the twenty
215
+ shortest — exact whenever it stops that way, and deterministic across
216
+ processes even when the budget bites. `analysis.cycle_search` records
217
+ `length_bound`, `budget`, `examined`, `budget_exhausted`,
218
+ `complete_through_length`, `cyclic_components` and `largest_component`, so a
219
+ truncated answer says so. Django 5.2 now builds in 82 s (45 620 nodes,
220
+ 109 373 edges, 53 MB). Found by the real-repository benchmark; the synthetic
221
+ corpus alone would never have shown it.
222
+
223
+ **A lifecycle event claiming "no prior observation" over an UNAVAILABLE row
224
+ raised `AttributeError`** instead of `lifecycle_transition_stale`
225
+ (`routing/lifecycle_storage.py`); found while making the type gate clean.
226
+
227
+ ### Added
228
+
229
+ - **Every supported cell gates merges.** The CI matrix is windows/ubuntu/macos
230
+ × CPython 3.11–3.14, all twelve cells blocking; the advisory `portability`
231
+ job is gone. Ubuntu and macOS 3.13 ran for the first time and pass.
232
+ - **A type gate.** `python -m mypy` over `src/graphite` runs in the lint
233
+ job; 142 findings were fixed with zero `type: ignore` comments added, and
234
+ the gate is clean under `--platform` win32, linux and darwin.
235
+ `graphite/py.typed` ships (the artifact verifier refuses a wheel without
236
+ it) and the distribution carries `Typing :: Typed`.
237
+ - **A coverage floor.** Branch coverage is collected on the 3.12 leg of each
238
+ OS, combined (83.04 % on the first measurement), and enforced at
239
+ `COVERAGE_FLOOR = 83`; a `workflow_dispatch` input overrides the floor for
240
+ one run as the negative control (floor 84 turns the job red).
241
+ - **The security gates run in CI, unmodified.** A `security` job creates the
242
+ dev venv `aramid.toml` names and runs `aramid check --gate pre-commit|pre-push
243
+ --all --strict` against the committed configuration, asserting the tools
244
+ that ran (gitleaks, ruff, shadow; gitleaks, mypy, the suite, semgrep,
245
+ shadow) and refusing any degraded tool. A planted repo-root `graphite.py`
246
+ fails it at the pre-commit gate — and, under `python -m pytest`, every test
247
+ leg with it, which is the hazard the `-P` launch shape exists for.
248
+ - `SECURITY.md` (GitHub private vulnerability reporting), `CODEOWNERS`, issue
249
+ templates, dependabot for actions and pip, `authors` metadata.
250
+ - `daemon-install-linux` (systemd user unit), `daemon-install-macos`
251
+ (launchd agent), `daemon-uninstall-*` and `daemon-service-status`; all
252
+ three supervisors build their launch argv from one place, so `-P` is
253
+ present by construction. `daemon-health` reports the platform's supervisor.
254
+ - `benchmarks/`: a deterministic synthetic corpus and a build benchmark; a CI
255
+ `benchmark` job records a 3 000-file build as an artifact and fails only
256
+ past a catastrophic budget. `capabilities` declares
257
+ `supported_repo_files: 7000` with its basis; `docs/benchmarks.md` holds the
258
+ measurements, including Django 5.2.
259
+ - Reference documentation that cannot drift: `docs/reference/cli.md`
260
+ (generated from the parser; a test fails when stale),
261
+ `docs/reference/configuration.md` (every `Config` field and every
262
+ `GRAPHITE_*` variable the code reads, both directions tested),
263
+ `docs/reference/exit-codes.md` (every subcommand has a row),
264
+ `docs/compatibility.md` (what 1.x promises, the deprecation policy, the
265
+ support matrix).
266
+ - Releases are built in CI from the approved tag with pinned tools
267
+ (`release-build-constraints.txt`), refused unless both digests equal the
268
+ reviewed ones, attached to the GitHub Release, and published with PEP 740
269
+ attestations that are true because this workflow built the bytes.
270
+
271
+ ### Changed
272
+
273
+ - `Development Status :: 5 - Production/Stable`. From 1.0.0 the surfaces in
274
+ `docs/compatibility.md` are stable; a breaking change to any of them is a
275
+ major release.
276
+ - The CI security job pins aramid 0.6.0 (the version the maintainer's
277
+ machine runs). mypy's configuration lives in `setup.cfg` for now: aramid
278
+ 0.6.0's typecheck runner arms on `[tool.mypy]` and hands non-Python files
279
+ to mypy (accepted in channel round 140, fixed in aramid 0.6.1); both
280
+ files say when to move it back. Two reasoned entries in
281
+ `.aramid-suppressions.toml` from the same defect retire themselves.
282
+
15
283
  ## [0.5.3] — 2026-08-28
16
284
 
17
285
  ### Fixed
@@ -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,20 +89,32 @@ 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
 
102
107
  No live provider calls in automated tests are permitted. Exercise network and subprocess behavior through a fake worker, fake provider, and fake process boundary with controlled time, output, failures, and descendants. Tests must remain offline, deterministic, and safe to run without credentials.
103
108
 
109
+ ### Coverage floor
110
+
111
+ CI collects branch coverage on the 3.12 leg of every OS, combines the three
112
+ data files in the `coverage` job, and fails under `COVERAGE_FLOOR` in
113
+ `.github/workflows/ci.yml`. The floor is the integer part of the measured
114
+ total: raise it when a release measures higher, and never lower it without a
115
+ CHANGELOG entry saying what was removed and why. No single leg's figure is
116
+ the measurement -- platform-only code is covered on one OS each.
117
+
104
118
  ## Security expectations
105
119
 
106
120
  Treat repository files, file names, symlinks, Git metadata, generated graph data, and model output as untrusted input. Pass subprocess arguments as an argument vector and never interpolate untrusted values into a shell command. Preserve path containment, bounded I/O, explicit timeouts, atomic writes, output encoding, and safe error redaction at every relevant boundary.