graphifyy 0.9.2__tar.gz → 0.9.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 (303) hide show
  1. {graphifyy-0.9.2/graphifyy.egg-info → graphifyy-0.9.3}/PKG-INFO +4 -2
  2. {graphifyy-0.9.2 → graphifyy-0.9.3}/README.md +3 -1
  3. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/__main__.py +27 -1
  4. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/build.py +67 -2
  5. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/export.py +50 -3
  6. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/extract.py +1308 -161
  7. graphifyy-0.9.3/graphify/extractors/csharp.py +393 -0
  8. graphifyy-0.9.3/graphify/paths.py +234 -0
  9. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/reflect.py +307 -2
  10. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/report.py +64 -0
  11. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/semantic_cleanup.py +10 -0
  12. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/serve.py +24 -3
  13. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/symbol_resolution.py +22 -2
  14. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/watch.py +2 -1
  15. {graphifyy-0.9.2 → graphifyy-0.9.3/graphifyy.egg-info}/PKG-INFO +4 -2
  16. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphifyy.egg-info/SOURCES.txt +2 -0
  17. {graphifyy-0.9.2 → graphifyy-0.9.3}/pyproject.toml +1 -1
  18. graphifyy-0.9.3/tests/test_cpp_objc_cross_file_calls.py +263 -0
  19. graphifyy-0.9.3/tests/test_csharp_type_resolution.py +569 -0
  20. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_explain_cli.py +43 -0
  21. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_export.py +69 -0
  22. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_extract.py +85 -0
  23. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_hypergraph.py +93 -0
  24. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_js_import_resolution.py +251 -0
  25. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_languages.py +443 -0
  26. graphifyy-0.9.3/tests/test_paths.py +99 -0
  27. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_reflect.py +246 -0
  28. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_report.py +44 -0
  29. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_semantic_cleanup.py +33 -0
  30. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_serve.py +48 -0
  31. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_symbol_resolution.py +90 -4
  32. graphifyy-0.9.2/graphify/extractors/csharp.py +0 -158
  33. graphifyy-0.9.2/graphify/paths.py +0 -46
  34. graphifyy-0.9.2/tests/test_csharp_type_resolution.py +0 -172
  35. {graphifyy-0.9.2 → graphifyy-0.9.3}/LICENSE +0 -0
  36. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/__init__.py +0 -0
  37. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/_minhash.py +0 -0
  38. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/affected.py +0 -0
  39. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/always_on/agents-md.md +0 -0
  40. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/always_on/antigravity-rules.md +0 -0
  41. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/always_on/claude-md.md +0 -0
  42. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/always_on/gemini-md.md +0 -0
  43. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/always_on/kiro-steering.md +0 -0
  44. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/always_on/vscode-instructions.md +0 -0
  45. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/analyze.py +0 -0
  46. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/benchmark.py +0 -0
  47. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/cache.py +0 -0
  48. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/callflow_html.py +0 -0
  49. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/cargo_introspect.py +0 -0
  50. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/cluster.py +0 -0
  51. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/command-kilo.md +0 -0
  52. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/dedup.py +0 -0
  53. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/detect.py +0 -0
  54. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/diagnostics.py +0 -0
  55. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/extractors/__init__.py +0 -0
  56. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/extractors/base.py +0 -0
  57. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/extractors/blade.py +0 -0
  58. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/extractors/elixir.py +0 -0
  59. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/extractors/razor.py +0 -0
  60. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/extractors/zig.py +0 -0
  61. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/file_slice.py +0 -0
  62. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/global_graph.py +0 -0
  63. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/google_workspace.py +0 -0
  64. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/hooks.py +0 -0
  65. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/ids.py +0 -0
  66. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/ingest.py +0 -0
  67. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/llm.py +0 -0
  68. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/manifest.py +0 -0
  69. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/manifest_ingest.py +0 -0
  70. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/mcp_ingest.py +0 -0
  71. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/multigraph_compat.py +0 -0
  72. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/pg_introspect.py +0 -0
  73. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/prs.py +0 -0
  74. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/querylog.py +0 -0
  75. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/resolver_registry.py +0 -0
  76. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/ruby_resolution.py +0 -0
  77. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/scip_ingest.py +0 -0
  78. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/security.py +0 -0
  79. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skill-agents.md +0 -0
  80. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skill-aider.md +0 -0
  81. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skill-amp.md +0 -0
  82. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skill-claw.md +0 -0
  83. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skill-codex.md +0 -0
  84. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skill-copilot.md +0 -0
  85. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skill-devin.md +0 -0
  86. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skill-droid.md +0 -0
  87. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skill-kilo.md +0 -0
  88. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skill-kiro.md +0 -0
  89. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skill-opencode.md +0 -0
  90. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skill-pi.md +0 -0
  91. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skill-trae.md +0 -0
  92. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skill-vscode.md +0 -0
  93. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skill-windows.md +0 -0
  94. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skill.md +0 -0
  95. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/agents/references/add-watch.md +0 -0
  96. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/agents/references/exports.md +0 -0
  97. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/agents/references/extraction-spec.md +0 -0
  98. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/agents/references/github-and-merge.md +0 -0
  99. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/agents/references/hooks.md +0 -0
  100. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/agents/references/query.md +0 -0
  101. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/agents/references/transcribe.md +0 -0
  102. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/agents/references/update.md +0 -0
  103. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/amp/references/add-watch.md +0 -0
  104. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/amp/references/exports.md +0 -0
  105. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/amp/references/extraction-spec.md +0 -0
  106. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/amp/references/github-and-merge.md +0 -0
  107. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/amp/references/hooks.md +0 -0
  108. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/amp/references/query.md +0 -0
  109. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/amp/references/transcribe.md +0 -0
  110. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/amp/references/update.md +0 -0
  111. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/claude/references/add-watch.md +0 -0
  112. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/claude/references/exports.md +0 -0
  113. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/claude/references/extraction-spec.md +0 -0
  114. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/claude/references/github-and-merge.md +0 -0
  115. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/claude/references/hooks.md +0 -0
  116. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/claude/references/query.md +0 -0
  117. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/claude/references/transcribe.md +0 -0
  118. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/claude/references/update.md +0 -0
  119. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/claw/references/add-watch.md +0 -0
  120. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/claw/references/exports.md +0 -0
  121. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/claw/references/extraction-spec.md +0 -0
  122. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/claw/references/github-and-merge.md +0 -0
  123. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/claw/references/hooks.md +0 -0
  124. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/claw/references/query.md +0 -0
  125. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/claw/references/transcribe.md +0 -0
  126. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/claw/references/update.md +0 -0
  127. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/codex/references/add-watch.md +0 -0
  128. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/codex/references/exports.md +0 -0
  129. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/codex/references/extraction-spec.md +0 -0
  130. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/codex/references/github-and-merge.md +0 -0
  131. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/codex/references/hooks.md +0 -0
  132. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/codex/references/query.md +0 -0
  133. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/codex/references/transcribe.md +0 -0
  134. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/codex/references/update.md +0 -0
  135. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/copilot/references/add-watch.md +0 -0
  136. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/copilot/references/exports.md +0 -0
  137. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/copilot/references/extraction-spec.md +0 -0
  138. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/copilot/references/github-and-merge.md +0 -0
  139. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/copilot/references/hooks.md +0 -0
  140. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/copilot/references/query.md +0 -0
  141. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/copilot/references/transcribe.md +0 -0
  142. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/copilot/references/update.md +0 -0
  143. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/droid/references/add-watch.md +0 -0
  144. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/droid/references/exports.md +0 -0
  145. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/droid/references/extraction-spec.md +0 -0
  146. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/droid/references/github-and-merge.md +0 -0
  147. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/droid/references/hooks.md +0 -0
  148. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/droid/references/query.md +0 -0
  149. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/droid/references/transcribe.md +0 -0
  150. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/droid/references/update.md +0 -0
  151. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/kilo/references/add-watch.md +0 -0
  152. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/kilo/references/exports.md +0 -0
  153. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/kilo/references/extraction-spec.md +0 -0
  154. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/kilo/references/github-and-merge.md +0 -0
  155. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/kilo/references/hooks.md +0 -0
  156. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/kilo/references/query.md +0 -0
  157. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/kilo/references/transcribe.md +0 -0
  158. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/kilo/references/update.md +0 -0
  159. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/kiro/references/add-watch.md +0 -0
  160. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/kiro/references/exports.md +0 -0
  161. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/kiro/references/extraction-spec.md +0 -0
  162. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/kiro/references/github-and-merge.md +0 -0
  163. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/kiro/references/hooks.md +0 -0
  164. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/kiro/references/query.md +0 -0
  165. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/kiro/references/transcribe.md +0 -0
  166. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/kiro/references/update.md +0 -0
  167. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/opencode/references/add-watch.md +0 -0
  168. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/opencode/references/exports.md +0 -0
  169. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/opencode/references/extraction-spec.md +0 -0
  170. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/opencode/references/github-and-merge.md +0 -0
  171. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/opencode/references/hooks.md +0 -0
  172. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/opencode/references/query.md +0 -0
  173. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/opencode/references/transcribe.md +0 -0
  174. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/opencode/references/update.md +0 -0
  175. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/pi/references/add-watch.md +0 -0
  176. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/pi/references/exports.md +0 -0
  177. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/pi/references/extraction-spec.md +0 -0
  178. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/pi/references/github-and-merge.md +0 -0
  179. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/pi/references/hooks.md +0 -0
  180. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/pi/references/query.md +0 -0
  181. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/pi/references/transcribe.md +0 -0
  182. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/pi/references/update.md +0 -0
  183. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/trae/references/add-watch.md +0 -0
  184. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/trae/references/exports.md +0 -0
  185. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/trae/references/extraction-spec.md +0 -0
  186. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/trae/references/github-and-merge.md +0 -0
  187. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/trae/references/hooks.md +0 -0
  188. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/trae/references/query.md +0 -0
  189. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/trae/references/transcribe.md +0 -0
  190. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/trae/references/update.md +0 -0
  191. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/vscode/references/add-watch.md +0 -0
  192. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/vscode/references/exports.md +0 -0
  193. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/vscode/references/extraction-spec.md +0 -0
  194. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/vscode/references/github-and-merge.md +0 -0
  195. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/vscode/references/hooks.md +0 -0
  196. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/vscode/references/query.md +0 -0
  197. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/vscode/references/transcribe.md +0 -0
  198. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/vscode/references/update.md +0 -0
  199. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/windows/references/add-watch.md +0 -0
  200. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/windows/references/exports.md +0 -0
  201. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/windows/references/extraction-spec.md +0 -0
  202. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/windows/references/github-and-merge.md +0 -0
  203. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/windows/references/hooks.md +0 -0
  204. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/windows/references/query.md +0 -0
  205. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/windows/references/transcribe.md +0 -0
  206. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/skills/windows/references/update.md +0 -0
  207. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/transcribe.py +0 -0
  208. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/tree_html.py +0 -0
  209. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/validate.py +0 -0
  210. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphify/wiki.py +0 -0
  211. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphifyy.egg-info/dependency_links.txt +0 -0
  212. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphifyy.egg-info/entry_points.txt +0 -0
  213. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphifyy.egg-info/requires.txt +0 -0
  214. {graphifyy-0.9.2 → graphifyy-0.9.3}/graphifyy.egg-info/top_level.txt +0 -0
  215. {graphifyy-0.9.2 → graphifyy-0.9.3}/setup.cfg +0 -0
  216. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_affected_cli.py +0 -0
  217. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_agents_platform.py +0 -0
  218. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_analyze.py +0 -0
  219. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_anthropic_custom_endpoint.py +0 -0
  220. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_antigravity_install.py +0 -0
  221. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_astro_extraction.py +0 -0
  222. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_backend_extras.py +0 -0
  223. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_benchmark.py +0 -0
  224. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_build.py +0 -0
  225. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_cache.py +0 -0
  226. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_callflow_html.py +0 -0
  227. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_cargo_introspect.py +0 -0
  228. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_charmap_encoding.py +0 -0
  229. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_chunking.py +0 -0
  230. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_claude_cli_backend.py +0 -0
  231. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_claude_md.py +0 -0
  232. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_cli_export.py +0 -0
  233. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_cluster.py +0 -0
  234. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_codebuddy.py +0 -0
  235. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_confidence.py +0 -0
  236. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_cpp_preprocess.py +0 -0
  237. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_dart.py +0 -0
  238. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_dedup.py +0 -0
  239. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_detect.py +0 -0
  240. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_devin.py +0 -0
  241. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_dotnet.py +0 -0
  242. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_extract_cli.py +0 -0
  243. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_extraction_spec_ids.py +0 -0
  244. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_falkordb_integration.py +0 -0
  245. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_file_node_id_spec.py +0 -0
  246. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_file_slice.py +0 -0
  247. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_global_graph.py +0 -0
  248. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_google_workspace.py +0 -0
  249. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_hooks.py +0 -0
  250. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_id_normalization_contract.py +0 -0
  251. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_image_vision.py +0 -0
  252. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_import_extension_resolution.py +0 -0
  253. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_incremental.py +0 -0
  254. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_ingest.py +0 -0
  255. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_install.py +0 -0
  256. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_install_references.py +0 -0
  257. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_install_roundtrip.py +0 -0
  258. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_install_strings.py +0 -0
  259. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_install_upgrade.py +0 -0
  260. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_java_type_resolution.py +0 -0
  261. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_label_retry.py +0 -0
  262. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_labeling.py +0 -0
  263. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_language_resolvers.py +0 -0
  264. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_llm_backends.py +0 -0
  265. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_llm_parser.py +0 -0
  266. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_manifest_ingest.py +0 -0
  267. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_mcp_ingest.py +0 -0
  268. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_minhash.py +0 -0
  269. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_multigraph_compat.py +0 -0
  270. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_multigraph_diagnostics.py +0 -0
  271. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_multilang.py +0 -0
  272. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_obsidian_dangling_member.py +0 -0
  273. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_obsidian_filename_cap.py +0 -0
  274. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_office_limits.py +0 -0
  275. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_ollama.py +0 -0
  276. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_openai_custom_endpoint.py +0 -0
  277. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_pascal.py +0 -0
  278. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_path_cli.py +0 -0
  279. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_pg_introspect.py +0 -0
  280. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_pipeline.py +0 -0
  281. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_provider_registry.py +0 -0
  282. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_prs.py +0 -0
  283. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_python_import_resolution.py +0 -0
  284. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_query_cli.py +0 -0
  285. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_querylog.py +0 -0
  286. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_rationale.py +0 -0
  287. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_read_hook.py +0 -0
  288. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_ruby_resolution.py +0 -0
  289. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_scip_ingest.py +0 -0
  290. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_security.py +0 -0
  291. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_semantic_similarity.py +0 -0
  292. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_serve_http.py +0 -0
  293. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_skillgen.py +0 -0
  294. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_swift_cross_file_calls.py +0 -0
  295. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_swift_import_resolution.py +0 -0
  296. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_terraform.py +0 -0
  297. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_transcribe.py +0 -0
  298. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_ts_inheritance.py +0 -0
  299. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_validate.py +0 -0
  300. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_vue_extraction.py +0 -0
  301. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_watch.py +0 -0
  302. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_wheel_packaging.py +0 -0
  303. {graphifyy-0.9.2 → graphifyy-0.9.3}/tests/test_wiki.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: graphifyy
