graphite-code 0.5.1__tar.gz → 0.5.2__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 (257) hide show
  1. {graphite_code-0.5.1 → graphite_code-0.5.2}/CHANGELOG.md +29 -0
  2. {graphite_code-0.5.1 → graphite_code-0.5.2}/PKG-INFO +1 -1
  3. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/__init__.py +1 -1
  4. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/buildlock.py +53 -3
  5. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/cli.py +3055 -3053
  6. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/daemon.py +782 -745
  7. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_build_detach.py +15 -6
  8. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_buildlock.py +52 -1
  9. graphite_code-0.5.2/tests/test_daemon_build_lock.py +158 -0
  10. graphite_code-0.5.2/tests/test_daemon_child_lock.py +104 -0
  11. graphite_code-0.5.1/tests/test_daemon_build_lock.py +0 -83
  12. {graphite_code-0.5.1 → graphite_code-0.5.2}/.gitignore +0 -0
  13. {graphite_code-0.5.1 → graphite_code-0.5.2}/ARCHITECTURE.md +0 -0
  14. {graphite_code-0.5.1 → graphite_code-0.5.2}/CONTRIBUTING.md +0 -0
  15. {graphite_code-0.5.1 → graphite_code-0.5.2}/LICENSE +0 -0
  16. {graphite_code-0.5.1 → graphite_code-0.5.2}/README.md +0 -0
  17. {graphite_code-0.5.1 → graphite_code-0.5.2}/RELEASING.md +0 -0
  18. {graphite_code-0.5.1 → graphite_code-0.5.2}/pyproject.toml +0 -0
  19. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/__main__.py +0 -0
  20. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/_cleanup_worker.py +0 -0
  21. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/activation.py +0 -0
  22. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/agent_hooks.py +0 -0
  23. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/agent_settings.py +0 -0
  24. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/analyze.py +0 -0
  25. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/answer_contract.py +0 -0
  26. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/bootstrap.py +0 -0
  27. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/cache.py +0 -0
  28. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/channel.py +0 -0
  29. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/cluster.py +0 -0
  30. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/config.py +0 -0
  31. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/context.py +0 -0
  32. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/daemon_health.py +0 -0
  33. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/debt.py +0 -0
  34. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/dependency_install.py +0 -0
  35. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/detach.py +0 -0
  36. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/doctor.py +0 -0
  37. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/doctor_probes.py +0 -0
  38. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/engine_identity.py +0 -0
  39. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/export/__init__.py +0 -0
  40. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/export/html.py +0 -0
  41. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/export/json.py +0 -0
  42. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/export/md.py +0 -0
  43. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/extract/__init__.py +0 -0
  44. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/extract/ast.py +0 -0
  45. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/freshness.py +0 -0
  46. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/git.py +0 -0
  47. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/graph.py +0 -0
  48. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/graph_io.py +0 -0
  49. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/health.py +0 -0
  50. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/hook_entry.py +0 -0
  51. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/hookinstall.py +0 -0
  52. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/hookshim.py +0 -0
  53. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/incident_ledger.py +0 -0
  54. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/ingest.py +0 -0
  55. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/init.py +0 -0
  56. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/io.py +0 -0
  57. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/listing.py +0 -0
  58. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/llm.py +0 -0
  59. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/llm_probe.py +0 -0
  60. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/mcp.py +0 -0
  61. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/mcp_server.py +0 -0
  62. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/natural_query.py +0 -0
  63. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/overlays.py +0 -0
  64. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/probe_process.py +0 -0
  65. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/probe_workspace.py +0 -0
  66. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/process_contracts.py +0 -0
  67. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/provider_observer.py +0 -0
  68. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/query.py +0 -0
  69. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/query_plan.py +0 -0
  70. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/replacement_audit.py +0 -0
  71. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/resolve.py +0 -0
  72. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/review.py +0 -0
  73. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/__init__.py +0 -0
  74. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/approval.py +0 -0
  75. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/classifier.py +0 -0
  76. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/claude_executor.py +0 -0
  77. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/claude_probe.py +0 -0
  78. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/cli_identity.py +0 -0
  79. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/codex_executor.py +0 -0
  80. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/codex_probe.py +0 -0
  81. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/context_builder.py +0 -0
  82. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/contracts.py +0 -0
  83. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/diff_policy.py +0 -0
  84. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/edit_apply.py +0 -0
  85. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/effort.py +0 -0
  86. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/lifecycle.py +0 -0
  87. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/lifecycle_operator.py +0 -0
  88. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/lifecycle_service.py +0 -0
  89. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/lifecycle_storage.py +0 -0
  90. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/ollama_executor.py +0 -0
  91. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/ollama_probe.py +0 -0
  92. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/openrouter_executor.py +0 -0
  93. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/openrouter_probe.py +0 -0
  94. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/policy.py +0 -0
  95. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/probe_runner.py +0 -0
  96. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/process_runner.py +0 -0
  97. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/profiles.py +0 -0
  98. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/prompt.py +0 -0
  99. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/registry.py +0 -0
  100. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/route_pool.py +0 -0
  101. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/route_pool_execution.py +0 -0
  102. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/schema_validation.py +0 -0
  103. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/service.py +0 -0
  104. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/settings.py +0 -0
  105. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/shadow.py +0 -0
  106. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/storage.py +0 -0
  107. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/telemetry.py +0 -0
  108. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/worktree.py +0 -0
  109. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/zai_edit.py +0 -0
  110. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/zai_executor.py +0 -0
  111. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/routing/zai_probe.py +0 -0
  112. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/savings.py +0 -0
  113. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/ts_bridge.py +0 -0
  114. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/ts_resolver.mjs +0 -0
  115. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/typescript_activation.py +0 -0
  116. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/usage_ledger.py +0 -0
  117. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/validation.py +0 -0
  118. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/watch.py +0 -0
  119. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/windows_job.py +0 -0
  120. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/windows_startup.py +0 -0
  121. {graphite_code-0.5.1 → graphite_code-0.5.2}/src/graphite/windows_task.py +0 -0
  122. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/conftest.py +0 -0
  123. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/data/sqloracle/blindspots.py.txt +0 -0
  124. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/data/sqloracle/round69.py.txt +0 -0
  125. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/fake_clis/fake_cli.py +0 -0
  126. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/fixtures/provider_lifecycle_schema_v1.sql +0 -0
  127. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/fixtures/routing_schema_v2_ca77600.sql +0 -0
  128. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/fixtures/routing_schema_v3_94eb333.sql +0 -0
  129. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/fixtures/routing_schema_v4_lifecycle_migration.sql +0 -0
  130. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_activation.py +0 -0
  131. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_activation_backstop.py +0 -0
  132. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_agent_hook_activation.py +0 -0
  133. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_agent_hooks.py +0 -0
  134. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_agent_settings.py +0 -0
  135. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_answer_contract.py +0 -0
  136. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_arrow_and_constructor_binding.py +0 -0
  137. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_atomic_write.py +0 -0
  138. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_bootstrap.py +0 -0
  139. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_cache_engine_identity.py +0 -0
  140. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_cache_file_set.py +0 -0
  141. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_cache_pruning.py +0 -0
  142. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_call_graph.py +0 -0
  143. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_channel_cli.py +0 -0
  144. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_channel_core.py +0 -0
  145. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_channel_lock.py +0 -0
  146. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_channel_mcp.py +0 -0
  147. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_channel_register.py +0 -0
  148. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_channel_report.py +0 -0
  149. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_channel_status.py +0 -0
  150. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_class_field_arrow_binding.py +0 -0
  151. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_cli_channel.py +0 -0
  152. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_cli_version.py +0 -0
  153. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_context.py +0 -0
  154. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_cwd_relative_config.py +0 -0
  155. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_daemon.py +0 -0
  156. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_daemon_activation.py +0 -0
  157. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_daemon_engine_staleness.py +0 -0
  158. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_daemon_health.py +0 -0
  159. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_daemon_health_activation.py +0 -0
  160. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_debt.py +0 -0
  161. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_destructured_binding.py +0 -0
  162. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_determinism.py +0 -0
  163. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_dispatch_evidence.py +0 -0
  164. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_doctor.py +0 -0
  165. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_doctor_agent_hooks.py +0 -0
  166. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_doctor_hooks.py +0 -0
  167. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_doctor_managed_docs.py +0 -0
  168. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_documentation.py +0 -0
  169. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_edit_apply.py +0 -0
  170. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_engine_identity.py +0 -0
  171. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_external_calls.py +0 -0
  172. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_git_security.py +0 -0
  173. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_go_rust.py +0 -0
  174. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_graph_io.py +0 -0
  175. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_graph_provider_isolation.py +0 -0
  176. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_hardening.py +0 -0
  177. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_health.py +0 -0
  178. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_hook_entry.py +0 -0
  179. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_hook_template.py +0 -0
  180. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_hookinstall.py +0 -0
  181. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_hookshim.py +0 -0
  182. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_html_security.py +0 -0
  183. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_incident_ledger.py +0 -0
  184. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_init.py +0 -0
  185. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_init_activation_doctrine.py +0 -0
  186. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_init_hooks.py +0 -0
  187. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_init_validation_exit.py +0 -0
  188. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_lifecycle_operator.py +0 -0
  189. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_listing.py +0 -0
  190. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_listing_surfaces.py +0 -0
  191. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_llm.py +0 -0
  192. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_mcp.py +0 -0
  193. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_method_dispatch_scope.py +0 -0
  194. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_monorepo.py +0 -0
  195. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_natural_query.py +0 -0
  196. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_node_identity.py +0 -0
  197. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_overlays.py +0 -0
  198. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_path_leak_fixture.py +0 -0
  199. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_probe_diagnostics.py +0 -0
  200. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_probe_workspace.py +0 -0
  201. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_provider_claude_probe.py +0 -0
  202. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_provider_codex_probe.py +0 -0
  203. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_provider_lifecycle.py +0 -0
  204. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_provider_lifecycle_service.py +0 -0
  205. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_provider_lifecycle_storage.py +0 -0
  206. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_provider_observer.py +0 -0
  207. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_provider_ollama_probe.py +0 -0
  208. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_provider_openrouter_probe.py +0 -0
  209. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_provider_probe_runner.py +0 -0
  210. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_public_surface.py +0 -0
  211. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_published_schemas.py +0 -0
  212. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_python_resolver.py +0 -0
  213. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_query_plan.py +0 -0
  214. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_reliability.py +0 -0
  215. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_replacement_audit.py +0 -0
  216. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_resolve.py +0 -0
  217. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_review.py +0 -0
  218. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_route_pool.py +0 -0
  219. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_approval.py +0 -0
  220. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_benchmark.py +0 -0
  221. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_classifier.py +0 -0
  222. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_claude_executor.py +0 -0
  223. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_cli.py +0 -0
  224. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_cli_contracts.py +0 -0
  225. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_cli_recovery.py +0 -0
  226. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_codex_executor.py +0 -0
  227. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_context.py +0 -0
  228. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_contracts.py +0 -0
  229. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_diff_policy.py +0 -0
  230. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_executor.py +0 -0
  231. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_openrouter_executor.py +0 -0
  232. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_policy.py +0 -0
  233. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_process_runner.py +0 -0
  234. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_profiles.py +0 -0
  235. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_registry.py +0 -0
  236. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_schema_validation.py +0 -0
  237. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_security.py +0 -0
  238. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_service.py +0 -0
  239. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_shadow.py +0 -0
  240. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_storage.py +0 -0
  241. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_telemetry.py +0 -0
  242. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_worktree.py +0 -0
  243. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_zai_executor.py +0 -0
  244. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_routing_zai_probe.py +0 -0
  245. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_savings.py +0 -0
  246. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_search.py +0 -0
  247. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_smoke.py +0 -0
  248. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_sqloracle.py +0 -0
  249. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_typescript_activation.py +0 -0
  250. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_typescript_resolver.py +0 -0
  251. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_usage_ledger.py +0 -0
  252. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_verify_artifact.py +0 -0
  253. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_verify_published_release.py +0 -0
  254. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_watch.py +0 -0
  255. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_windows_startup.py +0 -0
  256. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_windows_task.py +0 -0
  257. {graphite_code-0.5.1 → graphite_code-0.5.2}/tests/test_zai_edit.py +0 -0
