sidegraph 0.17.2__tar.gz → 0.17.3__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 (607) hide show
  1. {sidegraph-0.17.2 → sidegraph-0.17.3}/.github/workflows/ci.yml +1 -0
  2. {sidegraph-0.17.2 → sidegraph-0.17.3}/CHANGELOG.md +17 -0
  3. {sidegraph-0.17.2 → sidegraph-0.17.3}/CLAUDE.md +1 -1
  4. {sidegraph-0.17.2 → sidegraph-0.17.3}/PKG-INFO +1 -1
  5. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/guides/troubleshooting.md +5 -2
  6. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/integrations/graphify.md +14 -2
  7. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/reference/cli.md +1 -1
  8. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/reference/hooks.md +7 -0
  9. {sidegraph-0.17.2 → sidegraph-0.17.3}/package.json +1 -1
  10. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/.claude-plugin/plugin.json +1 -1
  11. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/.codex-plugin/plugin.json +1 -1
  12. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/.muse-plugin/plugin.json +1 -1
  13. {sidegraph-0.17.2 → sidegraph-0.17.3}/pyproject.toml +1 -1
  14. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/__init__.py +1 -1
  15. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/cli.py +12 -6
  16. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/githooks.py +152 -30
  17. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/integrity.py +27 -2
  18. sidegraph-0.17.3/tests/githooks_legacy_helper.py +46 -0
  19. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/githooks_support.py +0 -21
  20. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_githooks.py +2 -5
  21. sidegraph-0.17.3/tests/test_githooks_atomic_lock.py +246 -0
  22. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_integrity_refresh_hook.py +87 -0
  23. {sidegraph-0.17.2 → sidegraph-0.17.3}/uv.lock +1 -1
  24. {sidegraph-0.17.2 → sidegraph-0.17.3}/.agents/plugins/marketplace.json +0 -0
  25. {sidegraph-0.17.2 → sidegraph-0.17.3}/.claude-plugin/marketplace.json +0 -0
  26. {sidegraph-0.17.2 → sidegraph-0.17.3}/.github/CODEOWNERS +0 -0
  27. {sidegraph-0.17.2 → sidegraph-0.17.3}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  28. {sidegraph-0.17.2 → sidegraph-0.17.3}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  29. {sidegraph-0.17.2 → sidegraph-0.17.3}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  30. {sidegraph-0.17.2 → sidegraph-0.17.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  31. {sidegraph-0.17.2 → sidegraph-0.17.3}/.github/secret_scanning.yml +0 -0
  32. {sidegraph-0.17.2 → sidegraph-0.17.3}/.github/workflows/publish.yml +0 -0
  33. {sidegraph-0.17.2 → sidegraph-0.17.3}/.github/workflows/scorecard.yml +0 -0
  34. {sidegraph-0.17.2 → sidegraph-0.17.3}/.github/workflows/session-link-gate.yml +0 -0
  35. {sidegraph-0.17.2 → sidegraph-0.17.3}/.gitignore +0 -0
  36. {sidegraph-0.17.2 → sidegraph-0.17.3}/.gitleaks.toml +0 -0
  37. {sidegraph-0.17.2 → sidegraph-0.17.3}/.opencode/INSTALL.md +0 -0
  38. {sidegraph-0.17.2 → sidegraph-0.17.3}/.pre-commit-config.yaml +0 -0
  39. {sidegraph-0.17.2 → sidegraph-0.17.3}/.python-version +0 -0
  40. {sidegraph-0.17.2 → sidegraph-0.17.3}/AGENTS.md +0 -0
  41. {sidegraph-0.17.2 → sidegraph-0.17.3}/CODE_OF_CONDUCT.md +0 -0
  42. {sidegraph-0.17.2 → sidegraph-0.17.3}/CONTRIBUTING.md +0 -0
  43. {sidegraph-0.17.2 → sidegraph-0.17.3}/LICENSE +0 -0
  44. {sidegraph-0.17.2 → sidegraph-0.17.3}/README.md +0 -0
  45. {sidegraph-0.17.2 → sidegraph-0.17.3}/SECURITY.md +0 -0
  46. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/README.md +0 -0
  47. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/concepts/anchoring.md +0 -0
  48. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/concepts/data-model.md +0 -0
  49. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/concepts/decision-memory.md +0 -0
  50. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/concepts/mind-model.md +0 -0
  51. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/concepts/retrieval.md +0 -0
  52. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/getting-started/bootstrap.md +0 -0
  53. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/getting-started/claude-code-setup.md +0 -0
  54. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/getting-started/codex-setup.md +0 -0
  55. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/getting-started/installation.md +0 -0
  56. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/getting-started/quickstart.md +0 -0
  57. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/guides/adr-export.md +0 -0
  58. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/guides/capturing-decisions.md +0 -0
  59. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/guides/ci-cd-maintenance.md +0 -0
  60. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/guides/health-check.md +0 -0
  61. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/guides/migrating-to-0.14.md +0 -0
  62. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/guides/migrating-to-0.17.md +0 -0
  63. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/guides/naming-your-domains.md +0 -0
  64. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/guides/retrieval-in-sessions.md +0 -0
  65. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/guides/semantic-docs.md +0 -0
  66. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/guides/surviving-refactors.md +0 -0
  67. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/guides/team-workflow.md +0 -0
  68. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/guides/using-0.15.md +0 -0
  69. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/guides/using-0.16.md +0 -0
  70. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/guides/verifying-your-setup.md +0 -0
  71. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/integrations/claude-code.md +0 -0
  72. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/integrations/codex.md +0 -0
  73. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/integrations/grok.md +0 -0
  74. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/integrations/muse.md +0 -0
  75. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/integrations/opencode.md +0 -0
  76. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/llms.txt +0 -0
  77. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/pilot-kit/README.md +0 -0
  78. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/pilot-kit/corpus_fit.py +0 -0
  79. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/pilot-kit/judge-prompt.md +0 -0
  80. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/pilot-kit/questions-prompt.md +0 -0
  81. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/pilot-kit/rubric-template.md +0 -0
  82. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/reference/configuration.md +0 -0
  83. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/reference/git-bindings.md +0 -0
  84. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/reference/mcp-tools.md +0 -0
  85. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/reference/operations.md +0 -0
  86. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/reference/releasing.md +0 -0
  87. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/reference/stability.md +0 -0
  88. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/reference/store-format.md +0 -0
  89. {sidegraph-0.17.2 → sidegraph-0.17.3}/docs/whitepaper/index.md +0 -0
  90. {sidegraph-0.17.2 → sidegraph-0.17.3}/index.js +0 -0
  91. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/.mcp.json +0 -0
  92. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/codex/hooks.json +0 -0
  93. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/codex/mcp.json +0 -0
  94. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/hooks/hooks.json +0 -0
  95. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/check-plan/SKILL.md +0 -0
  96. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/check-plan/agents/openai.yaml +0 -0
  97. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/compact/SKILL.md +0 -0
  98. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/compact/agents/openai.yaml +0 -0
  99. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/explain-why/SKILL.md +0 -0
  100. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/explain-why/agents/openai.yaml +0 -0
  101. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/heal-anchors/SKILL.md +0 -0
  102. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/heal-anchors/agents/openai.yaml +0 -0
  103. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/health-check/SKILL.md +0 -0
  104. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/health-check/agents/openai.yaml +0 -0
  105. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/health-check/scripts/collect.py +0 -0
  106. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/import-adrs/SKILL.md +0 -0
  107. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/import-adrs/agents/openai.yaml +0 -0
  108. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/manage-domains/SKILL.md +0 -0
  109. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/manage-domains/agents/openai.yaml +0 -0
  110. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/name-domains/SKILL.md +0 -0
  111. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/name-domains/agents/openai.yaml +0 -0
  112. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/ratify-decisions/SKILL.md +0 -0
  113. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/ratify-decisions/agents/openai.yaml +0 -0
  114. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/record-decision/SKILL.md +0 -0
  115. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/record-decision/agents/openai.yaml +0 -0
  116. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/record-fact/SKILL.md +0 -0
  117. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/record-fact/agents/openai.yaml +0 -0
  118. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/setup/SKILL.md +0 -0
  119. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/setup/agents/openai.yaml +0 -0
  120. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/stats/SKILL.md +0 -0
  121. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/stats/agents/openai.yaml +0 -0
  122. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/triage-drift/SKILL.md +0 -0
  123. {sidegraph-0.17.2 → sidegraph-0.17.3}/plugin/sidegraph/skills/triage-drift/agents/openai.yaml +0 -0
  124. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/adr.py +0 -0
  125. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/adr_cli.py +0 -0
  126. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/adr_files.py +0 -0
  127. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/adr_identity.py +0 -0
  128. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/anchoring.py +0 -0
  129. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/bash_paths.py +0 -0
  130. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/bootstrap/__init__.py +0 -0
  131. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/bootstrap/apply.py +0 -0
  132. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/bootstrap/catalog.py +0 -0
  133. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/bootstrap/cli.py +0 -0
  134. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/bootstrap/integrations.py +0 -0
  135. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/bootstrap/model.py +0 -0
  136. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/bootstrap/planner.py +0 -0
  137. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/bootstrap/proof.py +0 -0
  138. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/bootstrap/review.py +0 -0
  139. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/bootstrap/scan.py +0 -0
  140. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/capture.py +0 -0
  141. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/config.py +0 -0
  142. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/delivery.py +0 -0
  143. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/doc_import.py +0 -0
  144. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/doctor.py +0 -0
  145. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/domain_snapshot.py +0 -0
  146. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/domains.py +0 -0
  147. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/drift_checks.py +0 -0
  148. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/engine/__init__.py +0 -0
  149. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/engine/rationale.py +0 -0
  150. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/engine/reader.py +0 -0
  151. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/freshness.py +0 -0
  152. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/gitenv.py +0 -0
  153. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/gitio.py +0 -0
  154. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/host/__init__.py +0 -0
  155. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/host/claude_settings.py +0 -0
  156. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/host/diagnostics.py +0 -0
  157. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/host/hooks.py +0 -0
  158. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/host/launch.py +0 -0
  159. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/host/mcp_entry.py +0 -0
  160. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/host/session_identity.py +0 -0
  161. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/host/subagent.py +0 -0
  162. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/hot_index.py +0 -0
  163. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/importer.py +0 -0
  164. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/input_limits.py +0 -0
  165. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/maintenance.py +0 -0
  166. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/maintenance_clock.py +0 -0
  167. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/migrate.py +0 -0
  168. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/nearest_anchored.py +0 -0
  169. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/okf.py +0 -0
  170. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/profiles.py +0 -0
  171. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/redaction_scan.py +0 -0
  172. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/redaction_tokens.py +0 -0
  173. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/retrieval.py +0 -0
  174. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/schema.py +0 -0
  175. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/seed_ladder.py +0 -0
  176. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/server.py +0 -0
  177. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/stats/__init__.py +0 -0
  178. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/stats/model.py +0 -0
  179. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/stats/render.py +0 -0
  180. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/store.py +0 -0
  181. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/store_format.py +0 -0
  182. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/store_layout.py +0 -0
  183. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/store_rules.py +0 -0
  184. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/store_view.py +0 -0
  185. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/sync.py +0 -0
  186. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/text_paths.py +0 -0
  187. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/tool_rate_limit.py +0 -0
  188. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/validation_errors.py +0 -0
  189. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/verify.py +0 -0
  190. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/viz/__init__.py +0 -0
  191. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/viz/assets/vis-network.min.js +0 -0
  192. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/viz/model.py +0 -0
  193. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/viz/render.py +0 -0
  194. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/viz/template.html +0 -0
  195. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/worktree_seeds.py +0 -0
  196. {sidegraph-0.17.2 → sidegraph-0.17.3}/src/sidegraph/write_boundary.py +0 -0
  197. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/__init__.py +0 -0
  198. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/bounded_listings_support.py +0 -0
  199. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/conftest.py +0 -0
  200. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/drift_checks_support.py +0 -0
  201. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/auto_policy/parity_goldens.json +0 -0
  202. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/bitfinex_slice.json +0 -0
  203. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/bootstrap/flows/bmad/_bmad-output/planning-artifacts/architecture/cache/ARCHITECTURE-SPINE.md +0 -0
  204. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/bootstrap/flows/generic-adr/docs/adr/001-retry.md +0 -0
  205. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/bootstrap/flows/genkovich-sdd/docs/features/cache/adr/001.md +0 -0
  206. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/bootstrap/flows/spec-kit/specs/cache/plan.md +0 -0
  207. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/bootstrap/flows/superpowers/docs/superpowers/specs/2026-07-01-cache-design.md +0 -0
  208. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/bootstrap/graph.json +0 -0
  209. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/bootstrap/hosts/claude/.claude/settings.json +0 -0
  210. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/bootstrap/hosts/claude/.mcp.json +0 -0
  211. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/bootstrap/hosts/codex/.codex/config.toml +0 -0
  212. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/bootstrap/hosts/codex/.codex/hooks/hooks.json +0 -0
  213. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/bootstrap/scan/docs/adr/001-safe.md +0 -0
  214. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/corpus/PUBLIC_CORPORA.txt +0 -0
  215. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/corpus/openspec/_provenance.json +0 -0
  216. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/corpus/openspec/graph.json +0 -0
  217. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/corpus/self-corpus/_provenance.json +0 -0
  218. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/corpus/self-corpus/expected/retrieval-engine-reader.md +0 -0
  219. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/corpus/self-corpus/graph.json +0 -0
  220. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/adr/0001-use-sessions.md +0 -0
  221. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/bmad/ARCHITECTURE-SPINE.md +0 -0
  222. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/bmad/tree/_bmad-output/planning-artifacts/architecture/architecture-payments-2026-07-30/.memlog.md +0 -0
  223. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/bmad/tree/_bmad-output/planning-artifacts/architecture/architecture-payments-2026-07-30/ARCHITECTURE-SPINE.md +0 -0
  224. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/bmad/tree/_bmad-output/planning-artifacts/prds/prd-payments-2026-07-30/prd.md +0 -0
  225. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/genkovich/0001-queue-backpressure.md +0 -0
  226. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/genkovich/sad.md +0 -0
  227. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/genkovich/tree/docs/features/payments/adr/0001-queue-backpressure.md +0 -0
  228. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/genkovich/tree/docs/features/payments/sad.md +0 -0
  229. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/genkovich/tree/docs/features/payments/spec.md +0 -0
  230. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/openspec/design-empty.md +0 -0
  231. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/openspec/design-h3-split.md +0 -0
  232. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/openspec/design-with-summary.md +0 -0
  233. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/openspec/design.md +0 -0
  234. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/openspec/proposal-alternatives.md +0 -0
  235. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/openspec/proposal.md +0 -0
  236. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/openspec/tree/openspec/changes/add-thing/design.md +0 -0
  237. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/openspec/tree/openspec/changes/add-thing/proposal.md +0 -0
  238. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/openspec/tree/openspec/changes/add-thing/specs/cap/spec.md +0 -0
  239. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/openspec/tree/openspec/changes/add-thing/tasks.md +0 -0
  240. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/openspec/tree/openspec/changes/archive/2026-01-01-add-thing/design.md +0 -0
  241. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/openspec/tree/openspec/changes/archive/2026-01-01-add-thing/proposal.md +0 -0
  242. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/openspec/tree/openspec/config.yaml +0 -0
  243. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/openspec/tree/openspec/specs/cap/spec.md +0 -0
  244. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/spec-kit/plan.md +0 -0
  245. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/spec-kit/tree/specs/003-payment-retries/plan.md +0 -0
  246. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/spec-kit/tree/specs/003-payment-retries/spec.md +0 -0
  247. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/superpowers/2026-07-01-example-design.md +0 -0
  248. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/flows/superpowers/2026-07-01-thin-design.md +0 -0
  249. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/member_names_python_graph.json +0 -0
  250. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/member_names_swift_graph.json +0 -0
  251. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/fixtures/mini_graph.json +0 -0
  252. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/graph_fixtures.py +0 -0
  253. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/graph_repo_support.py +0 -0
  254. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/membership_support.py +0 -0
  255. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/moved_adoption_support.py +0 -0
  256. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/opencode_plugin_runner.mjs +0 -0
  257. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/snapshot_support.py +0 -0
  258. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/store_format2_support.py +0 -0
  259. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/store_non_file_support.py +0 -0
  260. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/store_record_support.py +0 -0
  261. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_adr.py +0 -0
  262. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_adr_files.py +0 -0
  263. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_adr_identity.py +0 -0
  264. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_agent_brief_manifests.py +0 -0
  265. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_anchor_descriptor.py +0 -0
  266. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_anchor_paths.py +0 -0
  267. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_anchoring.py +0 -0
  268. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_anchoring_mapping_refresh.py +0 -0
  269. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_anchoring_membership.py +0 -0
  270. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_bash_paths.py +0 -0
  271. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_batch_panel_fixes.py +0 -0
  272. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_bootstrap_apply.py +0 -0
  273. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_bootstrap_catalog.py +0 -0
  274. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_bootstrap_cli.py +0 -0
  275. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_bootstrap_descriptor_cleanup.py +0 -0
  276. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_bootstrap_integrations.py +0 -0
  277. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_bootstrap_model.py +0 -0
  278. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_bootstrap_planner.py +0 -0
  279. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_bootstrap_proof.py +0 -0
  280. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_bootstrap_review.py +0 -0
  281. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_bootstrap_scan.py +0 -0
  282. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_bootstrap_scan_regular.py +0 -0
  283. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_bootstrap_unanchored.py +0 -0
  284. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_bounded_listings.py +0 -0
  285. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_capture_auto_accept.py +0 -0
  286. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_capture_facts.py +0 -0
  287. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_capture_initiative.py +0 -0
  288. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_capture_integration.py +0 -0
  289. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_capture_neighbors.py +0 -0
  290. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_capture_propose.py +0 -0
  291. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_capture_redact.py +0 -0
  292. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_cli_adr.py +0 -0
  293. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_cli_compact.py +0 -0
  294. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_cli_compact_format2.py +0 -0
  295. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_cli_doctor.py +0 -0
  296. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_cli_domains.py +0 -0
  297. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_cli_domains_snapshot.py +0 -0
  298. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_cli_graph_paths.py +0 -0
  299. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_cli_import.py +0 -0
  300. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_cli_init.py +0 -0
  301. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_cli_init_hooks.py +0 -0
  302. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_cli_okf.py +0 -0
  303. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_cli_ratify.py +0 -0
  304. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_cli_ratify_facts.py +0 -0
  305. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_cli_stats.py +0 -0
  306. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_cli_store_precedence.py +0 -0
  307. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_cli_sync.py +0 -0
  308. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_cli_sync_check.py +0 -0
  309. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_cli_unreadable_paths.py +0 -0
  310. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_cli_viz.py +0 -0
  311. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_codex_plugin.py +0 -0
  312. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_command_windows.py +0 -0
  313. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_community_baseline.py +0 -0
  314. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_compact_skill.py +0 -0
  315. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_config.py +0 -0
  316. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_config_ancestor_lookup.py +0 -0
  317. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_config_borrowed_graph.py +0 -0
  318. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_corpus_expected_renders.py +0 -0
  319. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_corpus_self_corpus.py +0 -0
  320. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_coverage_telemetry_e2e.py +0 -0
  321. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_delivery_evidence.py +0 -0
  322. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_delivery_stats.py +0 -0
  323. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_delivery_surfaces.py +0 -0
  324. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_digest_integrity.py +0 -0
  325. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_doc_corpus_integration.py +0 -0
  326. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_doc_import.py +0 -0
  327. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_docs_claims.py +0 -0
  328. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_doctor.py +0 -0
  329. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_doctor_auto_share.py +0 -0
  330. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_doctor_code_drift.py +0 -0
  331. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_doctor_dangling_supports.py +0 -0
  332. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_doctor_duplicate_entity.py +0 -0
  333. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_doctor_graph_root_mismatch.py +0 -0
  334. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_doctor_graph_stale.py +0 -0
  335. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_doctor_integrity.py +0 -0
  336. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_doctor_never_surfaced.py +0 -0
  337. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_doctor_record_identity.py +0 -0
  338. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_doctor_scan.py +0 -0
  339. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_doctor_stale_instructions.py +0 -0
  340. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_doctor_unhashable_payload.py +0 -0
  341. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_doctor_unratified_accept.py +0 -0
  342. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_domain_seed_names.py +0 -0
  343. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_domain_snapshot_format.py +0 -0
  344. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_domain_snapshot_notices.py +0 -0
  345. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_domains.py +0 -0
  346. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_drift_checks.py +0 -0
  347. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_engine_build.py +0 -0
  348. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_entity_duplicate_resolution.py +0 -0
  349. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_entity_get_or_create_race.py +0 -0
  350. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_entity_twins.py +0 -0
  351. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_entry_point_arguments.py +0 -0
  352. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_env_isolation.py +0 -0
  353. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_fact_reachability_gate.py +0 -0
  354. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_file_bound_retrieval.py +0 -0
  355. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_flow_profile.py +0 -0
  356. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_format2_notices.py +0 -0
  357. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_git_bindings_blame.py +0 -0
  358. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_git_bindings_hook.py +0 -0
  359. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_git_bindings_provenance.py +0 -0
  360. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_git_env.py +0 -0
  361. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_git_output_utf8.py +0 -0
  362. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_graph_freshness.py +0 -0
  363. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_graphify_pin.py +0 -0
  364. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_health_check_skill.py +0 -0
  365. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_hook_body_compaction.py +0 -0
  366. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_hook_contention.py +0 -0
  367. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_hook_failure_diagnostics.py +0 -0
  368. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_hook_payload_encoding.py +0 -0
  369. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_hook_showings.py +0 -0
  370. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_hook_spawn_guard.py +0 -0
  371. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_host_agent_brief.py +0 -0
  372. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_host_agent_state.py +0 -0
  373. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_host_borrowed_graph.py +0 -0
  374. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_host_claude_settings.py +0 -0
  375. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_host_drift_nudge.py +0 -0
  376. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_host_integrity.py +0 -0
  377. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_host_launch.py +0 -0
  378. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_host_pretool.py +0 -0
  379. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_host_pretool_records.py +0 -0
  380. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_host_session_key.py +0 -0
  381. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_host_session_start.py +0 -0
  382. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_host_session_start_concurrent.py +0 -0
  383. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_host_stop.py +0 -0
  384. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_host_subagent_start.py +0 -0
  385. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_host_subdirectory_launch.py +0 -0
  386. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_host_touch_events.py +0 -0
  387. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_host_wiring_checks.py +0 -0
  388. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_hot_index.py +0 -0
  389. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_hot_index_facts.py +0 -0
  390. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_hot_index_hook.py +0 -0
  391. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_hot_path_imports.py +0 -0
  392. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_import_dry_run_preview.py +0 -0
  393. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_importer.py +0 -0
  394. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_index_journal_mode.py +0 -0
  395. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_integrity.py +0 -0
  396. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_integrity_engine_version.py +0 -0
  397. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_integrity_replay.py +0 -0
  398. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_integrity_stranded.py +0 -0
  399. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_lazy_root.py +0 -0
  400. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_maintenance_clock.py +0 -0
  401. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_maintenance_dry_run_parity.py +0 -0
  402. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_maintenance_plan.py +0 -0
  403. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_maintenance_publish.py +0 -0
  404. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_mcp_entry_ratify_policy.py +0 -0
  405. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_mcp_entry_repo_root.py +0 -0
  406. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_member_anchor_wiring.py +0 -0
  407. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_merge_matrix_format2.py +0 -0
  408. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_migrate.py +0 -0
  409. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_mind_model_additive_fields.py +0 -0
  410. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_muse_plugin.py +0 -0
  411. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_nearest_anchored.py +0 -0
  412. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_okf.py +0 -0
  413. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_opencode_plugin.py +0 -0
  414. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_openspec_profile.py +0 -0
  415. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_pathless_descriptor_adoption.py +0 -0
  416. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_pilot_kit_corpus_fit.py +0 -0
  417. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_portable_json.py +0 -0
  418. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_private_tests_isolation.py +0 -0
  419. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_proposal_lifecycle.py +0 -0
  420. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_public_hook_launch.py +0 -0
  421. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_ratify_listing_gone_dark.py +0 -0
  422. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_ratify_policy.py +0 -0
  423. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_rationale_compatibility.py +0 -0
  424. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_reader_community_labels.py +0 -0
  425. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_reader_doc_nodes.py +0 -0
  426. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_reader_engine_version.py +0 -0
  427. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_reader_file_labels.py +0 -0
  428. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_reader_graph.py +0 -0
  429. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_reader_integration.py +0 -0
  430. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_reader_label_conventions.py +0 -0
  431. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_reader_member_name_lookup.py +0 -0
  432. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_reader_member_names.py +0 -0
  433. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_reader_nodes_in_file.py +0 -0
  434. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_reader_perf.py +0 -0
  435. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_reader_rationale.py +0 -0
  436. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_reader_read.py +0 -0
  437. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_reader_resolve.py +0 -0
  438. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_reader_stub_collapse.py +0 -0
  439. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_reader_subdir_mismatch.py +0 -0
  440. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_reader_type_split.py +0 -0
  441. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_reader_version.py +0 -0
  442. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_readers_format2.py +0 -0
  443. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_rebuild_instruction.py +0 -0
  444. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_redaction_scan.py +0 -0
  445. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_redaction_seeded.py +0 -0
  446. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_redaction_tokens.py +0 -0
  447. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_repo_relative_paths_portable.py +0 -0
  448. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_repoint_integration.py +0 -0
  449. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_retrieval_budget_counters.py +0 -0
  450. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_retrieval_context.py +0 -0
  451. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_retrieval_drift_marker.py +0 -0
  452. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_retrieval_drilldown.py +0 -0
  453. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_retrieval_facts.py +0 -0
  454. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_retrieval_guessed.py +0 -0
  455. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_retrieval_ids.py +0 -0
  456. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_retrieval_integration.py +0 -0
  457. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_retrieval_membership_generations.py +0 -0
  458. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_retrieval_membership_index.py +0 -0
  459. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_retrieval_rank.py +0 -0
  460. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_retrieval_seeds.py +0 -0
  461. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_retrieval_shown_ids.py +0 -0
  462. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_retrieval_terminal_evidence.py +0 -0
  463. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_retrieval_thin_tools.py +0 -0
  464. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_retrieval_toc.py +0 -0
  465. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_retrieval_toptier.py +0 -0
  466. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_retrieval_unratified.py +0 -0
  467. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_schema_descriptor.py +0 -0
  468. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_schema_domain.py +0 -0
  469. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_schema_fact.py +0 -0
  470. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_security_capture_diagnostics.py +0 -0
  471. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_security_capture_limits.py +0 -0
  472. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_security_cli_diagnostics.py +0 -0
  473. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_security_doc_limits.py +0 -0
  474. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_security_input_limits.py +0 -0
  475. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_security_mcp_write_diagnostics.py +0 -0
  476. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_security_numeric_enums.py +0 -0
  477. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_security_python_shapes.py +0 -0
  478. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_security_redaction_cursor.py +0 -0
  479. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_security_validation_errors.py +0 -0
  480. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_security_write_boundary.py +0 -0
  481. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_seed_ladder.py +0 -0
  482. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_seed_paths_portable.py +0 -0
  483. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_add_anchors.py +0 -0
  484. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_anchoring.py +0 -0
  485. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_annotations.py +0 -0
  486. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_argument_names.py +0 -0
  487. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_borrowed_graph.py +0 -0
  488. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_capture.py +0 -0
  489. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_commit_hint.py +0 -0
  490. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_coverage_events.py +0 -0
  491. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_decision_listing.py +0 -0
  492. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_domains.py +0 -0
  493. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_drilldown.py +0 -0
  494. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_facts.py +0 -0
  495. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_find_entity.py +0 -0
  496. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_get_store.py +0 -0
  497. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_get_task_context.py +0 -0
  498. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_graph_freshness.py +0 -0
  499. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_graph_path.py +0 -0
  500. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_import.py +0 -0
  501. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_invocation_policy.py +0 -0
  502. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_mcp_smoke.py +0 -0
  503. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_nearest_anchored.py +0 -0
  504. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_prewrite_anchor_validation.py +0 -0
  505. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_ratify_facts.py +0 -0
  506. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_record_types.py +0 -0
  507. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_redaction.py +0 -0
  508. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_refresh.py +0 -0
  509. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_render_events.py +0 -0
  510. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_result_contract.py +0 -0
  511. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_supersede.py +0 -0
  512. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_sync_anchors.py +0 -0
  513. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_telemetry.py +0 -0
  514. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_thin_tools.py +0 -0
  515. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_tolerant_seeds.py +0 -0
  516. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_server_verify.py +0 -0
  517. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_session_attribution.py +0 -0
  518. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_skill_frontmatter.py +0 -0
  519. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_stats_graph.py +0 -0
  520. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_stats_health.py +0 -0
  521. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_stats_model.py +0 -0
  522. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_stats_render.py +0 -0
  523. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_stats_skill.py +0 -0
  524. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_stats_snapshot.py +0 -0
  525. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_stop_hot_path.py +0 -0
  526. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_stop_rearm.py +0 -0
  527. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store.py +0 -0
  528. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_agent_state.py +0 -0
  529. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_atomic_write_tmp_names.py +0 -0
  530. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_bad_file.py +0 -0
  531. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_binding_index.py +0 -0
  532. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_canonical_stat.py +0 -0
  533. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_compact.py +0 -0
  534. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_coverage_telemetry.py +0 -0
  535. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_cross_process_mutations.py +0 -0
  536. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_derived_community.py +0 -0
  537. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_domain_members.py +0 -0
  538. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_domains.py +0 -0
  539. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_entities.py +0 -0
  540. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_fact_cascade.py +0 -0
  541. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_facts.py +0 -0
  542. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_file_anchor_names.py +0 -0
  543. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_format.py +0 -0
  544. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_format2_load.py +0 -0
  545. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_format2_marker.py +0 -0
  546. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_format2_writes.py +0 -0
  547. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_git_root_symlink.py +0 -0
  548. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_inventory.py +0 -0
  549. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_meta.py +0 -0
  550. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_migration.py +0 -0
  551. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_mutation_guard.py +0 -0
  552. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_mutation_immediate.py +0 -0
  553. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_non_file_entries.py +0 -0
  554. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_opens_with_merged_duplicate.py +0 -0
  555. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_persistence.py +0 -0
  556. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_ratification.py +0 -0
  557. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_rebuild_atomicity.py +0 -0
  558. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_record_identity.py +0 -0
  559. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_record_types.py +0 -0
  560. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_render_events.py +0 -0
  561. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_requirement.py +0 -0
  562. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_retrieval.py +0 -0
  563. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_rules.py +0 -0
  564. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_slug_conflicts.py +0 -0
  565. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_snapshot_wiring.py +0 -0
  566. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_stamping_marker.py +0 -0
  567. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_symlinks.py +0 -0
  568. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_telemetry.py +0 -0
  569. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_threading.py +0 -0
  570. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_tmp_sweep_age.py +0 -0
  571. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_store_update_meta_if.py +0 -0
  572. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_subagent_start_manifests.py +0 -0
  573. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_subprocess_text_encoding.py +0 -0
  574. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_sync_canonical_writes.py +0 -0
  575. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_sync_clean.py +0 -0
  576. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_sync_domains.py +0 -0
  577. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_sync_drift_cache.py +0 -0
  578. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_sync_fresh_clone.py +0 -0
  579. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_sync_integration.py +0 -0
  580. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_sync_membership.py +0 -0
  581. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_sync_moved_adoption.py +0 -0
  582. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_sync_rebind.py +0 -0
  583. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_sync_repoint.py +0 -0
  584. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_sync_report_has_findings.py +0 -0
  585. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_sync_run.py +0 -0
  586. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_sync_snapshot_membership.py +0 -0
  587. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_sync_toc_cache.py +0 -0
  588. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_sync_volatile_heal.py +0 -0
  589. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_sync_wiring.py +0 -0
  590. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_telemetry_retention.py +0 -0
  591. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_text_paths.py +0 -0
  592. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_verify_domain_snapshot.py +0 -0
  593. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_verify_format2_against.py +0 -0
  594. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_verify_format2_snapshot.py +0 -0
  595. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_verify_git_framing.py +0 -0
  596. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_verify_git_revisions.py +0 -0
  597. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_verify_git_stdin.py +0 -0
  598. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_verify_snapshot.py +0 -0
  599. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_verify_transitions.py +0 -0
  600. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_viz_asset_packaged.py +0 -0
  601. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_viz_model.py +0 -0
  602. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_viz_render_html.py +0 -0
  603. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_viz_render_json.py +0 -0
  604. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/test_worktree_seeds.py +0 -0
  605. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/wheel_server_trace.py +0 -0
  606. {sidegraph-0.17.2 → sidegraph-0.17.3}/tests/wheel_smoke.py +0 -0
  607. {sidegraph-0.17.2 → sidegraph-0.17.3}/tools/check_no_session_links.py +0 -0
@@ -201,6 +201,7 @@ jobs:
201
201
  tests/test_hot_index_facts.py `
202
202
  tests/test_delivery_evidence.py tests/test_delivery_stats.py tests/test_delivery_surfaces.py `
203
203
  tests/test_git_output_utf8.py `
204
+ tests/test_githooks_atomic_lock.py::test_windows_pid_probe_is_never_a_signal_from_python `
204
205
  tests/test_health_check_skill.py `
205
206
  $privateTests
206
207
  if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
@@ -7,6 +7,23 @@ interfaces, exactly, and what each one promises: [`docs/reference/stability.md`]
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.17.3] — 2026-10-26
11
+
12
+ ### Fixed
13
+
14
+ - Graph refresh hooks acquire both the runner lock and stale-breaker gate with exclusive
15
+ shell PID files. Concurrent hooks remain serialized on systems whose external `mkdir`
16
+ can report success to two racing callers, while live legacy directory locks are respected.
17
+ - Installer cleanup coordinates with the same breaker gate and preserves a lock whose owner
18
+ changed during its PID check. Optional native-lock failures no longer prevent publishing
19
+ an updated helper, and a failed gate PID write no longer leaves a permanent gate.
20
+ - Doctor detects the complete older directory-lock helper; plain `sidegraph-init` repairs
21
+ it while keeping hook bytes and the recorded choice. Automatic detection conservatively
22
+ preserves unfamiliar scripts and quoted examples. Run `sidegraph-init` after upgrading
23
+ in each clone that uses the old helper.
24
+ - Native Windows installer cleanup leaves PID-bearing locks untouched, instead of using
25
+ Python signal probes on process ids from a potentially different POSIX-shell namespace.
26
+
10
27
  ## [0.17.2] — 2026-10-25
11
28
 
12
29
  ### Fixed
@@ -13,7 +13,7 @@ task-aware retrieval + decision memory riding on the engine's entity graph.
13
13
  continuing participant in the project. A core feature must preserve, organize, or deliver
14
14
  accumulated project understanding. Document search by itself is not the product.
15
15
 
16
- Sidegraph is pre-1.0 (v0.17.2) — the full loop (capture, ratification, mistakes-first
16
+ Sidegraph is pre-1.0 (v0.17.3) — the full loop (capture, ratification, mistakes-first
17
17
  retrieval, refactor-surviving re-anchoring, semantic docs layer, mind-model domains, and the
18
18
  facts evidence layer) ships and is exercised end-to-end, but interfaces may still move before
19
19
  a stable release. See
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: sidegraph
3
- Version: 0.17.2
3
+ Version: 0.17.3
4
4
  Summary: Own the memory, rent the graph — a durable decision/lessons layer as a sidecar over a code-graph engine.
5
5
  Project-URL: Homepage, https://github.com/SantyagoSeaman/sidegraph
6
6
  Project-URL: Repository, https://github.com/SantyagoSeaman/sidegraph
@@ -222,8 +222,11 @@ entity file is skipped, an anchor to the same name mints a second entity; once t
222
222
 
223
223
  ### refresh-helper-outdated
224
224
 
225
- A git hook runs a graph refresh helper that starts Graphify without `GRAPHIFY_NO_AUTO_REFRESH=1`
226
- (an older Sidegraph wrote it). Graphify 0.9.87 and later refresh the agent skills they installed,
225
+ A git hook runs an older graph refresh helper that uses directory-based locking, or starts
226
+ Graphify without `GRAPHIFY_NO_AUTO_REFRESH=1`.
227
+ The directory lock can admit overlapping rebuilds with uutils `mkdir`; the replacement uses
228
+ exclusive PID files for both the runner lock and the stale-breaker gate.
229
+ Graphify 0.9.87 and later refresh the agent skills they installed,
227
230
  such as `~/.claude/skills/graphify/`, on the first run after a Graphify upgrade unless that
228
231
  variable is set. One hook that runs the helper is enough for the check to fire, and a recorded "no"
229
232
  does not silence it. Run `sidegraph-init` with no flag: it rewrites the helper and keeps your
@@ -242,13 +242,25 @@ the `/graphify` skill's `--update` form).
242
242
  [above](#linked-worktrees-read-the-main-checkouts-graph)), so a commit in one does nothing.
243
243
  Rebuilding there as well costs about 18 MB and 43 s for a cold build on one field corpus with
244
244
  25 worktrees, and agents commit in worktrees constantly.
245
- - **In the background, one at a time.** Git never waits for the rebuild. A lock directory keeps
245
+ - **In the background, one at a time.** Git never waits for the rebuild. An exclusively created PID file keeps
246
246
  one rebuild running; a hook that fires while it runs makes it go once more when it finishes, so
247
247
  the graph never settles on a tree older than the last request. A lock left by a job that died
248
248
  is detected and cleared: by its pid, or, when the job died before writing one (a full disk, a
249
- quota), by its age, ten minutes. `sidegraph-init --hooks` clears such a lock at once. One risk
249
+ quota), by its age, ten minutes. Re-running `sidegraph-init --hooks` refreshes the helper and clears a dead lock;
250
+ a fresh lock without a PID is kept until the ten-minute grace period expires. One risk
250
251
  remains: after a reboot a pid can belong to a live, unrelated process, and the hook then waits
251
252
  until that process exits.
253
+ Plain `sidegraph-init` also repairs an outdated helper in use, keeping the recorded choice.
254
+ Older helpers used lock directories; the new helper respects a live old runner and can
255
+ recover its stale directory. Both the lock and the stale-breaker gate now use shell
256
+ exclusive creation, so locking does not depend on external `mkdir`.
257
+ Installer cleanup takes the same breaker gate before removing a dead runner lock. A small
258
+ native OS lock on `sidegraph-graph-refresh.repair` serializes installers while they recover
259
+ a dead gate; the coordination file stays in the common git directory to keep a stable inode.
260
+ Where native locks are unsupported, helper publication still proceeds and optional installer
261
+ cleanup is skipped. On Windows, PID-bearing locks are left to the POSIX helper to recover,
262
+ because its PIDs need not be native Windows PIDs. Installing or repairing a helper does not
263
+ itself start a rebuild.
252
264
  - **Where the output goes.** The last rebuild's output is `sidegraph-graph-refresh.log` in the
253
265
  common git directory (`.git/` in a normal clone).
254
266
  - **No `sidegraph-sync` in the hook.** The next memory call notices the new graph version and
@@ -1562,7 +1562,7 @@ plain-text line):
1562
1562
  | `orphaned-record` | an open (`proposed`/`accepted`) decision or fact whose every Tier-2 (leaf) anchor is `orphaned`, so retrieval reaches it only through its file or domain; one finding per record, at its canonical file, naming how many leaf anchors it has. Statuses come from `index.db` and read "as of last sync", so the check is skipped while the statuses are not computed for the current graph (see *Skipped checks* below; run `sidegraph-sync`). It is the record-level view of `orphaned-binding` (any record it flags has orphaned bindings, which that code already reports), so it adds no new reason for `--check` to exit `2`. The fix is to rebuild a stale graph, then `sidegraph-sync`; otherwise re-anchor the record with `add_anchors` or the `heal-anchors` skill, or supersede it if the code is gone. Needs a usable `index.db` |
1563
1563
  | `store-uncommitted` | store files nobody committed for at least 24 hours: records, bindings, entities, an archive segment or the root files (`format`, `.gitignore`, `stamping_live_since`) that `git status` shows as new, modified or deleted. One finding, at the store path, naming how many files there are, how old the oldest is and what kinds they are (a new `archive/` segment beside deleted records reads as "a compaction"). A new record or entity file is aged by its ULID name, which a `git stash` cannot rewrite; a modified file, or anything under `bindings/`, by its modification time; a deleted file by its subdirectory's, a lower bound. Entries fully staged in the index are not counted, so a `--check` run in a pre-commit hook does not block the commit that fixes the problem. **`--check` exits `2` on it.** The fix is to commit the store in a pull request. Silent, and not run, outside a git repository, with no `git`, or when git is slower than 2 seconds |
1564
1564
  | `plugin-off-in-subdirectories` | Claude Code sessions started below the repository root run without the Sidegraph plugin. Claude Code reads the project settings (`.claude/settings.json`) of the launch directory only, and the repository root's `.claude/settings.local.json` for every launch inside the repository; `enabledPlugins` merges key by key (user settings, then project, then local). Two problems, reported as up to two findings: (a) the plugin is enabled only in the root's `.claude/settings.json`, so every directory below the root without its own enabling settings runs without it (reported at that file); (b) nested directories with a `.claude/settings.json` or `.claude/settings.local.json` of their own that sets `enabledPlugins` and whose merge leaves the plugin off (reported at the first such directory, naming how many there are; a nested settings file without `enabledPlugins` cannot change the merge, so it is not counted). The walk covers every directory that holds a tracked file (one `git ls-files` over the index, a few tens of milliseconds on a large repository) and looks for the two settings files in each directly, so a repository that ignores `.claude/` is covered. User settings are read from `$CLAUDE_CONFIG_DIR/settings.json` when that variable is set and non-empty, else from `~/.claude/settings.json`. Silent when the plugin is not enabled for a launch at the root at all, and not run outside a git repository, with no `git`, or when `git ls-files` takes longer than 5 seconds. **`--check` exits `2` on it**, like any finding; enabling the plugin at user scope or in the root's `.claude/settings.local.json` clears it |
1565
- | `refresh-helper-outdated` | a git hook runs a graph refresh helper whose Graphify line lacks `GRAPHIFY_NO_AUTO_REFRESH=1` (an older Sidegraph wrote it), so Graphify 0.9.87 and later can rewrite the agent skills they installed on the first rebuild after an upgrade. Advisory, per clone (the helper lives in `.git/hooks`); **`--check` exits `2` on it**, like any finding. One hook that runs the helper is enough, and a recorded decline does not silence it. The fix is `sidegraph-init` (no flag), which refreshes the helper and keeps a recorded hook choice. Silent outside a git repository, with no helper, or when no hook runs it |
1565
+ | `refresh-helper-outdated` | a git hook runs an older graph refresh helper that uses directory-based locking (unsafe with uutils `mkdir`), or whose Graphify line lacks `GRAPHIFY_NO_AUTO_REFRESH=1` (Graphify 0.9.87 and later can rewrite installed agent skills after an upgrade). Advisory, per clone (the helper lives in `.git/hooks`); **`--check` exits `2` on it**, like any finding. One hook that runs the helper is enough, and a recorded decline does not silence it. The fix is `sidegraph-init` (no flag), which refreshes the helper and keeps a recorded hook choice. Silent outside a git repository, with no helper, or when no hook runs it |
1566
1566
  | `engine-version` | the graph was built by a Graphify release this Sidegraph was not tested with (its `graph.graphify_version` stamp is neither absent nor in the tested set), so anchors and domains may read differently. Advisory; **`--check` exits `2` on it**, like any finding. Pin Graphify to a tested release listed under [Engine version pinning](../integrations/graphify.md#engine-version-pinning), then rebuild the graph. It also fires for a mention-edge graph (Graphify 0.9.62 or later) over a store without a minimum Sidegraph version; follow the [0.14 migration guide](https://github.com/SantyagoSeaman/sidegraph/blob/main/docs/guides/migrating-to-0.14.md). A malformed marker leaves this second case silent. Silent without a readable graph |
1567
1567
  | `store-not-migrated` | the store uses the pre-0.17 layout (a format marker below major 2) and holds at least one record of any kind (initiatives included). One finding, at the store path, naming `sidegraph-migrate` (run on the default branch, after the team checklist in the [0.17 migration guide](https://github.com/SantyagoSeaman/sidegraph/blob/main/docs/guides/migrating-to-0.17.md)) and the `sidegraph:compact` skill. Degraded; **`--check` exits `2` on it**, like any finding. Silent on an empty store and on a store that is already format 2 |
1568
1568
  | `maintenance-due` | a format-2 store that `sidegraph-compact` would tidy: more than 200 per-record files, or a legacy `archive/` segment it would delete, or a misplaced or duplicated shard line, or a stray shard file. `sidegraph-verify` does not report these; maintenance heals them. A segment kept because one of its lines is not safe to delete does not count. Advisory; **`--check` exits `2` on it**, like any finding. Run `sidegraph-compact` on the default branch. Silent on a store that is not format 2 |
@@ -402,6 +402,13 @@ raises costs only its own line.
402
402
  > installed on the first rebuild after an upgrade. Ask the user to run `sidegraph-init`,
403
403
  > which refreshes the helper and keeps their recorded hook choice; do not run it unasked.
404
404
 
405
+ A helper using the old directory-based lock is also reported: uutils `mkdir` can admit
406
+ overlapping rebuilds. `sidegraph-init` replaces it with exclusive PID-file locking while
407
+ preserving hook bytes and the recorded choice. The outdated-helper check recognizes concrete defects in simple shell helpers,
408
+ rather than comparing the whole template. It conservatively skips unfamiliar scripts with
409
+ heredocs or multiline quotes. This controls automatic defect repair; an ordinary init
410
+ that refreshes a fully installed hook still rewrites its managed helper.
411
+
405
412
  It also sends a notice for the person. See
406
413
  [`refresh-helper-outdated`](../guides/troubleshooting.md#refresh-helper-outdated).
407
414
  15. **Refresh-hook line.** When no git hook keeps the graph fresh, one line tells the model to
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sidegraph",
3
- "version": "0.17.2",
3
+ "version": "0.17.3",
4
4
  "type": "module",
5
5
  "exports": "./index.js"
6
6
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sidegraph",
3
3
  "displayName": "Sidegraph",
4
- "version": "0.17.2",
4
+ "version": "0.17.3",
5
5
  "description": "Durable decision/lessons memory over your code graph — ADRs, gotchas, and constraints that survive refactors, retrieved mistakes-first.",
6
6
  "author": { "name": "SantyagoSeaman", "url": "https://github.com/SantyagoSeaman" },
7
7
  "homepage": "https://github.com/SantyagoSeaman/sidegraph",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sidegraph",
3
- "version": "0.17.2",
3
+ "version": "0.17.3",
4
4
  "description": "Durable decision/lessons memory over your code graph — ADRs, gotchas, and constraints that survive refactors, retrieved mistakes-first.",
5
5
  "license": "Apache-2.0",
6
6
  "skills": "./skills/",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "name": "sidegraph",
4
- "version": "0.17.2",
4
+ "version": "0.17.3",
5
5
  "description": "Durable decisions, facts and lessons over your code graph.",
6
6
  "compat": {
7
7
  "source": "native",
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sidegraph"
3
- version = "0.17.2"
3
+ version = "0.17.3"
4
4
  description = "Own the memory, rent the graph — a durable decision/lessons layer as a sidecar over a code-graph engine."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -30,7 +30,7 @@ if TYPE_CHECKING:
30
30
  )
31
31
  from .store import Store
32
32
 
33
- __version__ = "0.17.2"
33
+ __version__ = "0.17.3"
34
34
 
35
35
  # name -> the submodule that defines it
36
36
  _LAZY = {
@@ -416,11 +416,14 @@ def _hook_graph_problem(
416
416
 
417
417
 
418
418
  def _refresh_outdated_helper(info: githooks.RepoInfo) -> bool:
419
- """Rewrite the helper alone when a hook runs it and it runs Graphify without
419
+ """Rewrite a helper in use with the old directory lock or a Graphify run without
420
420
  ``GRAPHIFY_NO_AUTO_REFRESH=1``; ``True`` when it did. The helper is per repository, so this
421
421
  holds whatever graph the store reads; the recorded choice and the hook files are not touched.
422
422
  see design/superpowers/specs/2026-10-14-graphify-upgrade-design.md (D7)"""
423
- if githooks.helper_is_current(info) is not False or not githooks.helper_in_use(info):
423
+ old_lock = githooks.helper_uses_directory_lock(info)
424
+ if (
425
+ githooks.helper_is_current(info) is not False and not old_lock
426
+ ) or not githooks.helper_in_use(info):
424
427
  return False
425
428
  graphify = shutil.which("graphify")
426
429
  try:
@@ -428,10 +431,13 @@ def _refresh_outdated_helper(info: githooks.RepoInfo) -> bool:
428
431
  except OSError as e:
429
432
  print(f"graph refresh hook: could not refresh the helper ({e})")
430
433
  return False
431
- print(
432
- "graph refresh hook: refreshed the helper, which ran Graphify without "
433
- f"{githooks.GRAPH_REBUILD_ENV}"
434
- )
434
+ if old_lock:
435
+ print("graph refresh hook: refreshed the helper, which used directory-based locking")
436
+ else:
437
+ print(
438
+ "graph refresh hook: refreshed the helper, which ran Graphify without "
439
+ f"{githooks.GRAPH_REBUILD_ENV}"
440
+ )
435
441
  return True
436
442
 
437
443
 
@@ -27,8 +27,10 @@ import shlex
27
27
  import shutil
28
28
  import stat
29
29
  import subprocess
30
+ import sys
30
31
  import tempfile
31
32
  import time
33
+ from collections.abc import Iterator
32
34
  from dataclasses import dataclass
33
35
  from pathlib import Path
34
36
  from typing import Literal
@@ -76,17 +78,14 @@ _GIT_TIMEOUT = 10.0
76
78
  # wrote its pid; only its age says so. The helper breaks one after this long (``-mmin +10``).
77
79
  _STALE_LOCK_SECONDS = 600
78
80
 
79
- # The helper, verbatim from the reviewed design (D5): the "want" flag, ``take``, the ``.gate``
80
- # stale-breaker and ``me=$(exec sh -c 'echo $PPID')`` are the tested locking and are not to be
81
- # edited. One review amendment: a lock with no pid (its owner died, or could not write, between
82
- # ``mkdir`` and ``echo``) is broken once it is ten minutes old, the age checked again under the
83
- # gate; before, it blocked every rebuild for good. ``@GRAPHIFY@`` is the one substitution: the
84
- # path found at install time, quoted, or ''. A second amendment sets ``GRAPHIFY_NO_AUTO_REFRESH=1``
85
- # on the rebuild line (design/superpowers/specs/2026-10-14-graphify-upgrade-design.md, D7).
81
+ # D5's want flag, stale-breaker gate and runner PID are preserved. The lock and gate
82
+ # now use the shell's noclobber redirection (O_EXCL), independently of external mkdir.
83
+ # Old directory locks are read during upgrade; fresh incomplete owners are left alone.
84
+ # @GRAPHIFY@ is the install-time fallback, and every rebuild disables skill auto-refresh.
86
85
  _HELPER_TEMPLATE = r"""#!/bin/sh
87
86
  # Installed by sidegraph-init: keeps graphify-out/graph.json current for Sidegraph.
88
87
  # Main checkout only: a linked worktree reads the main checkout's graph. The rebuild runs
89
- # in the background so git never waits. A "want" flag plus a lock directory run one rebuild
88
+ # in the background so git never waits. A "want" flag plus an exclusive PID file run one rebuild
90
89
  # at a time and never miss a request that arrives during one.
91
90
  hook=$1; shift
92
91
  [ "$(git rev-parse --path-format=absolute --git-dir 2>/dev/null)" = \
@@ -102,19 +101,23 @@ log=$common/sidegraph-graph-refresh.log
102
101
  : > "$want" 2>/dev/null || exit 0
103
102
  (
104
103
  me=$(exec sh -c 'echo $PPID')
105
- take() { mkdir "$lock" 2>/dev/null && echo "$me" > "$lock/pid"; }
104
+ take() { (set -C; echo "$me" > "$lock") 2>/dev/null; }
105
+ read_pid() {
106
+ if [ -d "$lock" ]; then cat "$lock/pid" 2>/dev/null; else cat "$lock" 2>/dev/null; fi
107
+ }
106
108
  if ! take; then
107
- pid=$(cat "$lock/pid" 2>/dev/null)
109
+ pid=$(read_pid)
108
110
  if [ -n "$pid" ]; then
109
111
  kill -0 "$pid" 2>/dev/null && exit 0 # a runner is alive; it will see $want
110
112
  else # the owner is mid-write, or died there
111
113
  [ -n "$(find "$lock" -maxdepth 0 -mmin +10 2>/dev/null)" ] || exit 0
112
114
  fi
113
- mkdir "$lock.gate" 2>/dev/null || exit 0 # one stale-breaker at a time
114
- [ "$(cat "$lock/pid" 2>/dev/null)" = "$pid" ] &&
115
+ (set -C; : > "$lock.gate") 2>/dev/null || exit 0
116
+ (echo "$me" > "$lock.gate") 2>/dev/null || : # PID write failure must not leak the gate
117
+ [ "$(read_pid)" = "$pid" ] &&
115
118
  { [ -n "$pid" ] || [ -n "$(find "$lock" -maxdepth 0 -mmin +10 2>/dev/null)" ]; } &&
116
119
  rm -rf "$lock"
117
- rmdir "$lock.gate"; take || exit 0
120
+ rm -f "$lock.gate"; take || exit 0
118
121
  fi
119
122
  cd "$top" || { rm -rf "$lock"; exit 0; }
120
123
  while :; do
@@ -466,28 +469,125 @@ def _has_graphify_hook(info: RepoInfo) -> bool:
466
469
 
467
470
 
468
471
  def _lock_is_dead(lock: Path) -> bool:
469
- """Whether the helper's lock directory belongs to a job that is gone: its pid no longer runs,
470
- or it has no usable pid and is over ten minutes old. A pid that runs, even another user's, and
472
+ """Whether the helper's PID file or legacy lock directory belongs to a job that is gone:
473
+ its pid no longer runs, or it has no usable pid and is over ten minutes old. A pid that runs,
474
+ even another user's, and
471
475
  a recent pid-less lock (an owner still writing) are live. A pid can be reused by a live
472
476
  process after a reboot, which this cannot tell from the runner."""
473
477
  try:
474
- age = time.time() - lock.stat().st_mtime
478
+ before = lock.stat()
479
+ age = time.time() - before.st_mtime
475
480
  except OSError:
476
481
  return False
477
482
  try:
478
- text = (lock / "pid").read_text().strip()
483
+ text = (lock / "pid" if lock.is_dir() else lock).read_text().strip()
479
484
  except OSError:
480
485
  text = ""
481
486
  pid = int(text) if text.isdigit() else 0
482
487
  if pid <= 0:
483
- return age > _STALE_LOCK_SECONDS
488
+ dead = age > _STALE_LOCK_SECONDS
489
+ else:
490
+ if sys.platform == "win32":
491
+ # These are POSIX-shell PIDs. Windows kill(pid, 0) is not a liveness probe,
492
+ # and MSYS/native PID namespaces differ: leave recovery to the shell helper.
493
+ return False
494
+ try:
495
+ os.kill(pid, 0)
496
+ except ProcessLookupError:
497
+ dead = True
498
+ except OSError: # PermissionError: the process exists
499
+ return False
500
+ else:
501
+ return False
502
+ if not dead:
503
+ return False
504
+ # A live owner may finish and a fresh runner may acquire the path during kill(0).
505
+ # Under the breaker gate, only an unchanged dead owner can now safely be removed.
484
506
  try:
485
- os.kill(pid, 0)
486
- except ProcessLookupError:
487
- return True
488
- except OSError: # PermissionError: the process exists
507
+ after = lock.stat()
508
+ except OSError:
489
509
  return False
490
- return False
510
+ return (before.st_dev, before.st_ino, before.st_mtime_ns, before.st_ctime_ns) == (
511
+ after.st_dev,
512
+ after.st_ino,
513
+ after.st_mtime_ns,
514
+ after.st_ctime_ns,
515
+ )
516
+
517
+
518
+ def _remove_lock(path: Path) -> None:
519
+ """Remove a PID file or legacy directory, without following a symlink."""
520
+ if path.is_dir() and not path.is_symlink():
521
+ shutil.rmtree(path, ignore_errors=True)
522
+ else:
523
+ with contextlib.suppress(OSError):
524
+ path.unlink()
525
+
526
+
527
+ @contextlib.contextmanager
528
+ def _repair_guard(info: RepoInfo) -> Iterator[bool]:
529
+ """Serialize installers while repairing a dead breaker gate; the OS releases on exit.
530
+
531
+ Keep the coordination file: unlinking an advisory lock lets another installer lock a
532
+ different inode. A busy installer skips cleanup; the one holding the guard handles it.
533
+ """
534
+ try:
535
+ handle = (info.common / f"{HELPER_NAME}.repair").open("a+b")
536
+ except OSError:
537
+ yield False
538
+ return
539
+ with handle:
540
+ if sys.platform == "win32":
541
+ import msvcrt
542
+
543
+ if handle.tell() == 0:
544
+ handle.write(b"\0")
545
+ handle.flush()
546
+ handle.seek(0)
547
+ try:
548
+ msvcrt.locking(handle.fileno(), msvcrt.LK_NBLCK, 1)
549
+ except OSError:
550
+ yield False
551
+ return
552
+ try:
553
+ yield True
554
+ finally:
555
+ handle.seek(0)
556
+ msvcrt.locking(handle.fileno(), msvcrt.LK_UNLCK, 1)
557
+ else:
558
+ import fcntl
559
+
560
+ try:
561
+ fcntl.flock(handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
562
+ except OSError:
563
+ yield False
564
+ return
565
+ try:
566
+ yield True
567
+ finally:
568
+ fcntl.flock(handle, fcntl.LOCK_UN)
569
+
570
+
571
+ def _repair_dead_locks(info: RepoInfo) -> None:
572
+ """Clear dead locks under the helper's own breaker gate, never from a stale snapshot."""
573
+ with _repair_guard(info) as held:
574
+ if not held:
575
+ return
576
+ gate = info.common / f"{HELPER_NAME}.lock.gate"
577
+ if _lock_is_dead(gate):
578
+ _remove_lock(gate)
579
+ try:
580
+ handle = gate.open("x")
581
+ except FileExistsError:
582
+ return # a live/fresh gate, or a legacy breaker still using a directory
583
+ try:
584
+ with handle:
585
+ handle.write(f"{os.getpid()}\n")
586
+ lock = info.common / f"{HELPER_NAME}.lock"
587
+ if _lock_is_dead(lock):
588
+ _remove_lock(lock)
589
+ finally:
590
+ gate.unlink()
491
591
 
492
592
 
493
593
  # -- install, remove, status -----------------------------------------------------------------
@@ -499,12 +599,8 @@ def install_helper(info: RepoInfo, *, graphify: str | None) -> None:
499
599
  stale-breaker gate. For a repository whose hooks call it by hand.
500
600
  # see design/superpowers/specs/2026-10-02-graph-refresh-hook-design.md (D2, D5)"""
501
601
  info.own_hooks_dir.mkdir(parents=True, exist_ok=True)
502
- lock = info.common / f"{HELPER_NAME}.lock"
503
- if _lock_is_dead(lock): # re-running --hooks is the way out of a lock a dead job left
504
- shutil.rmtree(lock, ignore_errors=True)
602
+ _repair_dead_locks(info)
505
603
  _publish_file(info.helper, _encode(helper_script(graphify)), 0o755)
506
- with contextlib.suppress(OSError): # a stale stale-breaker's gate (D5)
507
- (info.common / f"{HELPER_NAME}.lock.gate").rmdir()
508
604
 
509
605
 
510
606
  def install(info: RepoInfo, *, graphify: str | None) -> InstallReport:
@@ -524,14 +620,15 @@ def install(info: RepoInfo, *, graphify: str | None) -> InstallReport:
524
620
 
525
621
  def remove(info: RepoInfo) -> RemoveReport:
526
622
  """Take Sidegraph out: the block from each hook, then (once) the helper, the want flag, any
527
- lock and gate, and the recorded choice. The rebuild log is left for the owner to read.
623
+ lock and gate, and the recorded choice. The rebuild log and native repair coordination
624
+ file stay: unlinking that file could let simultaneous installers lock different inodes.
528
625
  # see design/superpowers/specs/2026-10-02-graph-refresh-hook-design.md (D3)"""
529
626
  hooks = tuple(_strip(info.own_hooks_dir / hook, hook) for hook in HOOKS)
530
627
  helper_removed = info.helper.exists()
531
628
  info.helper.unlink(missing_ok=True)
532
629
  (info.common / f"{HELPER_NAME}.want").unlink(missing_ok=True)
533
630
  for leftover in ("lock", "lock.gate"):
534
- shutil.rmtree(info.common / f"{HELPER_NAME}.{leftover}", ignore_errors=True)
631
+ _remove_lock(info.common / f"{HELPER_NAME}.{leftover}")
535
632
  record_choice(info.top, declined=False)
536
633
  return RemoveReport(hooks, helper_removed)
537
634
 
@@ -604,3 +701,28 @@ def helper_in_use(info: RepoInfo) -> bool:
604
701
  ``hooks_dir`` calls it (``_runs_helper``), so a partly wired repository counts. Read-only.
605
702
  # see design/superpowers/specs/2026-10-14-graphify-upgrade-design.md (D7)"""
606
703
  return info.helper.is_file() and any(_runs_helper(info.hooks_dir / hook) for hook in HOOKS)
704
+
705
+
706
+ _DIRECTORY_LOCK = re.compile(
707
+ r'^\s*(?:take\(\)\s*\{\s*)?mkdir\s+(?:"\$lock(?:\.gate)?"|\$lock(?:\.gate)?)(?=\s|;|$)'
708
+ )
709
+
710
+
711
+ def helper_uses_directory_lock(info: RepoInfo) -> bool:
712
+ """Recognize shipped take()/standalone mkdir acquisitions, not arbitrary shell code.
713
+
714
+ Preserve unfamiliar helpers with heredocs/multiline quotes. Known old helpers have
715
+ simple acquisition lines; there is no need to parse arbitrary command/argument positions.
716
+ """
717
+ try:
718
+ installed = _decode(info.helper.read_bytes())
719
+ except OSError:
720
+ return False
721
+ if "<<" in installed:
722
+ return False
723
+ try:
724
+ if any("\n" in word for word in shlex.split(installed.replace("\\\n", ""), comments=True)):
725
+ return False
726
+ except ValueError:
727
+ return False
728
+ return any(_DIRECTORY_LOCK.match(line) for line in installed.splitlines())
@@ -735,7 +735,7 @@ def _detect_store_files_skipped(inputs: Inputs) -> Problem | None:
735
735
 
736
736
 
737
737
  def _detect_refresh_helper_outdated(inputs: Inputs) -> Problem | None:
738
- """A hook git runs calls a helper that runs Graphify without ``GRAPHIFY_NO_AUTO_REFRESH=1``
738
+ """A hook calls a helper with directory-based locking or missing ``GRAPHIFY_NO_AUTO_REFRESH=1``
739
739
  (an earlier ``sidegraph-init`` wrote it). Not run outside a git repository, with no helper, or
740
740
  when no hook calls it (``refresh-hook-missing`` says that); one hook that calls it is enough,
741
741
  so a partly wired repository and a recorded decline do not hide it. The fix is plain
@@ -751,8 +751,33 @@ def _detect_refresh_helper_outdated(inputs: Inputs) -> Problem | None:
751
751
  current = githooks.helper_is_current(info)
752
752
  if current is None or not githooks.helper_in_use(info):
753
753
  raise _NotRun
754
- if current:
754
+ old_lock = githooks.helper_uses_directory_lock(info)
755
+ if current and not old_lock:
755
756
  return None
757
+ if old_lock:
758
+ return Problem(
759
+ check="refresh-helper-outdated",
760
+ severity="advisory",
761
+ summary="graph refresh helper uses directory-based locking",
762
+ fix="sidegraph-init",
763
+ line=(
764
+ "Sidegraph: the graph refresh helper uses directory-based locking, which can "
765
+ "allow overlapping rebuilds with uutils mkdir. Ask the user to run "
766
+ "`sidegraph-init`, which refreshes the helper and keeps their recorded hook "
767
+ "choice; do not run it unasked."
768
+ ),
769
+ notice=(
770
+ "Sidegraph: the graph refresh helper uses directory-based locking, which can "
771
+ "allow overlapping rebuilds with uutils mkdir. Refresh it with `sidegraph-init`."
772
+ ),
773
+ findings=(
774
+ (
775
+ str(info.helper),
776
+ "the installed graph refresh helper uses directory-based "
777
+ "locking; run `sidegraph-init` to refresh it",
778
+ ),
779
+ ),
780
+ )
756
781
  return Problem(
757
782
  check="refresh-helper-outdated",
758
783
  severity="advisory",
@@ -0,0 +1,46 @@
1
+ """The public 0.17.2 directory-lock helper, frozen for upgrade regression tests."""
2
+
3
+ LEGACY_HELPER = r"""#!/bin/sh
4
+ # Installed by sidegraph-init: keeps graphify-out/graph.json current for Sidegraph.
5
+ # Main checkout only: a linked worktree reads the main checkout's graph. The rebuild runs
6
+ # in the background so git never waits. A "want" flag plus a lock directory run one rebuild
7
+ # at a time and never miss a request that arrives during one.
8
+ hook=$1; shift
9
+ [ "$(git rev-parse --path-format=absolute --git-dir 2>/dev/null)" = \
10
+ "$(git rev-parse --path-format=absolute --git-common-dir 2>/dev/null)" ] || exit 0
11
+ case $hook in post-checkout) [ "$3" = 1 ] && [ "$1" != "$2" ] || exit 0 ;; esac
12
+ top=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0
13
+ common=$(git rev-parse --path-format=absolute --git-common-dir 2>/dev/null) || exit 0
14
+ graphify=$(command -v graphify 2>/dev/null) || graphify=@GRAPHIFY@
15
+ [ -n "$graphify" ] && [ -x "$graphify" ] || exit 0
16
+ lock=$common/sidegraph-graph-refresh.lock
17
+ want=$common/sidegraph-graph-refresh.want
18
+ log=$common/sidegraph-graph-refresh.log
19
+ : > "$want" 2>/dev/null || exit 0
20
+ (
21
+ me=$(exec sh -c 'echo $PPID')
22
+ take() { mkdir "$lock" 2>/dev/null && echo "$me" > "$lock/pid"; }
23
+ if ! take; then
24
+ pid=$(cat "$lock/pid" 2>/dev/null)
25
+ if [ -n "$pid" ]; then
26
+ kill -0 "$pid" 2>/dev/null && exit 0 # a runner is alive; it will see $want
27
+ else # the owner is mid-write, or died there
28
+ [ -n "$(find "$lock" -maxdepth 0 -mmin +10 2>/dev/null)" ] || exit 0
29
+ fi
30
+ mkdir "$lock.gate" 2>/dev/null || exit 0 # one stale-breaker at a time
31
+ [ "$(cat "$lock/pid" 2>/dev/null)" = "$pid" ] &&
32
+ { [ -n "$pid" ] || [ -n "$(find "$lock" -maxdepth 0 -mmin +10 2>/dev/null)" ]; } &&
33
+ rm -rf "$lock"
34
+ rmdir "$lock.gate"; take || exit 0
35
+ fi
36
+ cd "$top" || { rm -rf "$lock"; exit 0; }
37
+ while :; do
38
+ while [ -e "$want" ]; do
39
+ rm -f "$want"
40
+ GRAPHIFY_NO_AUTO_REFRESH=1 PYTHONHASHSEED=0 "$graphify" update . > "$log" 2>&1
41
+ done
42
+ rm -rf "$lock"; [ -e "$want" ] || break; take || break
43
+ done
44
+ ) </dev/null >/dev/null 2>&1 &
45
+ exit 0
46
+ """
@@ -255,27 +255,6 @@ def have_shell(name: str) -> bool:
255
255
  return shutil.which(name) is not None
256
256
 
257
257
 
258
- def _mkdir_is_uutils() -> bool:
259
- """True when ``mkdir`` on PATH is uutils coreutils, the default on Ubuntu 26.04."""
260
- try:
261
- out = subprocess.run(
262
- ["mkdir", "--version"], capture_output=True, text=True, timeout=5
263
- ).stdout
264
- except (OSError, subprocess.SubprocessError):
265
- return False
266
- return "uutils" in out
267
-
268
-
269
- # uutils ``mkdir`` 0.10 is not atomic under a race: two callers creating one directory can both
270
- # exit 0, so the helper's mkdir lock lets two rebuilds run at once. The overlap tests are expected
271
- # to fail there until the helper takes its lock without an external mkdir.
272
- NON_ATOMIC_MKDIR = pytest.mark.xfail(
273
- _mkdir_is_uutils(),
274
- reason="uutils mkdir lets two racing callers both succeed; the helper lock relies on mkdir",
275
- strict=False,
276
- )
277
-
278
-
279
258
  # -- helpers shared by test_githooks and the private lock-race tests ------------------------
280
259
 
281
260
  SHELLS = [
@@ -20,7 +20,6 @@ from tests.githooks_support import (
20
20
  CREATED_HEADER,
21
21
  HELPER_NAME,
22
22
  HOOKS,
23
- NON_ATOMIC_MKDIR,
24
23
  SHELLS,
25
24
  STALE_STATES,
26
25
  Sandbox,
@@ -462,13 +461,11 @@ def test_the_rebuild_is_graphify_update_with_a_pinned_hash_seed_and_a_log(sandbo
462
461
  # tests/private/test_githooks.py; the two cases here keep the lock contract in every PR run.
463
462
 
464
463
 
465
- @NON_ATOMIC_MKDIR
466
464
  @pytest.mark.slow
467
465
  def test_t9_two_simultaneous_calls_never_overlap_on_a_fresh_lock(sandbox, monkeypatch):
468
466
  fresh_lock_burst(sandbox, monkeypatch, "sh", 2)
469
467
 
470
468
 
471
- @NON_ATOMIC_MKDIR
472
469
  @pytest.mark.slow
473
470
  def test_t9_two_simultaneous_calls_never_overlap_on_a_dead_pid_lock(sandbox, monkeypatch):
474
471
  stale_lock_burst(sandbox, monkeypatch, "sh", 2, "dead pid")
@@ -566,8 +563,8 @@ def test_a_pid_write_that_fails_does_not_block_later_rebuilds(sandbox):
566
563
  )
567
564
  assert done.returncode == 0
568
565
  lock = info.common / f"{HELPER_NAME}.lock"
569
- wait_for(lambda: (lock / "pid").exists(), what="the lock the failed write left")
570
- assert (lock / "pid").read_bytes() == b""
566
+ wait_for(lock.is_file, what="the lock the failed write left")
567
+ assert lock.read_bytes() == b""
571
568
  age(lock)
572
569
  commit(repo)
573
570
  starts = await_rebuilds(sandbox, info.common)