3
- Version: 0.9.2
3
+ Version: 0.9.3
4
4
  Summary: AI coding assistant skill (Claude Code, CodeBuddy, Codex, OpenCode, Kilo Code, Cursor, Gemini CLI, Aider, OpenClaw, Factory Droid, Trae, Hermes, Kiro, Pi, Devin CLI, Google Antigravity) - turn any folder of code, docs, papers, images, or videos into a queryable knowledge graph
5
5
  License: MIT License
6
6
 
@@ -687,7 +687,9 @@ graphify save-result --question "Q" --answer "A" --nodes Foo Bar --outcome usefu
687
687
  graphify reflect # aggregate graphify-out/memory/ outcomes into reflections/LESSONS.md
688
688
  graphify reflect --if-stale # no-op when LESSONS.md is already newer than every input (cheap to run each session)
689
689
  graphify reflect --out docs/LESSONS.md # write the lessons doc somewhere else
690
- graphify reflect --graph graphify-out/graph.json # also group lessons by community
690
+ graphify reflect --graph graphify-out/graph.json # group lessons by community + write the work-memory overlay (.graphify_learning.json)
691
+ # the overlay tags nodes preferred/tentative/contested (recency-weighted, with provenance);
692
+ # graphify explain / query then show a "Lesson:" hint, flagged "code changed — re-verify" when the source moved on
691
693
 
692
694
  graphify uninstall # remove from all platforms in one shot
693
695
  graphify uninstall --purge # also delete graphify-out/
@@ -550,7 +550,9 @@ graphify save-result --question "Q" --answer "A" --nodes Foo Bar --outcome usefu
550
550
  graphify reflect # aggregate graphify-out/memory/ outcomes into reflections/LESSONS.md
551
551
  graphify reflect --if-stale # no-op when LESSONS.md is already newer than every input (cheap to run each session)
552
552
  graphify reflect --out docs/LESSONS.md # write the lessons doc somewhere else
553
- graphify reflect --graph graphify-out/graph.json # also group lessons by community
553
+ graphify reflect --graph graphify-out/graph.json # group lessons by community + write the work-memory overlay (.graphify_learning.json)
554
+ # the overlay tags nodes preferred/tentative/contested (recency-weighted, with provenance);
555
+ # graphify explain / query then show a "Lesson:" hint, flagged "code changed — re-verify" when the source moved on
554
556
 
555
557
  graphify uninstall # remove from all platforms in one shot
556
558
  graphify uninstall --purge # also delete graphify-out/
@@ -3186,6 +3186,30 @@ def main() -> None:
3186
3186
  )
3187
3187
  print(f" Type: {d.get('file_type', '')}")
3188
3188
  print(f" Community: {d.get('community_name') or d.get('community', '')}")
3189
+ # Work-memory overlay: a derived experiential hint from `graphify reflect`,
3190
+ # merged in display-only from the .graphify_learning.json sidecar next to
3191
+ # graph.json. No line when the node has no overlay entry.
3192
+ try:
3193
+ from graphify.reflect import load_learning_overlay as _llo
3194
+ from graphify.security import sanitize_label as _sl
3195
+ _overlay = _llo(gp)
3196
+ _entry = _overlay.get(str(nid))
3197
+ if _entry:
3198
+ _status = _sl(str(_entry.get("status", "")))
3199
+ if _status == "contested":
3200
+ _line = (f" Lesson: contested (useful {_entry.get('uses', 0)} / "
3201
+ f"dead-end {_entry.get('neg', 0)})")
3202
+ elif _status == "preferred":
3203
+ _line = (f" Lesson: preferred source (start here) — "
3204
+ f"{_entry.get('uses', 0)} useful, score={_entry.get('score', 0)}")
3205
+ else:
3206
+ _line = (f" Lesson: {_status or 'tentative'} — "
3207
+ f"{_entry.get('uses', 0)} useful, score={_entry.get('score', 0)}")
3208
+ if _entry.get("stale"):
3209
+ _line += " [code changed since — re-verify]"
3210
+ print(_line)
3211
+ except Exception:
3212
+ pass
3189
3213
  print(f" Degree: {G.degree(nid)}")