@@ -12,6 +12,35 @@ machine-checkable identity; the version is for humans.
12
12
 
13
13
  [Keep a Changelog]: https://keepachangelog.com/en/1.1.0/
14
14
 
15
+ ## [0.5.2] — 2026-08-28
16
+
17
+ ### Fixed
18
+
19
+ **Force-stopping the daemon mid-build no longer wedges its successor for the
20
+ lock TTL** (#60). The build lock was held by the daemon on its child's behalf
21
+ (`ENV_LOCK_HELD` told the child not to contend), so `Stop-Process -Force` --
22
+ the documented post-install restart, which runs no `finally` -- left
23
+ `.build.lock` on disk naming a dead pid while the orphaned child kept writing
24
+ `graph-out/`. The successor could neither trust "holder dead" (the writer was
25
+ still alive) nor see "writer done", so it logged `build_skipped_locked` every
26
+ cycle and `daemon-health` warned for up to 600 s over a graph that was already
27
+ correct. A pid-liveness probe would not have helped and would have raced the
28
+ orphan in the very incident that filed the issue.
29
+
30
+ The lock now follows the writer: the daemon's child takes it itself, exactly
31
+ as a hook or manual `graphite build` does, and a killed daemon leaves no lock
32
+ at all. `build_skipped_locked` is therefore true for as long as a build is
33
+ really running and not a second longer. Two consequences are handled. The
34
+ child answers a refusal with exit status 75 (`EX_TEMPFAIL`) when the daemon
35
+ asks for it via `GRAPHITE_BUILD_LOCK_REPORT_REFUSAL`, so the cycle can book
36
+ "another builder owns the repo" as a skip and not a failed build; a human or a
37
+ hook still gets exit 0. And because the daemon's timeout kill now kills the
38
+ holder, the daemon releases the lock its dead child left -- by pid match only,
39
+ never by guessing. Under a Windows venv launcher `Popen.pid` is the launcher,
40
+ not the writer, so there the timeout path falls back to the TTL instead of
41
+ retrying next cycle: conservative by design, and noted here so nobody reads it
42
+ as a hang. `GRAPHITE_BUILD_LOCK_HELD` is gone; nothing outside the daemon set it.
43
+
15
44
  ## [0.5.1] — 2026-08-27
16
45
 
17
46
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: graphite-code
3
- Version: 0.5.1
3
+ Version: 0.5.2
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
@@ -38,4 +38,4 @@ __all__ = ["__version__"]
38
38
  #: Coarse and hand-maintained by construction. It is a release label, not
39
39
  #: evidence that a given fix is present -- to answer that, survey for the marker
40
40
  #: the fix introduced, or compare `graphite --version` fingerprints.
41
- __version__ = "0.5.1"
41
+ __version__ = "0.5.2"
@@ -9,6 +9,14 @@ Staleness is TTL-only, deliberately. `os.kill(pid, 0)` is the obvious liveness
9
9
  idiom and is WRONG here: on Windows, Python's `os.kill` ignores signal 0 and
10
10
  calls TerminateProcess, so the portable-looking probe would kill the very
11
11
  process it was checking.
12
+
13
+ The holder must be the process that writes (#60). The daemon used to take the
14
+ lock and hand its child an escape hatch; force-stopping the daemon then left
15
+ the record on disk with a dead pid while the orphaned child kept writing
16
+ graph-out/, and the successor could neither trust "holder dead" nor see
17
+ "writer done" -- so it skipped the repo for the whole TTL. Liveness probing
18
+ would not have helped: the recorded pid was dead while the writer was alive.
19
+ With the writer as holder, its death and its writes end together.
12
20
  """
13
21
  from __future__ import annotations
14
22
 
@@ -24,9 +32,13 @@ from typing import Any, Callable, Iterator
24
32
  # holding the lock past double the daemon's give-up point is dead by definition.
25
33
  DEFAULT_TTL_SECONDS = 600.0
26
34
 
27
- # Set by a parent that already holds the lock, so its child does not deadlock
28
- # against it. Mirrors the existing GRAPHITE_DAEMON_CHILD convention.
29
- ENV_LOCK_HELD = "GRAPHITE_BUILD_LOCK_HELD"
35
+ # A builder that cannot take the lock exits 0: a skipped build is a normal
36
+ # outcome for a human or a git hook, not a failure. The daemon needs more --
37
+ # "another builder owns the repo" must not be booked as a failed build -- so
38
+ # it sets this in its child's environment, and the child answers a refusal
39
+ # with REFUSED_EXIT_STATUS instead. Mirrors the GRAPHITE_DAEMON_CHILD convention.
40
+ ENV_REPORT_REFUSAL = "GRAPHITE_BUILD_LOCK_REPORT_REFUSAL"
41
+ REFUSED_EXIT_STATUS = 75 # sysexits.h EX_TEMPFAIL: try again later
30
42
 
31
43
 
32
44
  def lock_path(cache_dir: Path) -> Path:
@@ -50,6 +62,44 @@ def _is_stale(record: dict[str, Any] | None, now: float, ttl_seconds: float) ->
50
62
  return (now - float(started)) > ttl_seconds
51
63
 
52
64
 
65
+ def is_held(
66
+ cache_dir: Path,
67
+ *,
68
+ ttl_seconds: float = DEFAULT_TTL_SECONDS,
69
+ clock: Callable[[], float] = time.time,
70
+ ) -> bool:
71
+ """Advisory: would a builder be refused right now?
72
+
73
+ Reads only -- never acquires, never unlinks, never steals. The answer can
74
+ be stale by the time the caller acts on it, so correctness still rests on
75
+ the acquirer's own `build_lock`. The daemon uses it to avoid spawning a
76
+ child every cycle only to have it refused.
77
+ """
78
+ path = lock_path(cache_dir)
79
+ if not path.exists():
80
+ return False
81
+ return not _is_stale(_read_record(path), clock(), ttl_seconds)
82
+
83
+
84
+ def release_if_held_by(cache_dir: Path, *, pid: int) -> bool:
85
+ """Unlink the lock if its record names `pid`; True if it did.
86
+
87
+ For a parent that has just killed `pid`: the child died without its
88
+ `finally`, and no other process can hold a record carrying its pid, so this
89
+ releases on the dead child's behalf rather than stealing. It is NOT a
90
+ liveness probe -- the caller must already know the process is dead.
91
+ """
92
+ path = lock_path(cache_dir)
93
+ record = _read_record(path)
94
+ if record is None or record.get("pid") != pid:
95
+ return False
96
+ try:
97
+ path.unlink()
98
+ except OSError:
99
+ return False
100
+ return True
101
+
102
+
53
103
  def _create_exclusive(path: Path, now: float) -> bool:
54
104
  """Atomically create the lock file. False if someone else already has it."""
55
105
  try: