graphrefly 0.16.0__tar.gz → 0.18.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (332) hide show
  1. {graphrefly-0.16.0 → graphrefly-0.18.0}/.claude/skills/dev-dispatch/SKILL.md +1 -1
  2. {graphrefly-0.16.0 → graphrefly-0.18.0}/CHANGELOG.md +16 -0
  3. {graphrefly-0.16.0 → graphrefly-0.18.0}/CLAUDE.md +2 -1
  4. {graphrefly-0.16.0 → graphrefly-0.18.0}/PKG-INFO +1 -1
  5. {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/optimizations/qa-design-decisions.jsonl +1 -0
  6. {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/optimizations/summary-table.jsonl +2 -0
  7. {graphrefly-0.16.0 → graphrefly-0.18.0}/docs/optimizations.md +13 -7
  8. {graphrefly-0.16.0 → graphrefly-0.18.0}/pyproject.toml +1 -1
  9. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/__init__.py +0 -1
  10. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/node.py +8 -0
  11. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/__init__.py +0 -2
  12. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/adapters.py +9 -0
  13. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/composite.py +8 -9
  14. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/data_structures.py +164 -179
  15. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/tier2.py +5 -0
  16. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/graph/graph.py +23 -2
  17. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/ai.py +26 -52
  18. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/cqrs.py +3 -6
  19. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/harness/__init__.py +2 -0
  20. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/harness/loop.py +119 -58
  21. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/harness/strategy.py +13 -7
  22. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/harness/types.py +12 -4
  23. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/messaging.py +24 -33
  24. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/orchestration.py +1 -0
  25. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/reduction.py +2 -2
  26. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_extra_composite.py +8 -8
  27. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_extra_data_structures.py +38 -40
  28. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_patterns_cqrs.py +3 -4
  29. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_patterns_messaging.py +2 -2
  30. graphrefly-0.18.0/website/src/content/docs/api/ReactiveListBundle.md +24 -0
  31. graphrefly-0.18.0/website/src/content/docs/api/ReactiveLogBundle.md +24 -0
  32. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/ReactiveMapBundle.md +7 -6
  33. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/flat_map.md +5 -0
  34. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/index.md +0 -1
  35. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/node.md +3 -4
  36. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/reactive_list.md +2 -2
  37. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/reactive_log.md +1 -1
  38. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/reactive_map.md +2 -2
  39. graphrefly-0.16.0/website/src/content/docs/api/ReactiveListBundle.md +0 -25
  40. graphrefly-0.16.0/website/src/content/docs/api/ReactiveLogBundle.md +0 -25
  41. {graphrefly-0.16.0 → graphrefly-0.18.0}/.claude/skills/parity/SKILL.md +0 -0
  42. {graphrefly-0.16.0 → graphrefly-0.18.0}/.claude/skills/qa/SKILL.md +0 -0
  43. {graphrefly-0.16.0 → graphrefly-0.18.0}/.gemini/skills/dev-dispatch/SKILL.md +0 -0
  44. {graphrefly-0.16.0 → graphrefly-0.18.0}/.gemini/skills/parity/SKILL.md +0 -0
  45. {graphrefly-0.16.0 → graphrefly-0.18.0}/.github/workflows/pages.yml +0 -0
  46. {graphrefly-0.16.0 → graphrefly-0.18.0}/.github/workflows/release.yml +0 -0
  47. {graphrefly-0.16.0 → graphrefly-0.18.0}/.gitignore +0 -0
  48. {graphrefly-0.16.0 → graphrefly-0.18.0}/.mise.toml +0 -0
  49. {graphrefly-0.16.0 → graphrefly-0.18.0}/CONTRIBUTING.md +0 -0
  50. {graphrefly-0.16.0 → graphrefly-0.18.0}/GEMINI.md +0 -0
  51. {graphrefly-0.16.0 → graphrefly-0.18.0}/LICENSE +0 -0
  52. {graphrefly-0.16.0 → graphrefly-0.18.0}/README.md +0 -0
  53. {graphrefly-0.16.0 → graphrefly-0.18.0}/TRASH/EmitStrategy.md +0 -0
  54. {graphrefly-0.16.0 → graphrefly-0.18.0}/TRASH/emit_with_batch.md +0 -0
  55. {graphrefly-0.16.0 → graphrefly-0.18.0}/TRASH-FILES.md +0 -0
  56. {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/docs/DESIGN-ARCHIVE-INDEX.md +0 -0
  57. {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/docs/SESSION-access-control-actor-guard.md +0 -0
  58. {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/docs/SESSION-cross-repo-implementation-audit.md +0 -0
  59. {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/docs/SESSION-demo-test-strategy.md +0 -0
  60. {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/docs/SESSION-graphrefly-spec-design.md +0 -0
  61. {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/docs/SESSION-serialization-memory-footprint.md +0 -0
  62. {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/docs/SESSION-tier2-parity-nonlocal-forward-inner.md +0 -0
  63. {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/docs/SESSION-universal-reduction-layer.md +0 -0
  64. {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/docs/design-archive-index.jsonl +0 -0
  65. {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/optimizations/built-in-optimizations.jsonl +0 -0
  66. {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/optimizations/cross-language-notes.jsonl +0 -0
  67. {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/optimizations/parity-fixes.jsonl +0 -0
  68. {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/optimizations/resolved-decisions.jsonl +0 -0
  69. {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/roadmap/phase-0-foundation.jsonl +0 -0
  70. {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/roadmap/phase-1-graph-container.jsonl +0 -0
  71. {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/roadmap/phase-2-extra.jsonl +0 -0
  72. {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/roadmap/phase-3-resilience-data.jsonl +0 -0
  73. {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/roadmap/phase-4-domain-layers.jsonl +0 -0
  74. {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/roadmap/phase-5-framework-distribution.jsonl +0 -0
  75. {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/roadmap/phase-6-versioning.jsonl +0 -0
  76. {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/roadmap/phase-7-polish.jsonl +0 -0
  77. {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/roadmap/phase-8-reduction-layer.jsonl +0 -0
  78. {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/roadmap/phase-9-harness-sprint.jsonl +0 -0
  79. {graphrefly-0.16.0 → graphrefly-0.18.0}/benchmarks/py-baseline.json +0 -0
  80. {graphrefly-0.16.0 → graphrefly-0.18.0}/docs/ADAPTER-CONTRACT.md +0 -0
  81. {graphrefly-0.16.0 → graphrefly-0.18.0}/docs/benchmark.md +0 -0
  82. {graphrefly-0.16.0 → graphrefly-0.18.0}/docs/docs-guidance.md +0 -0
  83. {graphrefly-0.16.0 → graphrefly-0.18.0}/docs/roadmap.md +0 -0
  84. {graphrefly-0.16.0 → graphrefly-0.18.0}/docs/test-guidance.md +0 -0
  85. {graphrefly-0.16.0 → graphrefly-0.18.0}/examples/README.md +0 -0
  86. {graphrefly-0.16.0 → graphrefly-0.18.0}/examples/basic_counter.py +0 -0
  87. {graphrefly-0.16.0 → graphrefly-0.18.0}/llms.txt +0 -0
  88. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/__init__.py +0 -0
  89. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/compat/__init__.py +0 -0
  90. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/compat/async_utils.py +0 -0
  91. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/compat/asyncio_runner.py +0 -0
  92. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/compat/trio_runner.py +0 -0
  93. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/bridge.py +0 -0
  94. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/cancellation.py +0 -0
  95. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/clock.py +0 -0
  96. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/dynamic_node.py +0 -0
  97. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/guard.py +0 -0
  98. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/meta.py +0 -0
  99. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/protocol.py +0 -0
  100. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/runner.py +0 -0
  101. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/subgraph_locks.py +0 -0
  102. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/sugar.py +0 -0
  103. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/timer.py +0 -0
  104. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/versioning.py +0 -0
  105. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/backoff.py +0 -0
  106. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/backpressure.py +0 -0
  107. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/cascading_cache.py +0 -0
  108. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/checkpoint.py +0 -0
  109. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/cron.py +0 -0
  110. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/resilience.py +0 -0
  111. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/sources.py +0 -0
  112. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/tier1.py +0 -0
  113. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/graph/__init__.py +0 -0
  114. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/integrations/__init__.py +0 -0
  115. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/integrations/django.py +0 -0
  116. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/integrations/fastapi.py +0 -0
  117. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/__init__.py +0 -0
  118. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/domain_templates.py +0 -0
  119. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/graphspec.py +0 -0
  120. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/harness/bridge.py +0 -0
  121. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/memory.py +0 -0
  122. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/reactive_layout/__init__.py +0 -0
  123. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/reactive_layout/measurement_adapters.py +0 -0
  124. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/reactive_layout/reactive_block_layout.py +0 -0
  125. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/reactive_layout/reactive_layout.py +0 -0
  126. {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/py.typed +0 -0
  127. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/bench_core.py +0 -0
  128. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/conftest.py +0 -0
  129. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_adapter_contract.py +0 -0
  130. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_adapters_ingest.py +0 -0
  131. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_adapters_storage.py +0 -0
  132. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_backpressure.py +0 -0
  133. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_bridge.py +0 -0
  134. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_cascading_cache.py +0 -0
  135. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_concurrency.py +0 -0
  136. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_core.py +0 -0
  137. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_django.py +0 -0
  138. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_domain_templates.py +0 -0
  139. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_dynamic_node.py +0 -0
  140. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_edge_cases.py +0 -0
  141. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_extra_resilience.py +0 -0
  142. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_extra_sources.py +0 -0
  143. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_extra_sources_http.py +0 -0
  144. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_extra_tier1.py +0 -0
  145. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_extra_tier2.py +0 -0
  146. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_fastapi.py +0 -0
  147. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_graph.py +0 -0
  148. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_graphspec.py +0 -0
  149. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_guard.py +0 -0
  150. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_measurement_adapters.py +0 -0
  151. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_operator_protocol_matrix.py +0 -0
  152. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_patterns_ai.py +0 -0
  153. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_patterns_harness.py +0 -0
  154. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_patterns_memory.py +0 -0
  155. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_patterns_orchestration.py +0 -0
  156. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_patterns_reduction.py +0 -0
  157. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_perf_smoke.py +0 -0
  158. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_protocol.py +0 -0
  159. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_reactive_block_layout.py +0 -0
  160. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_reactive_layout.py +0 -0
  161. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_reduction.py +0 -0
  162. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_regressions.py +0 -0
  163. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_runner.py +0 -0
  164. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_smoke.py +0 -0
  165. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_sugar.py +0 -0
  166. {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_versioning.py +0 -0
  167. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/.gitignore +0 -0
  168. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/README.md +0 -0
  169. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/astro.config.mjs +0 -0
  170. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/content.config.ts +0 -0
  171. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/package.json +0 -0
  172. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/pnpm-lock.yaml +0 -0
  173. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/public/llms.txt +0 -0
  174. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/py-api-sidebar.mjs +0 -0
  175. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/scripts/gen_api_docs.py +0 -0
  176. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/scripts/sync-docs.mjs +0 -0
  177. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/components/GraphreflyHero.astro +0 -0
  178. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/components/Header.astro +0 -0
  179. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/components/MobileMenuFooter.astro +0 -0
  180. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/components/PyodidePlayground.tsx +0 -0
  181. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/components/Sidebar.astro +0 -0
  182. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/components/SiteTitle.astro +0 -0
  183. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/BackoffPreset.md +0 -0
  184. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/BackoffStrategy.md +0 -0
  185. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/CheckpointAdapter.md +0 -0
  186. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/CircuitBreaker.md +0 -0
  187. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/CircuitOpenError.md +0 -0
  188. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/CompactEntry.md +0 -0
  189. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/DeferWhen.md +0 -0
  190. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/DictCheckpointAdapter.md +0 -0
  191. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/DistillBundle.md +0 -0
  192. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/DownStrategy.md +0 -0
  193. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/Extraction.md +0 -0
  194. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/FileCheckpointAdapter.md +0 -0
  195. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/HttpBundle.md +0 -0
  196. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/JitterMode.md +0 -0
  197. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/MemoryCheckpointAdapter.md +0 -0
  198. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/Message.md +0 -0
  199. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/MessageType.md +0 -0
  200. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/Messages.md +0 -0
  201. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/NodeActions.md +0 -0
  202. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/NodeFn.md +0 -0
  203. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/NodeImpl.md +0 -0
  204. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/NodeStatus.md +0 -0
  205. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/PipeOperator.md +0 -0
  206. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/PubSubHub.md +0 -0
  207. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/ReactiveIndexBundle.md +0 -0
  208. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/SqliteCheckpointAdapter.md +0 -0
  209. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/SubscribeHints.md +0 -0
  210. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/TimeoutError.md +0 -0
  211. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/TokenBucket.md +0 -0
  212. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/VerifiableBundle.md +0 -0
  213. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/Versioned.md +0 -0
  214. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/WithBreakerBundle.md +0 -0
  215. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/WithStatusBundle.md +0 -0
  216. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/audit.md +0 -0
  217. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/batch.md +0 -0
  218. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/buffer.md +0 -0
  219. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/buffer_count.md +0 -0
  220. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/buffer_time.md +0 -0
  221. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/cache.md +0 -0
  222. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/cached.md +0 -0
  223. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/checkpoint_node_value.md +0 -0
  224. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/circuit_breaker.md +0 -0
  225. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/combine.md +0 -0
  226. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/concat.md +0 -0
  227. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/concat_map.md +0 -0
  228. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/constant.md +0 -0
  229. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/debounce.md +0 -0
  230. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/decorrelated_jitter.md +0 -0
  231. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/delay.md +0 -0
  232. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/derived.md +0 -0
  233. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/dispatch_messages.md +0 -0
  234. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/distill.md +0 -0
  235. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/distinct_until_changed.md +0 -0
  236. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/down_with_batch.md +0 -0
  237. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/effect.md +0 -0
  238. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/element_at.md +0 -0
  239. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/empty.md +0 -0
  240. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/exhaust_map.md +0 -0
  241. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/exponential.md +0 -0
  242. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/fallback.md +0 -0
  243. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/fibonacci.md +0 -0
  244. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/filter.md +0 -0
  245. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/find.md +0 -0
  246. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/first.md +0 -0
  247. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/first_value_from.md +0 -0
  248. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/for_each.md +0 -0
  249. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_any.md +0 -0
  250. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_async_iter.md +0 -0
  251. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_awaitable.md +0 -0
  252. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_cron.md +0 -0
  253. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_event_emitter.md +0 -0
  254. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_fs_watch.md +0 -0
  255. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_git_hook.md +0 -0
  256. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_http.md +0 -0
  257. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_iter.md +0 -0
  258. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_mcp.md +0 -0
  259. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_timer.md +0 -0
  260. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_webhook.md +0 -0
  261. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_websocket.md +0 -0
  262. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/gate.md +0 -0
  263. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/interval.md +0 -0
  264. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/is_batching.md +0 -0
  265. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/is_phase2_message.md +0 -0
  266. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/is_terminal_message.md +0 -0
  267. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/last.md +0 -0
  268. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/linear.md +0 -0
  269. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/log_slice.md +0 -0
  270. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/map.md +0 -0
  271. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/merge.md +0 -0
  272. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/message_tier.md +0 -0
  273. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/never.md +0 -0
  274. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/of.md +0 -0
  275. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/operator.md +0 -0
  276. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/pairwise.md +0 -0
  277. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/partition_for_batch.md +0 -0
  278. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/pausable.md +0 -0
  279. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/pipe.md +0 -0
  280. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/producer.md +0 -0
  281. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/propagates_to_meta.md +0 -0
  282. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/pubsub.md +0 -0
  283. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/race.md +0 -0
  284. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/rate_limiter.md +0 -0
  285. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/reactive_index.md +0 -0
  286. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/reduce.md +0 -0
  287. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/repeat.md +0 -0
  288. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/replay.md +0 -0
  289. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/rescue.md +0 -0
  290. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/resolve_backoff_preset.md +0 -0
  291. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/restore_graph_checkpoint.md +0 -0
  292. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/retry.md +0 -0
  293. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/sample.md +0 -0
  294. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/save_graph_checkpoint.md +0 -0
  295. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/scan.md +0 -0
  296. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/share.md +0 -0
  297. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/skip.md +0 -0
  298. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/start_with.md +0 -0
  299. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/state.md +0 -0
  300. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/subscribe.md +0 -0
  301. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/switch_map.md +0 -0
  302. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/take.md +0 -0
  303. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/take_until.md +0 -0
  304. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/take_while.md +0 -0
  305. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/tap.md +0 -0
  306. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/throttle.md +0 -0
  307. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/throw_error.md +0 -0
  308. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/timeout.md +0 -0
  309. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/timeout_node.md +0 -0
  310. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/to_array.md +0 -0
  311. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/to_list.md +0 -0
  312. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/to_sse.md +0 -0
  313. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/to_websocket.md +0 -0
  314. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/token_bucket.md +0 -0
  315. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/token_tracker.md +0 -0
  316. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/valve.md +0 -0
  317. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/verifiable.md +0 -0
  318. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/window.md +0 -0
  319. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/window_count.md +0 -0
  320. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/window_time.md +0 -0
  321. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/with_breaker.md +0 -0
  322. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/with_latest_from.md +0 -0
  323. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/with_max_attempts.md +0 -0
  324. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/with_status.md +0 -0
  325. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/zip.md +0 -0
  326. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/index.mdx +0 -0
  327. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/lab/python.mdx +0 -0
  328. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content.config.ts +0 -0
  329. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/env.d.ts +0 -0
  330. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/styles/custom.css +0 -0
  331. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/theme-prototypes.html +0 -0
  332. {graphrefly-0.16.0 → graphrefly-0.18.0}/website/tsconfig.json +0 -0
@@ -22,7 +22,7 @@ Load context and plan the implementation in a single pass. **Parallelize all rea
22
22
  Read in parallel:
23
23
 
24
24
  - **`~/src/graphrefly/GRAPHREFLY-SPEC.md`** — primary behavioral authority; read sections relevant to the task
25
- - **`~/src/graphrefly/COMPOSITION-GUIDE.md`** — composition patterns and (read when building Phase 4+ factories that compose primitives — covers lazy activation, subscription ordering, null guards, Versioned navigation, factory wiring order)
25
+ - **`~/src/graphrefly/COMPOSITION-GUIDE.md`** — composition patterns and insights (read when building Phase 4+ factories that compose primitives — covers lazy activation, subscription ordering, null guards, Versioned navigation, factory wiring order)
26
26
  - `docs/optimizations.md` — **active work items**, anti-patterns, and **deferred follow-ups** (read when touching protocol, batch, node lifecycle, or parity). Resolved decisions are archived in `archive/optimizations/*.jsonl` — search there for historical context (see `docs/docs-guidance.md` § "Optimization decision log")
27
27
  - **`docs/test-guidance.md`** — checklists for the layer you touch (protocol, node, graph, operators)
28
28
  - **`docs/roadmap.md`** — phase alignment and acceptance criteria (active/open items only; completed phases archived to `archive/roadmap/*.jsonl`)
@@ -2,6 +2,22 @@
2
2
 
3
3
  <!-- version list -->
4
4
 
5
+ ## v0.18.0 (2026-04-08)
6
+
7
+ ### Features
8
+
9
+ - Address optimizations
10
+ ([`beddc97`](https://github.com/graphrefly/graphrefly-py/commit/beddc979beda1b8320f4eb94f997c7ee142abed4))
11
+
12
+
13
+ ## v0.17.0 (2026-04-07)
14
+
15
+ ### Features
16
+
17
+ - 9.0 part 2
18
+ ([`1f2bcb7`](https://github.com/graphrefly/graphrefly-py/commit/1f2bcb704ab86e62901114a08433f42edf6a5415))
19
+
20
+
5
21
  ## v0.16.0 (2026-04-07)
6
22
 
7
23
  ### Features
@@ -31,8 +31,9 @@ install dependencies.
31
31
  ## Key docs
32
32
 
33
33
  - `~/src/graphrefly/GRAPHREFLY-SPEC.md` — protocol and behavioral specification (shared with graphrefly-ts)
34
- - `~/src/graphrefly/COMPOSITION-GUIDE.md` — **composition guide** — 坑, patterns, recipes for Phase 4+ factory authors. Read before building factories that compose primitives.
34
+ - `~/src/graphrefly/COMPOSITION-GUIDE.md` — **composition guide** — insights, patterns, recipes for Phase 4+ factory authors. **Read before building factories that compose primitives.** Covers: lazy activation, subscription ordering, null guards, feedback cycles, promptNode SENTINEL, wiring order.
35
35
  - `~/src/graphrefly/composition-guide.jsonl` — machine-readable composition entries (appendable)
36
+ - `archive/optimizations/` — **Optimizations archive** — built-in optimizations, resolved design decisions, cross-language parity notes, proposed improvements. Check before introducing new optimizations or debugging perf issues.
36
37
  - `docs/roadmap.md` — phased implementation plan
37
38
  - `docs/docs-guidance.md` — how to write and maintain documentation here
38
39
  - `docs/test-guidance.md` — testing conventions and organization
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: graphrefly
3
- Version: 0.16.0
3
+ Version: 0.18.0
4
4
  Summary: Reactive harness layer for agent workflows. Describe automations in plain language, trace every decision, enforce policies, persist checkpoints. Zero dependencies.
5
5
  Project-URL: Homepage, https://py.graphrefly.dev
6
6
  Project-URL: Repository, https://github.com/graphrefly/graphrefly-py
@@ -7,4 +7,5 @@
7
7
  {"id": "ingest-adapters-intentional-divergences", "title": "Ingest adapters intentional divergences", "status": "documented", "body": "### Ingest adapters — intentional cross-language divergences (parity review 2026-04-03)\n\n| Aspect | TypeScript | Python | Rationale |\n|--------|-----------|--------|-----------|\n| **`KafkaConsumerLike` protocol** | KafkaJS shape: `subscribe({topic, fromBeginning})`, `run({eachMessage})`, `disconnect()` | confluent-kafka shape: `subscribe(topics: list)`, `run(callback)` | Each port targets its ecosystem's dominant Kafka client library. Both are duck-typed; users plug in their library's consumer directly. |\n| **`RedisClientLike` protocol** | ioredis shape: `xadd(key, id, ...fieldsAndValues)`, `xread(...args)` — variadic positional | redis-py shape: `xadd(name, fields: dict)`, `xread(streams: dict)` — dict-based | Same reasoning: each port matches the dominant Redis client for its ecosystem. Serialize defaults match (`string[]` vs `dict[str, str]`). |\n| **`toSSE` / `to_sse` return type** | `ReadableStream<Uint8Array>` (Web Streams API) | `Iterator[str]` (Python generator) | Language-native streaming idiom. TS uses Web Streams for SSE (compatible with `Response` constructor); PY uses generators (compatible with WSGI/ASGI streaming responses). |\n| **`fromPrometheus` / `fromClickHouseWatch` `signal` option** | `signal: AbortSignal` for external cancellation | No equivalent; uses `active[0]` flag on teardown | PY has no standard `AbortSignal`. External cancellation in PY is handled by unsubscribing (which triggers the cleanup/stop function). Both ports stop cleanly on teardown. |\n| **`SyslogMessage` field naming** | camelCase: `appName`, `procId`, `msgId` | snake_case: `app_name`, `proc_id`, `msg_id` | Language convention applied to output data structures. Each port follows its ecosystem's naming idiom. |\n| **`fromCSV` / `fromNDJSON` source type** | `AsyncIterable<string>` (async streams with chunk buffering) | `Iterable[str]` (sync iterators via threads) | PY uses threads for I/O concurrency; sync iterables are natural for `csv.reader` integration. TS uses async iteration for streaming I/O. |\n| **`PulsarConsumerLike` protocol** | `pulsar-client` JS shape: `receive()` returns Promise, `acknowledge(msg)` returns Promise, getter methods (`getData()`, `getTopicName()`, etc.) | `pulsar-client` PY shape: `receive()` blocking, `acknowledge(msg)` sync, attribute methods (`data()`, `topic_name()`, etc.) | Each port matches the native Pulsar client API for its ecosystem. TS uses async loop; PY uses threaded blocking loop. |\n| **`PulsarProducerLike.send()` call shape** | Single object: `send({data, partitionKey, properties})` | Positional + kwargs: `send(data, partition_key=..., properties=...)` | Matches respective native Pulsar client SDK calling conventions. |\n| **`PulsarMessage` field naming** | camelCase: `messageId`, `publishTime`, `eventTime` | snake_case: `message_id`, `publish_time`, `event_time` | Language convention applied to output data structures. |\n| **`NATSClientLike` protocol** | nats.js shape: `subscribe()` returns `AsyncIterable`, `publish(subject, data)` | Dual: sync iterable (threaded drain) or async iterable/coroutine (via `Runner`). Auto-detected at subscribe time. Optional `runner` kwarg. | TS uses native async iteration. PY auto-detects sync vs async subscriptions: sync uses threaded drain, async uses `resolve_runner().schedule()`. Both support queue groups. |\n| **`RabbitMQChannelLike` protocol** | amqplib shape: `consume(queue, callback)` returns `Promise<{consumerTag}>`, `cancel(tag)`, `ack(msg)`, `publish(exchange, routingKey, content)` | pika shape: `basic_consume(queue, on_message_callback, auto_ack)`, `start_consuming()`, `basic_ack(delivery_tag)`, `basic_publish(exchange, routing_key, body)` | Each port matches its ecosystem's dominant AMQP library. Pika requires `start_consuming()` to enter the event loop; amqplib's consume is promise-based. |\n| **`RabbitMQMessage` field naming** | camelCase: `routingKey`, `deliveryTag` | snake_case: `routing_key`, `delivery_tag` | Language convention applied to output data structures. |"}
8
8
  {"id": "deferred-follow-ups", "title": "Deferred follow-ups (QA)", "status": "deferred", "body": "## Deferred follow-ups (QA)\n\nNon-blocking items tracked for later; not optimizations per se. Keep this section **identical** in `graphrefly-ts/docs/optimizations.md` and here (aside from language-specific labels in the first table).\n\n| Item | Notes |\n|------|-------|\n| **`lastDepValues` + `is` / referential equality (resolved 2026-03-31 — keep + document)** | Default `is` identity check is correct for the common immutable-value case. The `node(equals=)` option already exists for custom comparison. Document clearly that mutable dep values should use a custom `equals` function. No code change needed. |\n| **`sideEffects: false` in `package.json`** | TypeScript package only. Safe while the library has no import-time side effects. Revisit if global registration or polyfills are added at module load. |\n| **JSDoc / docstrings on `node()` and public APIs** | `docs/docs-guidance.md`: JSDoc on new TS exports; docstrings on new Python public APIs. |\n| **Roadmap §0.3 checkboxes** | Mark Phase 0.3 items when the team agrees the milestone is complete. |"}
9
9
  {"id": "ai-deferred-optimizations", "title": "AI surface deferred optimizations", "status": "deferred", "body": "### AI surface (Phase 4.4) — deferred optimizations (QA 2026-03-31)\n\n| Item | Status | Notes |\n|------|--------|-------|\n| **Re-indexes entire store on every change** | Deferred | Decision: diff-based indexing using `Versioned` snapshot version field to track indexed entries. Deferred to after Phase 6 — current N is small enough that full re-index is acceptable pre-1.0. |\n| **Budget packing always includes first item** | Documented behavior | The retrieval budget packer always includes the first ranked result even if it exceeds `max_tokens`. This is intentional \"never return empty\" semantics — a query that matches at least one entry always returns something. Callers who need strict budget enforcement should post-filter. |\n| **Retrieval pipeline auto-wires when vectors/KG enabled** | Documented behavior | When `embed_fn` or `enable_knowledge_graph` is set, the retrieval pipeline automatically wires vector search and KG expansion into the retrieval derived node. There is no explicit opt-in/opt-out per retrieval stage — the presence of the capability implies its use. Callers who need selective retrieval should use the individual nodes directly. |"}
10
+ {"id": "per-node-resource-tracking", "title": "Per-node resource tracking and subscriber audit", "status": "proposed", "body": "### Per-node resource tracking and subscriber audit (proposed)\n\nDiscovered during harnessLoop 9.0 debugging (TS): the #1 time sink was identifying orphan effects (subscriberCount=0 on terminal side-effects). A subscriber audit would have caught this instantly.\n\n**Lightweight (near-term):** `node.stats()` returning `{ activations, last_activated_at, subscriber_count, memory_estimate? }`. Activation count and subscriber count are trivially trackable. Memory estimate via rough serialization heuristic.\n\n**Rich (Phase 1 describe extension):** `graph.resource_profile()` walks all nodes, returns per-node stats + aggregate. This is the 'reactive DevTools' direction — aligns with inspection-as-test-harness philosophy.\n\n**Subscriber audit (highest ROI):** `graph.audit()` returning `{ orphan_effects: list[Node] }` — nodes with `_fn` (compute/effect) but `_sink_count == 0`. Would have saved most debugging time.\n\n**Memory concern:** TopicGraph instances hold reactive logs that grow unbounded. Long-running harness loops with hundreds of items accumulate. `resource_profile()` could surface this.\n\n**Proposed API surface:**\n- `node.stats() -> NodeStats` — per-node resource snapshot\n- `graph.audit() -> AuditResult` — orphan detection, unbounded log warnings\n- `graph.resource_profile() -> ResourceProfile` — aggregate memory + activation heatmap"}
10
11
  {"id": "tier2-deferred-semantics", "title": "Tier 2 deferred semantics", "status": "deferred", "body": "### Tier 2 extra operators (roadmap 2.2) — deferred semantics (QA)\n\nApplies to `src/extra/operators.ts` and `graphrefly.extra.tier2`. **Keep the table below identical in both repos’ `docs/optimizations.md`.**\n\n| Item | Status | Notes |\n|------|--------|-------|\n| **~~`sample` + `undefined` as `T`~~** | Resolved (2026-04-07) | Fixed in TS: `sample` now tracks source DATA via local `NO_VALUE` sentinel. Python had no ambiguity. |\n| **`mergeMap` / `merge_map` + `ERROR`** | Documented limitation (2026-03-31) | When the outer stream or one inner emits `ERROR`, other inner subscriptions may keep running until they complete or unsubscribe. Rx-style “first error cancels all sibling inners” is **not** specified or implemented. Current behavior (inner errors don’t cascade) is arguably more useful for parallel work — no change needed. Document in JSDoc/docstrings. |"}
@@ -45,3 +45,5 @@
45
45
  {"id": "summary-45", "topic": "Extra Phase 3.1 (resilience)", "python": "`graphrefly.extra.{backoff,resilience,checkpoint}` + `core/timer.py` (`ResettableTimer`); see §6 below", "typescript": "`src/extra/{backoff,resilience,checkpoint}.ts` + `core/timer.ts` (`ResettableTimer`); see §6 below"}
46
46
  {"id": "summary-46", "topic": "Extra Phase 3.2 (data structures)", "python": "`graphrefly.extra.data_structures` (`reactive_map`, …); see §17", "typescript": "`reactiveMap` + `reactive-base` (`Versioned` snapshots); see §17"}
47
47
  {"id": "summary-47", "topic": "Core hook shape", "python": "`NodeImpl._set_inspector_hook()` installs an internal, opt-in hook with `dep_message` and `run` events.", "typescript": "`NodeImpl._setInspectorHook()` mirrors the same hook contract (`dep_message`, `run`)."}
48
+ {"id": "summary-48", "topic": "Per-node resource tracking (proposed)", "python": "`node.stats()`, `graph.audit()`, `graph.resource_profile()` — proposed", "typescript": "`node.stats()`, `graph.audit()`, `graph.resourceProfile()` — proposed"}
49
+ {"id": "summary-49", "topic": "Subscriber audit (proposed, highest ROI)", "python": "Detect orphan effects (`_sink_count == 0` on effect nodes)", "typescript": "Detect orphan effects (`_sinkCount === 0` on effect nodes)"}
@@ -8,7 +8,7 @@
8
8
 
9
9
  - **PY `ReactiveMapBundle` parity — `.get(key)`, `.has(key)`, `.size` (noted 2026-04-07):**
10
10
  - **Level A: DONE (2026-04-07).** Added `.get(key)`, `.has(key)`, `.size` to PY `ReactiveMapBundle` matching TS signatures. PY harness `strategy.py` updated to use `.get(key)` instead of Versioned navigation.
11
- - **Level B: Deferred (post-1.0).** `ReactiveMapBundle.node` (TS) / `.data` (PY) emits `Versioned<{ map: ReadonlyMap<K,V> }>` / `Versioned(version, MappingProxyType)`. The `Versioned` wrapper is a protocol optimization (efficient RESOLVED dedup via version comparison) that leaks into composition code when using the node as a derived dep. **Proposed fix:** `.node` / `.data` emits the unwrapped map directly; version-based equality handled internally via `equals` option on the state node. Consumers see `ReadonlyMap<K,V>` / `MappingProxyType`, not `Versioned`. Breaking change defer to post-1.0 audit of all `Versioned` usage.
11
+ - **Level B: DONE (PY, 2026-04-07).** Removed `Versioned` wrapper from all reactive bundle APIs (ReactiveMap, ReactiveLog, ReactiveList, ReactiveIndex). `.data` / `.entries` / `.items` / `.ordered` now emit unwrapped domain types (`MappingProxyType`, `tuple`, etc.). Internal version counter drives efficient equality without leaking into composition code (spec §5.12). All downstream consumers updated (messaging, cqrs, ai, domain-templates, composite, harness/strategy).
12
12
 
13
13
  - **Whole-repo `emit` → `down` audit + `up` / backpressure / `message_tier` sweep (all phases, noted 2026-04-07):**
14
14
  - **TS: DONE (2026-04-07).** Renames: `emitWithBatch` → `downWithBatch`, `_emitToSinks` → `_downToSinks`, `_emitAutoValue` → `_downAutoValue`, `_boundEmitToSinks` → `_boundDownToSinks`, `_emitSequential` → `_downSequential`, `emitLine` → `flushLine` (reactive-layout). Batch param `emit` → `sink`. `up()` audit: no asymmetries. `messageTier()` audit: already clean. `NodeActions.emit()` kept (different semantics from `actions.down()`). CQRS `CommandActions.emit()` kept (domain concept). Spec updated (`_emitAutoValue` → `_downAutoValue`).
@@ -42,16 +42,22 @@ Non-blocking items tracked for later. **Keep this section identical in both repo
42
42
 
43
43
  | Item | Notes |
44
44
  |------|-------|
45
- | **`lastDepValues` + `Object.is` / referential equality (resolved 2026-03-31 — keep + document)** | Default `Object.is` identity check is correct for the common immutable-value case. The `node({ equals })` option already exists for custom comparison. Document clearly that mutable dep values should use a custom `equals` function. No code change needed. |
45
+ | **`lastDepValues` + `Object.is` / referential equality (resolved 2026-03-31 — documented)** | Default identity check is correct for the common immutable-value case. The `node(equals=...)` option already exists for custom comparison. Mutable dep values should use a custom `equals` function. **Documented in `node()` docstring (2026-04-07).** |
46
46
  | **`sideEffects: false` in `package.json`** | TypeScript package only. Safe while the library has no import-time side effects. Revisit if global registration or polyfills are added at module load. |
47
- | **JSDoc / docstrings on `node()` and public APIs** | `docs/docs-guidance.md`: JSDoc on new TS exports; docstrings on new Python public APIs. |
47
+ | **JSDoc / docstrings on `node()` and public APIs** | `docs/docs-guidance.md`: JSDoc on new TS exports; docstrings on new Python public APIs. `node()` equals guidance added (2026-04-07). `flat_map` ERROR behavior documented (2026-04-07). `from_redis_stream` COMPLETE/disconnect documented (2026-04-07). |
48
48
  | **Roadmap §0.3 checkboxes** | Mark Phase 0.3 items when the team agrees the milestone is complete. |
49
49
 
50
+ ### Factory teardown — `dispose()` pattern (D1/D2, noted 2026-04-07)
51
+
52
+ | Item | Status | Notes |
53
+ |------|--------|-------|
54
+ | **Phase 4+ factories don't register internal nodes on the graph** | **DONE (TS + PY, 2026-04-07)** | Added `Graph.addDisposer(fn)` / `Graph.add_disposer(fn)` — general-purpose disposer registration drained on `destroy()` **before** TEARDOWN signal. TS: Fixed `harnessLoop`, `strategyModel`, `agentMemory`, `feedback`, `gate`, `contentModerationGraph`, `funnel` bridge, `ChatStreamGraph`, `ToolRegistryGraph`. PY: Fixed `harness_loop`, `reduction.py`, `ChatStreamGraph`, `ToolRegistryGraph`, `AgentMemoryGraph`. Dead `_version` counter removed from all reactive bundles (TS + PY). |
55
+
50
56
  ### AI surface (Phase 4.4) — deferred optimizations
51
57
 
52
58
  | Item | Status | Notes |
53
59
  |------|--------|-------|
54
- | **Re-indexes entire store on every change** | Deferred | Decision: diff-based indexing using `Versioned` snapshot version field to track indexed entries. Deferred to after Phase 6 — current N is small enough that full re-index is acceptable pre-1.0. |
60
+ | **Re-indexes entire store on every change** | Deferred | Decision: diff-based indexing using internal version counter to track indexed entries. Deferred to after Phase 6 — current N is small enough that full re-index is acceptable pre-1.0. |
55
61
  | **Budget packing always includes first item** | Documented behavior | The retrieval budget packer always includes the first ranked result even if it exceeds `maxTokens`. This is intentional "never return empty" semantics — a query that matches at least one entry always returns something. Callers who need strict budget enforcement should post-filter. |
56
62
  | **Retrieval pipeline auto-wires when vectors/KG enabled** | Documented behavior | When `embedFn` or `enableKnowledgeGraph` is set, the retrieval pipeline automatically wires vector search and KG expansion into the retrieval derived node. There is no explicit opt-in/opt-out per retrieval stage — the presence of the capability implies its use. Callers who need selective retrieval should use the individual nodes directly. |
57
63
 
@@ -59,14 +65,14 @@ Non-blocking items tracked for later. **Keep this section identical in both repo
59
65
 
60
66
  | Item | Status | Notes |
61
67
  |------|--------|-------|
62
- | **`mergeMap` / `merge_map` + `ERROR`** | Documented limitation (2026-03-31) | When the outer stream or one inner emits `ERROR`, other inner subscriptions may keep running until they complete or unsubscribe. Rx-style "first error cancels all sibling inners" is **not** specified or implemented. Current behavior (inner errors don't cascade) is arguably more useful for parallel work — no change needed. Document in JSDoc/docstrings. |
68
+ | **`mergeMap` / `merge_map` + `ERROR`** | **Documented (PY docstring, 2026-04-07)** | Inner errors propagate downstream but do not cancel sibling inners. Outer ERROR cancels all inners. Current behavior is intentional for parallel work. **Documented in `flat_map` docstring.** |
63
69
 
64
70
  ### Ingest adapters — deferred items
65
71
 
66
72
  | Item | Status | Notes |
67
73
  |------|--------|-------|
68
- | **`fromRedisStream` / `from_redis_stream` never emits COMPLETE** | Documented limitation (2026-04-03) | Long-lived stream consumers intentionally never complete. The consumer loop runs until teardown. This is expected behavior for persistent stream sources (same as Kafka). Document in JSDoc/docstrings. |
69
- | **`fromRedisStream` / `from_redis_stream` does not disconnect client** | Documented limitation (2026-04-03) | The caller owns the Redis client lifecycle. The adapter does not call `disconnect()` on teardown — the caller is responsible for closing the connection. Same contract as `fromKafka` (caller owns `consumer.connect()`/`disconnect()`). |
74
+ | **`fromRedisStream` / `from_redis_stream` never emits COMPLETE** | **Documented (PY docstring, 2026-04-07)** | Long-lived stream consumers intentionally never complete. **Documented in `from_redis_stream` docstring.** |
75
+ | **`fromRedisStream` / `from_redis_stream` does not disconnect client** | **Documented (PY docstring, 2026-04-07)** | The caller owns the Redis client lifecycle. **Documented in `from_redis_stream` docstring.** |
70
76
  | **PY `from_csv` / `from_ndjson` thread not joined on cleanup** | Documented limitation (2026-04-03) | Python file-ingest adapters run in a daemon thread. On teardown, `active[0] = False` signals the thread to exit but does not `join()` it. The daemon flag ensures the thread does not block process exit. A future optimization could add optional `join(timeout)` on cleanup for stricter resource control. |
71
77
 
72
78
  ### Intentional cross-language divergences
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "graphrefly"
3
- version = "0.16.0"
3
+ version = "0.18.0"
4
4
  description = "Reactive harness layer for agent workflows. Describe automations in plain language, trace every decision, enforce policies, persist checkpoints. Zero dependencies."
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -26,7 +26,6 @@ from graphrefly.core.node import (
26
26
  NodeImpl,
27
27
  NodeStatus,
28
28
  SubscribeHints,
29
- cleanup_result,
30
29
  node,
31
30
  )
32
31
  from graphrefly.core.protocol import (
@@ -976,6 +976,14 @@ def node(
976
976
  **kwargs: Any option key accepted by :class:`NodeImpl` (e.g. ``name``,
977
977
  ``initial``, ``equals``, ``guard``, ``thread_safe``).
978
978
 
979
+ **Equality (``equals``):** The default ``equals`` uses identity (``is``)
980
+ for immutable values. If your node produces *mutable* dep values
981
+ (dicts, lists) that are modified in place, provide a custom ``equals``
982
+ function — otherwise downstream nodes may skip updates because the
983
+ identity check sees the *same* object::
984
+
985
+ node([source], compute_fn, equals=lambda a, b: a == b)
986
+
979
987
  Returns:
980
988
  A new :class:`NodeImpl` instance.
981
989
 
@@ -110,7 +110,6 @@ from graphrefly.extra.data_structures import (
110
110
  ReactiveListBundle,
111
111
  ReactiveLogBundle,
112
112
  ReactiveMapBundle,
113
- Versioned,
114
113
  log_slice,
115
114
  pubsub,
116
115
  reactive_index,
@@ -214,7 +213,6 @@ __all__ = [
214
213
  "ReactiveListBundle",
215
214
  "ReactiveLogBundle",
216
215
  "ReactiveMapBundle",
217
- "Versioned",
218
216
  "NS_PER_MS",
219
217
  "NS_PER_SEC",
220
218
  "CacheTier",
@@ -1848,6 +1848,15 @@ def from_redis_stream(
1848
1848
 
1849
1849
  Uses XREAD with BLOCK to reactively consume stream entries.
1850
1850
 
1851
+ **Lifecycle notes:**
1852
+
1853
+ - This source intentionally **never emits** ``COMPLETE``. Long-lived
1854
+ stream consumers are expected to run indefinitely; completion would
1855
+ tear down downstream operators prematurely.
1856
+ - The caller **owns the Redis client lifecycle**. ``from_redis_stream``
1857
+ does not close or disconnect the client on teardown — call
1858
+ ``client.close()`` yourself when the graph is destroyed.
1859
+
1851
1860
  Args:
1852
1861
  client: Redis client instance with ``xread`` method (caller owns connection).
1853
1862
  key: Redis stream key.
@@ -113,11 +113,9 @@ class DistillBundle:
113
113
 
114
114
 
115
115
  def _snapshot_map(store: ReactiveMapBundle) -> Mapping[str, Any]:
116
- snap = store.data.get()
116
+ snap = store.entries.get()
117
117
  if snap is None:
118
118
  return {}
119
- if hasattr(snap, "value"):
120
- return cast("Mapping[str, Any]", snap.value)
121
119
  return cast("Mapping[str, Any]", snap)
122
120
 
123
121
 
@@ -218,9 +216,9 @@ def distill(
218
216
  )
219
217
 
220
218
  compact = derived(
221
- [store.data, context_node],
219
+ [store.entries, context_node],
222
220
  lambda deps, _a: _pack_compact(
223
- deps[0].value if hasattr(deps[0], "value") else deps[0],
221
+ deps[0],
224
222
  deps[1],
225
223
  score,
226
224
  cost,
@@ -228,8 +226,8 @@ def distill(
228
226
  ),
229
227
  )
230
228
  size = derived(
231
- [store.data],
232
- lambda deps, _a: len(deps[0].value if hasattr(deps[0], "value") else deps[0]),
229
+ [store.entries],
230
+ lambda deps, _a: len(deps[0]) if deps[0] is not None else 0,
233
231
  initial=0,
234
232
  )
235
233
  compact.subscribe(lambda _msgs: None)
@@ -260,8 +258,9 @@ def _compute_evictions(
260
258
  evict: Callable[[str, Any], Any],
261
259
  ) -> list[str]:
262
260
  out: list[str] = []
263
- current = get(store.data)
264
- snapshot = current.value if hasattr(current, "value") else current
261
+ snapshot = get(store.entries)
262
+ if snapshot is None:
263
+ return out
265
264
  for key, mem in snapshot.items():
266
265
  verdict = evict(key, mem)
267
266
  if isinstance(verdict, Node):