3190
3214
  from graphify.build import edge_data
3191
3215
  connections: list[tuple[str, str, dict]] = [] # (direction, neighbor_id, edge_data)
@@ -3529,10 +3553,12 @@ def main() -> None:
3529
3553
  tokens = {"input": 0, "output": 0}
3530
3554
  from graphify.export import _git_head as _gh
3531
3555
  _commit = _gh()
3556
+ from graphify.report import load_learning_for_report as _llfr
3532
3557
  report = generate(G, communities, cohesion, labels, gods, surprises,
3533
3558
  {"warning": "cluster-only mode — file stats not available"},
3534
3559
  tokens, str(watch_path), suggested_questions=questions,
3535
- min_community_size=min_community_size, built_at_commit=_commit)
3560
+ min_community_size=min_community_size, built_at_commit=_commit,
3561
+ learning=_llfr(out / "graph.json"))
3536
3562
  (out / "GRAPH_REPORT.md").write_text(report, encoding="utf-8")
3537
3563
  stages.mark("report")
3538
3564
  from graphify.export import backup_if_protected as _backup
@@ -53,6 +53,56 @@ _FILE_TYPE_SYNONYMS = {
53
53
  }
54
54
 
55
55
 
56
+ # Hyperedge member lists are canonically keyed `nodes` (see graphify/llm.py
57
+ # extraction spec), but LLM/subagent drift and externally-supplied graph.json
58
+ # sometimes emit `members` or `node_ids`. _normalize_hyperedge_members folds
59
+ # those aliases into `nodes` at ingest so every downstream consumer reads one
60
+ # canonical key — mirroring the `from`/`to` edge-endpoint tolerance below.
61
+ _HE_MEMBER_ALIASES = ("members", "node_ids")
62
+
63
+
64
+ def _normalize_hyperedge_members(he: object) -> None:
65
+ """Canonicalize a hyperedge's member list onto the `nodes` key, in place.
66
+
67
+ If `nodes` is already a list it wins (canonical), and only stray alias keys
68
+ are dropped. Otherwise the first alias (`members`, then `node_ids`) that is a
69
+ list is moved to `nodes`, deduped preserving order, with a single stderr
70
+ WARNING naming the hyperedge id and alias used. Leftover alias keys are
71
+ always removed so downstream code never re-reads them.
72
+ """
73
+ if not isinstance(he, dict):
74
+ return
75
+ if not isinstance(he.get("nodes"), list):
76
+ for alias in _HE_MEMBER_ALIASES:
77
+ val = he.get(alias)
78
+ if isinstance(val, list):
79
+ seen: set = set()
80
+ deduped: list = []
81
+ for ref in val:
82
+ try:
83
+ is_dupe = ref in seen
84
+ except TypeError:
85
+ is_dupe = False # unhashable ref: keep it, validator flags it
86
+ if is_dupe:
87
+ continue
88
+ try:
89
+ seen.add(ref)
90
+ except TypeError:
91
+ pass
92
+ deduped.append(ref)
93
+ he["nodes"] = deduped
94
+ print(
95
+ f"[graphify] WARNING: hyperedge "
96
+ f"'{he.get('id', '?')}' uses field '{alias}' instead of "
97
+ f"'nodes'; normalizing.",
98
+ file=sys.stderr,
99
+ )
100
+ break
101
+ # Drop any leftover alias keys regardless of which branch ran above.
102
+ for alias in _HE_MEMBER_ALIASES:
103
+ he.pop(alias, None)
104
+
105
+
56
106
  def _norm_source_file(p: str | None, root: str | None = None) -> str | None:
57
107
  """Normalize path separators and relativize absolute paths.
58
108
 
@@ -279,6 +329,14 @@ def build_from_json(extraction: dict, *, directed: bool = False, root: str | Pat
279
329
  if ft and ft not in {"code", "document", "paper", "image", "rationale", "concept"}:
280
330
  node["file_type"] = _FILE_TYPE_SYNONYMS.get(ft, "concept")
281
331
 
332
+ # Canonicalize hyperedge member lists (#1561): producers sometimes key the
333
+ # member list `members`/`node_ids` instead of `nodes`. Fold aliases onto
334
+ # `nodes` here — BEFORE validation and the semantic-rekey loop below — so
335
+ # every downstream consumer (rekey, source_file relativize, to_json) reads
336
+ # one canonical key, the same way edge endpoints alias from/to at build.
337
+ for he in extraction.get("hyperedges", []) or []:
338
+ _normalize_hyperedge_members(he)
339
+
282
340
  errors = validate_extraction(extraction)
283
341
  # Dangling edges (stdlib/external imports) are expected - only warn about real schema errors.
284
342
  real_errors = [e for e in errors if "does not match any node id" not in e]
@@ -489,8 +547,15 @@ def build_from_json(extraction: dict, *, directed: bool = False, root: str | Pat
489
547
  ".ts": "js", ".tsx": "js",
490
548
  ".go": "go", ".rs": "rs",
491
549
  ".java": "jvm", ".kt": "jvm", ".scala": "jvm", ".groovy": "jvm",
492
- ".c": "c", ".h": "c", ".cc": "cpp", ".cpp": "cpp", ".hpp": "cpp",
493
- ".cu": "cpp", ".cuh": "cpp", ".metal": "cpp",
550
+ # C, C++, and ObjC interoperate within one compilation unit: a method
551
+ # declared in a shared `.h` is defined/called from a `.c`/`.cpp`/`.m`
552
+ # sibling, so a cross-file INFERRED call from impl to its header decl
553
+ # is legitimate, not a phantom name-collision across languages. Treat
554
+ # the whole C family as one so the receiver-typed C++/ObjC member-call
555
+ # resolvers' header-targeting edges survive build (#1547/#1556).
556
+ ".c": "c", ".h": "c", ".cc": "c", ".cpp": "c", ".hpp": "c",
557
+ ".cxx": "c", ".hh": "c", ".hxx": "c",
558
+ ".cu": "c", ".cuh": "c", ".metal": "c", ".m": "c", ".mm": "c",
494
559
  ".rb": "rb", ".php": "php", ".cs": "cs", ".swift": "swift", ".lua": "lua",
495
560
  }
496
561
  src_ext = Path(G.nodes[src].get("source_file") or "").suffix.lower()
@@ -636,6 +636,7 @@ def to_html(
636
636
  community_labels: dict[int, str] | None = None,
637
637
  member_counts: dict[int, int] | None = None,
638
638
  node_limit: int | None = None,
639
+ learning_overlay: dict | None = None,
639
640
  ) -> None:
640
641
  """Generate an interactive vis.js HTML visualization of the graph.
641
642
 
@@ -678,7 +679,7 @@ def to_html(
678
679
  if raw_hyperedges:
679
680
  remapped = []
680
681
  for he in raw_hyperedges:
681
- he_members = he.get("nodes") or he.get("members") or []
682
+ he_members = he.get("nodes", [])
682
683
  comm_ids, seen = [], set()
683
684
  for nid in he_members:
684
685
  c = node_to_community.get(nid)
@@ -713,6 +714,21 @@ def to_html(
713
714
  max_deg = max(degree.values(), default=1) or 1
714
715
  max_mc = (max(member_counts.values(), default=1) or 1) if member_counts else 1
715
716
 
717
+ # Work-memory overlay (derived sidecar). When not passed explicitly, load it
718
+ # best-effort from the sibling .graphify_learning.json next to the output
719
+ # graph.html (which lives beside graph.json). Empty/missing => no learning
720
+ # fields, so the un-annotated render is byte-identical to pre-feature.
721
+ if learning_overlay is None:
722
+ learning_overlay = {}
723
+ try:
724
+ from graphify.reflect import load_learning_overlay as _llo
725
+ learning_overlay = _llo(Path(output_path))
726
+ except Exception:
727
+ learning_overlay = {}
728
+ # Status -> ring color. preferred=green, contested=amber. Tentative gets no
729
+ # ring (it's not yet trustworthy enough to highlight in the map).
730
+ _RING = {"preferred": "#22c55e", "contested": "#f59e0b"}
731
+
716
732
  # Build nodes list for vis.js
717
733
  vis_nodes = []
718
734
  for node_id, data in G.nodes(data=True):
@@ -728,7 +744,7 @@ def to_html(
728
744
  size = 10 + 30 * (deg / max_deg)
729
745
  # Only show label for high-degree nodes by default; others show on hover
730
746
  font_size = 12 if deg >= max_deg * 0.15 else 0
731
- vis_nodes.append({
747
+ node = {
732
748
  "id": node_id,
733
749
  "label": label,
734
750
  "color": {"background": color, "border": color, "highlight": {"background": "#ffffff", "border": color}},
@@ -740,7 +756,38 @@ def to_html(
740
756
  "source_file": sanitize_label(str(data.get("source_file") or "")),
741
757
  "file_type": data.get("file_type", ""),
742
758
  "degree": deg,
743
- })
759
+ }
760
+ # Conditional learning fields — only present for annotated nodes, so
761
+ # un-annotated output keeps the exact pre-feature node dict shape.
762
+ entry = learning_overlay.get(str(node_id)) if learning_overlay else None
763
+ if entry:
764
+ status = sanitize_label(str(entry.get("status", "")))
765
+ stale = bool(entry.get("stale"))
766
+ node["learning_status"] = status
767
+ node["learning_stale"] = stale
768
+ ring = _RING.get(status)
769
+ if ring:
770
+ # Status-colored ring via the border; stale => desaturated +
771
+ # dashed (vis.js supports per-node `shapeProperties.borderDashes`).
772
+ if stale:
773
+ ring = "#9ca3af"
774
+ node["shapeProperties"] = {"borderDashes": [4, 4]}
775
+ node["borderWidth"] = 3
776
+ node["color"] = {
777
+ "background": color, "border": ring,
778
+ "highlight": {"background": "#ffffff", "border": ring},
779
+ }
780
+ # Lesson line appended to the hover title.
781
+ if status == "contested":
782
+ lesson = f"Lesson: contested (useful {entry.get('uses', 0)} / dead-end {entry.get('neg', 0)})"
783
+ elif status == "preferred":
784
+ lesson = f"Lesson: preferred source ({entry.get('uses', 0)} useful, score={entry.get('score', 0)})"
785
+ else:
786
+ lesson = f"Lesson: {status} ({entry.get('uses', 0)} useful)"
787
+ if stale:
788
+ lesson += " [code changed — re-verify]"
789
+ node["title"] = _html.escape(label) + "\n" + _html.escape(sanitize_label(lesson))
790
+ vis_nodes.append(node)
744
791
 
745
792
  # Build edges list. Restore original edge direction from _src/_tgt
746
793
  # (stashed by build.py for exactly this reason): undirected NetworkX