graphrefly 0.9.0__tar.gz → 0.10.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 (297) hide show
  1. {graphrefly-0.9.0 → graphrefly-0.10.0}/CHANGELOG.md +8 -0
  2. {graphrefly-0.9.0 → graphrefly-0.10.0}/PKG-INFO +1 -1
  3. {graphrefly-0.9.0 → graphrefly-0.10.0}/docs/optimizations.md +13 -2
  4. {graphrefly-0.9.0 → graphrefly-0.10.0}/docs/roadmap.md +58 -6
  5. {graphrefly-0.9.0 → graphrefly-0.10.0}/pyproject.toml +1 -1
  6. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/__init__.py +3 -0
  7. graphrefly-0.10.0/src/graphrefly/core/bridge.py +146 -0
  8. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/patterns/__init__.py +2 -0
  9. graphrefly-0.10.0/src/graphrefly/patterns/domain_templates.py +721 -0
  10. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/patterns/graphspec.py +6 -2
  11. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/patterns/reduction.py +60 -65
  12. graphrefly-0.10.0/tests/test_bridge.py +164 -0
  13. graphrefly-0.10.0/tests/test_domain_templates.py +353 -0
  14. {graphrefly-0.9.0 → graphrefly-0.10.0}/.claude/skills/dev-dispatch/SKILL.md +0 -0
  15. {graphrefly-0.9.0 → graphrefly-0.10.0}/.claude/skills/parity/SKILL.md +0 -0
  16. {graphrefly-0.9.0 → graphrefly-0.10.0}/.claude/skills/qa/SKILL.md +0 -0
  17. {graphrefly-0.9.0 → graphrefly-0.10.0}/.gemini/skills/dev-dispatch/SKILL.md +0 -0
  18. {graphrefly-0.9.0 → graphrefly-0.10.0}/.gemini/skills/parity/SKILL.md +0 -0
  19. {graphrefly-0.9.0 → graphrefly-0.10.0}/.github/workflows/pages.yml +0 -0
  20. {graphrefly-0.9.0 → graphrefly-0.10.0}/.github/workflows/release.yml +0 -0
  21. {graphrefly-0.9.0 → graphrefly-0.10.0}/.gitignore +0 -0
  22. {graphrefly-0.9.0 → graphrefly-0.10.0}/.mise.toml +0 -0
  23. {graphrefly-0.9.0 → graphrefly-0.10.0}/CLAUDE.md +0 -0
  24. {graphrefly-0.9.0 → graphrefly-0.10.0}/CONTRIBUTING.md +0 -0
  25. {graphrefly-0.9.0 → graphrefly-0.10.0}/GEMINI.md +0 -0
  26. {graphrefly-0.9.0 → graphrefly-0.10.0}/LICENSE +0 -0
  27. {graphrefly-0.9.0 → graphrefly-0.10.0}/README.md +0 -0
  28. {graphrefly-0.9.0 → graphrefly-0.10.0}/archive/docs/DESIGN-ARCHIVE-INDEX.md +0 -0
  29. {graphrefly-0.9.0 → graphrefly-0.10.0}/archive/docs/SESSION-access-control-actor-guard.md +0 -0
  30. {graphrefly-0.9.0 → graphrefly-0.10.0}/archive/docs/SESSION-cross-repo-implementation-audit.md +0 -0
  31. {graphrefly-0.9.0 → graphrefly-0.10.0}/archive/docs/SESSION-demo-test-strategy.md +0 -0
  32. {graphrefly-0.9.0 → graphrefly-0.10.0}/archive/docs/SESSION-graphrefly-spec-design.md +0 -0
  33. {graphrefly-0.9.0 → graphrefly-0.10.0}/archive/docs/SESSION-serialization-memory-footprint.md +0 -0
  34. {graphrefly-0.9.0 → graphrefly-0.10.0}/archive/docs/SESSION-tier2-parity-nonlocal-forward-inner.md +0 -0
  35. {graphrefly-0.9.0 → graphrefly-0.10.0}/archive/docs/SESSION-universal-reduction-layer.md +0 -0
  36. {graphrefly-0.9.0 → graphrefly-0.10.0}/benchmarks/py-baseline.json +0 -0
  37. {graphrefly-0.9.0 → graphrefly-0.10.0}/docs/ADAPTER-CONTRACT.md +0 -0
  38. {graphrefly-0.9.0 → graphrefly-0.10.0}/docs/benchmark.md +0 -0
  39. {graphrefly-0.9.0 → graphrefly-0.10.0}/docs/docs-guidance.md +0 -0
  40. {graphrefly-0.9.0 → graphrefly-0.10.0}/docs/test-guidance.md +0 -0
  41. {graphrefly-0.9.0 → graphrefly-0.10.0}/examples/README.md +0 -0
  42. {graphrefly-0.9.0 → graphrefly-0.10.0}/examples/basic_counter.py +0 -0
  43. {graphrefly-0.9.0 → graphrefly-0.10.0}/llms.txt +0 -0
  44. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/__init__.py +0 -0
  45. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/compat/__init__.py +0 -0
  46. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/compat/async_utils.py +0 -0
  47. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/compat/asyncio_runner.py +0 -0
  48. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/compat/trio_runner.py +0 -0
  49. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/cancellation.py +0 -0
  50. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/clock.py +0 -0
  51. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/dynamic_node.py +0 -0
  52. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/guard.py +0 -0
  53. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/meta.py +0 -0
  54. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/node.py +0 -0
  55. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/protocol.py +0 -0
  56. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/runner.py +0 -0
  57. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/subgraph_locks.py +0 -0
  58. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/sugar.py +0 -0
  59. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/timer.py +0 -0
  60. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/versioning.py +0 -0
  61. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/__init__.py +0 -0
  62. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/adapters.py +0 -0
  63. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/backoff.py +0 -0
  64. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/backpressure.py +0 -0
  65. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/cascading_cache.py +0 -0
  66. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/checkpoint.py +0 -0
  67. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/composite.py +0 -0
  68. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/cron.py +0 -0
  69. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/data_structures.py +0 -0
  70. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/resilience.py +0 -0
  71. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/sources.py +0 -0
  72. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/tier1.py +0 -0
  73. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/tier2.py +0 -0
  74. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/graph/__init__.py +0 -0
  75. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/graph/graph.py +0 -0
  76. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/integrations/__init__.py +0 -0
  77. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/integrations/django.py +0 -0
  78. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/integrations/fastapi.py +0 -0
  79. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/patterns/ai.py +0 -0
  80. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/patterns/cqrs.py +0 -0
  81. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/patterns/memory.py +0 -0
  82. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/patterns/messaging.py +0 -0
  83. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/patterns/orchestration.py +0 -0
  84. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/patterns/reactive_layout/__init__.py +0 -0
  85. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/patterns/reactive_layout/measurement_adapters.py +0 -0
  86. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/patterns/reactive_layout/reactive_block_layout.py +0 -0
  87. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/patterns/reactive_layout/reactive_layout.py +0 -0
  88. {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/py.typed +0 -0
  89. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/bench_core.py +0 -0
  90. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/conftest.py +0 -0
  91. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_adapter_contract.py +0 -0
  92. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_adapters_ingest.py +0 -0
  93. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_adapters_storage.py +0 -0
  94. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_backpressure.py +0 -0
  95. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_cascading_cache.py +0 -0
  96. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_concurrency.py +0 -0
  97. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_core.py +0 -0
  98. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_django.py +0 -0
  99. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_dynamic_node.py +0 -0
  100. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_edge_cases.py +0 -0
  101. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_extra_composite.py +0 -0
  102. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_extra_data_structures.py +0 -0
  103. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_extra_resilience.py +0 -0
  104. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_extra_sources.py +0 -0
  105. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_extra_sources_http.py +0 -0
  106. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_extra_tier1.py +0 -0
  107. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_extra_tier2.py +0 -0
  108. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_fastapi.py +0 -0
  109. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_graph.py +0 -0
  110. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_graphspec.py +0 -0
  111. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_guard.py +0 -0
  112. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_measurement_adapters.py +0 -0
  113. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_patterns_ai.py +0 -0
  114. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_patterns_cqrs.py +0 -0
  115. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_patterns_memory.py +0 -0
  116. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_patterns_messaging.py +0 -0
  117. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_patterns_orchestration.py +0 -0
  118. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_patterns_reduction.py +0 -0
  119. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_perf_smoke.py +0 -0
  120. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_protocol.py +0 -0
  121. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_reactive_block_layout.py +0 -0
  122. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_reactive_layout.py +0 -0
  123. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_reduction.py +0 -0
  124. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_regressions.py +0 -0
  125. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_runner.py +0 -0
  126. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_smoke.py +0 -0
  127. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_sugar.py +0 -0
  128. {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_versioning.py +0 -0
  129. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/.gitignore +0 -0
  130. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/README.md +0 -0
  131. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/astro.config.mjs +0 -0
  132. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/content.config.ts +0 -0
  133. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/package.json +0 -0
  134. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/pnpm-lock.yaml +0 -0
  135. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/public/llms.txt +0 -0
  136. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/py-api-sidebar.mjs +0 -0
  137. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/scripts/gen_api_docs.py +0 -0
  138. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/scripts/sync-docs.mjs +0 -0
  139. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/components/GraphreflyHero.astro +0 -0
  140. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/components/Header.astro +0 -0
  141. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/components/MobileMenuFooter.astro +0 -0
  142. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/components/PyodidePlayground.tsx +0 -0
  143. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/components/Sidebar.astro +0 -0
  144. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/components/SiteTitle.astro +0 -0
  145. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/BackoffPreset.md +0 -0
  146. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/BackoffStrategy.md +0 -0
  147. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/CheckpointAdapter.md +0 -0
  148. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/CircuitBreaker.md +0 -0
  149. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/CircuitOpenError.md +0 -0
  150. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/DeferWhen.md +0 -0
  151. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/DictCheckpointAdapter.md +0 -0
  152. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/DistillBundle.md +0 -0
  153. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/EmitStrategy.md +0 -0
  154. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/Extraction.md +0 -0
  155. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/FileCheckpointAdapter.md +0 -0
  156. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/HttpBundle.md +0 -0
  157. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/JitterMode.md +0 -0
  158. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/MemoryCheckpointAdapter.md +0 -0
  159. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/Message.md +0 -0
  160. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/MessageType.md +0 -0
  161. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/Messages.md +0 -0
  162. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/NodeActions.md +0 -0
  163. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/NodeFn.md +0 -0
  164. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/NodeImpl.md +0 -0
  165. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/NodeStatus.md +0 -0
  166. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/PipeOperator.md +0 -0
  167. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/PubSubHub.md +0 -0
  168. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/ReactiveIndexBundle.md +0 -0
  169. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/ReactiveListBundle.md +0 -0
  170. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/ReactiveLogBundle.md +0 -0
  171. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/ReactiveMapBundle.md +0 -0
  172. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/SqliteCheckpointAdapter.md +0 -0
  173. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/SubscribeHints.md +0 -0
  174. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/TokenBucket.md +0 -0
  175. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/VerifiableBundle.md +0 -0
  176. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/Versioned.md +0 -0
  177. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/WithBreakerBundle.md +0 -0
  178. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/WithStatusBundle.md +0 -0
  179. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/audit.md +0 -0
  180. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/batch.md +0 -0
  181. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/buffer.md +0 -0
  182. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/buffer_count.md +0 -0
  183. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/buffer_time.md +0 -0
  184. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/cached.md +0 -0
  185. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/checkpoint_node_value.md +0 -0
  186. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/circuit_breaker.md +0 -0
  187. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/combine.md +0 -0
  188. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/concat.md +0 -0
  189. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/concat_map.md +0 -0
  190. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/constant.md +0 -0
  191. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/debounce.md +0 -0
  192. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/decorrelated_jitter.md +0 -0
  193. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/delay.md +0 -0
  194. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/derived.md +0 -0
  195. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/dispatch_messages.md +0 -0
  196. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/distill.md +0 -0
  197. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/distinct_until_changed.md +0 -0
  198. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/effect.md +0 -0
  199. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/element_at.md +0 -0
  200. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/emit_with_batch.md +0 -0
  201. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/empty.md +0 -0
  202. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/exhaust_map.md +0 -0
  203. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/exponential.md +0 -0
  204. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/fibonacci.md +0 -0
  205. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/filter.md +0 -0
  206. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/find.md +0 -0
  207. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/first.md +0 -0
  208. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/first_value_from.md +0 -0
  209. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/flat_map.md +0 -0
  210. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/for_each.md +0 -0
  211. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_any.md +0 -0
  212. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_async_iter.md +0 -0
  213. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_awaitable.md +0 -0
  214. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_cron.md +0 -0
  215. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_event_emitter.md +0 -0
  216. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_fs_watch.md +0 -0
  217. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_git_hook.md +0 -0
  218. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_http.md +0 -0
  219. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_iter.md +0 -0
  220. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_mcp.md +0 -0
  221. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_timer.md +0 -0
  222. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_webhook.md +0 -0
  223. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_websocket.md +0 -0
  224. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/gate.md +0 -0
  225. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/index.md +0 -0
  226. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/interval.md +0 -0
  227. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/is_batching.md +0 -0
  228. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/is_phase2_message.md +0 -0
  229. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/is_terminal_message.md +0 -0
  230. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/last.md +0 -0
  231. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/linear.md +0 -0
  232. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/log_slice.md +0 -0
  233. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/map.md +0 -0
  234. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/merge.md +0 -0
  235. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/message_tier.md +0 -0
  236. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/never.md +0 -0
  237. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/node.md +0 -0
  238. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/of.md +0 -0
  239. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/operator.md +0 -0
  240. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/pairwise.md +0 -0
  241. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/partition_for_batch.md +0 -0
  242. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/pausable.md +0 -0
  243. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/pipe.md +0 -0
  244. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/producer.md +0 -0
  245. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/propagates_to_meta.md +0 -0
  246. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/pubsub.md +0 -0
  247. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/race.md +0 -0
  248. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/rate_limiter.md +0 -0
  249. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/reactive_index.md +0 -0
  250. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/reactive_list.md +0 -0
  251. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/reactive_log.md +0 -0
  252. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/reactive_map.md +0 -0
  253. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/reduce.md +0 -0
  254. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/repeat.md +0 -0
  255. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/replay.md +0 -0
  256. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/rescue.md +0 -0
  257. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/resolve_backoff_preset.md +0 -0
  258. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/restore_graph_checkpoint.md +0 -0
  259. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/retry.md +0 -0
  260. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/sample.md +0 -0
  261. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/save_graph_checkpoint.md +0 -0
  262. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/scan.md +0 -0
  263. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/share.md +0 -0
  264. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/skip.md +0 -0
  265. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/start_with.md +0 -0
  266. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/state.md +0 -0
  267. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/subscribe.md +0 -0
  268. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/switch_map.md +0 -0
  269. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/take.md +0 -0
  270. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/take_until.md +0 -0
  271. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/take_while.md +0 -0
  272. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/tap.md +0 -0
  273. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/throttle.md +0 -0
  274. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/throw_error.md +0 -0
  275. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/timeout.md +0 -0
  276. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/to_array.md +0 -0
  277. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/to_list.md +0 -0
  278. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/to_sse.md +0 -0
  279. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/to_websocket.md +0 -0
  280. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/token_bucket.md +0 -0
  281. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/token_tracker.md +0 -0
  282. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/verifiable.md +0 -0
  283. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/window.md +0 -0
  284. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/window_count.md +0 -0
  285. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/window_time.md +0 -0
  286. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/with_breaker.md +0 -0
  287. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/with_latest_from.md +0 -0
  288. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/with_max_attempts.md +0 -0
  289. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/with_status.md +0 -0
  290. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/zip.md +0 -0
  291. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/index.mdx +0 -0
  292. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/lab/python.mdx +0 -0
  293. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content.config.ts +0 -0
  294. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/env.d.ts +0 -0
  295. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/styles/custom.css +0 -0
  296. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/theme-prototypes.html +0 -0
  297. {graphrefly-0.9.0 → graphrefly-0.10.0}/website/tsconfig.json +0 -0
@@ -2,6 +2,14 @@
2
2
 
3
3
  <!-- version list -->
4
4
 
5
+ ## v0.10.0 (2026-04-06)
6
+
7
+ ### Features
8
+
9
+ - 8.2
10
+ ([`abe2102`](https://github.com/graphrefly/graphrefly-py/commit/abe210208ff248d883ac319855108aa4d345812f))
11
+
12
+
5
13
  ## v0.9.0 (2026-04-06)
6
14
 
7
15
  ### Features
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: graphrefly
3
- Version: 0.9.0
3
+ Version: 0.10.0
4
4
  Summary: Reactive graph protocol for human + LLM co-operation. Composable nodes, glitch-free diamond resolution, two-phase push, durable streaming. Zero dependencies.
5
5
  Project-URL: Homepage, https://py.graphrefly.dev
6
6
  Project-URL: Repository, https://github.com/graphrefly/graphrefly-py
@@ -139,10 +139,21 @@ Union-find over node identity merges components when nodes list dependencies at
139
139
  - **~~`to_json()` → `to_dict()` + `to_json_string()` (Phase 1.4, noted 2026-04-05, resolved 2026-04-05):~~** PY: `to_json()` renamed to `to_json_string()`; `to_dict()` added as alias of `snapshot()`; `to_json` alias removed (pre-1.0, no backward compat needed). TS: `toJSON()` renamed to `toObject()`; `toJSON()` kept as ECMAScript hook. Spec §3.8 updated.
140
140
  - **~~Initial value, cached state, and equals interaction (all phases, noted 2026-04-05, resolved 2026-04-05):~~** Resolved with `_SENTINEL` / `NO_VALUE` sentinel (TS: `Symbol.for("graphrefly/NO_VALUE")`, PY: `_SENTINEL = object()`). Replaces the `_has_emitted_data` boolean flag entirely. One field (`_cached`) instead of two — impossible to desync. Key semantics: (1) When `initial` option is present (even as `None`), `_cached = initial` — `equals` IS called on first emission. (2) When `initial` is absent, `_cached = _SENTINEL` — first emission always DATA. (3) INVALIDATE / `reset_on_teardown` set `_cached = _SENTINEL`. (4) Resubscribable: terminal reset now also sets `_cached = _SENTINEL` — new subscriber always gets DATA. (5) Reconnect: cache retained → same-value emits RESOLVED — correct. (6) `get()` returns `None` when `_cached is _SENTINEL`. Spec §2.5 updated.
141
141
  - **Auto-edge registration is local-only (Phase 1.1, noted 2026-04-05):** `Graph.add()` auto-registers edges for deps within the same `Graph` instance only. Cross-subgraph deps still require explicit `connect()`. Consistent with spec: cross-subgraph edges are explicit wiring.
142
+ - **Core `bridge()` helper (Phase 8.2, noted 2026-04-06, QA 2026-04-06):** Both TS and PY export `bridge(from, to, opts?)` / `bridge(from_node, to_node, *, name, down)` from `core/bridge`. Creates a graph-visible effect node that intercepts messages from `from` via `on_message` and forwards to `to.down()`. TS updated in QA: default forwards **all standard types** (DATA, DIRTY, RESOLVED, COMPLETE, ERROR, TEARDOWN, PAUSE, RESUME, INVALIDATE). Terminal types always cause bridge self-termination regardless of `down` filter. `funnel()` explicitly excludes TEARDOWN from inter-stage bridges. `DEFAULT_DOWN` exported for callers. **PY needs update** to match: (1) `DEFAULT_DOWN` should include all standard types, (2) terminal self-termination regardless of `down`, (3) `funnel()` should exclude TEARDOWN explicitly.
142
143
  - **GraphSpec cross-language parity (Phase 8.3, noted 2026-04-06, QA 2026-04-06):** Both TS and PY implement `compileSpec`/`compile_spec`, `decompileGraph`/`decompile_graph`, `llmCompose`/`llm_compose`, `llmRefine`/`llm_refine`, `specDiff`/`spec_diff`, and `validateSpec`/`validate_spec` in `patterns/graphspec`. Key alignment: (1) `GraphSpec` schema is identical JSON shape — `nodes`, `templates`, `feedback` top-level keys. TS uses TypeScript types; PY uses TypedDict. (2) `compile_spec` resolves nodes in dependency order (state/producer first, then derived/effect/operator). Catalog is passed explicitly (`GraphSpecCatalog`) — no global registry. (3) Template instantiation creates mounted subgraphs via `graph.mount()`. `$param` bindings resolve to top-level nodes. (4) Feedback edges wire via §8.1 `feedback()` primitive. (5) `decompile_graph` uses `describe(detail="standard")`, skips `__meta__` and `__feedback_*` internal nodes. Template detection via meta-based recovery (primary) + structural fingerprinting (fallback; includes dep names for accuracy). (6) `spec_diff` is pure JSON comparison — template-aware, feedback-aware. (7) LLM APIs (`llm_compose`/`llm_refine`) share identical system prompt and validation pipeline. (8) `validate_spec` checks bind targets exist in outer nodes, rejects feedback self-cycles, validates template param completeness. QA fixes: idempotent unsub in feedback(), deterministic output node selection in decompile, `contextlib.suppress` for connect dedup. Both repos: 35 tests each.
143
- - **Feedback bare DATA to reentry/counter — deferred to 8.2 (Phase 8.1, noted 2026-04-06, decided 2026-04-06):** Both TS and PY `feedback()` send bare `[DATA]` to `reentry` and `counter` nodes without a preceding `DIRTY`. This is a deliberate protocol shortcut: the feedback subscriber operates outside the normal two-phase push because it is a subscribe-based bridge (not a node). Acceptable because feedback reentry targets are always state nodes (which re-derive DIRTY internally via `down()`). Will be resolved when feedback is rearchitected as a graph-visible bridge node in 8.2 — at that point the bridge node participates in two-phase push naturally.
144
+ - **~~Feedback bare DATA to reentry/counter — deferred to 8.2 (Phase 8.1, noted 2026-04-06, resolved 2026-04-06):~~** Resolved in 8.2. `feedback()` now uses a graph-registered effect node (`__feedback_effect_<condition>`) that intercepts condition messages via `on_message` and forwards to reentry/counter. The effect participates in two-phase push naturally. Old subscribe-based bridge removed. Both TS and PY.
144
145
  - **`llm_compose`/`llm_refine` sync (PY) vs async (TS) — intentional divergence (Phase 8.3, noted 2026-04-06):** PY: synchronous, adapter must return `LLMResponse` directly. TS: `async function` returning `Promise<GraphSpec>`. PY design invariant: no `async def` / `Awaitable` in public APIs. TS spec §5.10 allows `await` at system boundaries (LLM adapter is external I/O, not reactive scheduling). Both are correct for their language idiom. Adapter contracts differ: PY adapters must be sync; TS adapters return Promise-compatible values.
145
- - **Reduction primitives cross-language parity (Phase 8.1, noted 2026-04-06, QA 2026-04-06):** Both TS and PY implement `stratify`, `funnel`, `feedback`, `budget_gate`/`budgetGate`, and `scorer` in `patterns/reduction`. All five follow the orchestration factory pattern (`_base_meta`, `_register_step`). Key alignment: (1) `stratify` buffers DIRTY until DATA arriveson classifier miss, emits `[DIRTY, RESOLVED]` to preserve spec §1.3.1 (both). (2) `funnel` bridges stages via `subscribe` forwarding DIRTY/DATA/RESOLVED/COMPLETE/ERROR to preserve two-phase protocol. TODO(8.2): replace with graph-visible bridge nodes. (3) `feedback` counter node is source of truth (resettable via `graph.set()`); uses `continue` (not `return`) on max_iterations so remaining batch messages process. Counter name is `__feedback_<condition>` to support multiple loops per graph. (4) `budget_gate`/`budgetGate` force-flushes all buffered items on terminal regardless of budget; sends RESUME before terminal if paused; forwards constraint ERROR downstream, silences constraint COMPLETE, forwards unknown constraint types via default. (5) `scorer` coerces `None`/`undefined` to 0 before multiplication (no TypeError/NaN divergence). TS `ScoredItem` is a plain object; PY `ScoredItem` is a class with `__slots__` and `__eq__`. Meta keys: `reduction: True`, `reduction_type: "<name>"`. Both repos: 22 tests each.
146
+ - **Domain templates cross-language parity (Phase 8.2, noted 2026-04-06):** Both TS and PY implement `observability_graph`/`observabilityGraph`, `issue_tracker_graph`/`issueTrackerGraph`, `content_moderation_graph`/`contentModerationGraph`, `data_quality_graph`/`dataQualityGraph` in `patterns/domain_templates` / `patterns/domain-templates`. Key alignment: (1) All four use source injection (option B) user passes a source node, template wires the topology. (2) Same well-known node names across both. (3) Null guard on derived fns: PY returns `None`, TS returns `undefined` both skip computation when source hasn't emitted real data. (4) PY `derived` fn signature is `(vals, actions)` vs TS `(vals) => ...`. (5) Options: PY uses `@dataclass`, TS uses interface types. (6) Meta keys: `domain_template: True`, `template_type: "<name>"`. Both repos: TS 24 tests, PY 21 tests.
147
+ - **Intentional cross-language divergences (Phase 8.2, noted 2026-04-06 parity):** (A) Bridge `down` option type: TS `readonly symbol[]`, PY `Sequence[MessageType] | None` — idiomatic to each language. (B) `BridgeOptions`: TS exports a named type alias, PY uses keyword args on the function — Pythonic kwargs pattern. (C) `batch()` syntax: TS callback `batch(() => { ... })`, PY context manager `with batch(): ...` — per spec §6.1. (E) `ScoredItem`: TS is a plain object / type alias, PY is a class with `__slots__`, `__eq__`, `__repr__` — PY more ergonomic for debugging; runtime payloads identical.
148
+ - **`content_moderation_graph` review accumulator read-modify-write (Phase 8.2, noted 2026-04-06 QA, patched 2026-04-06):** The `__review_accumulator` effect uses `review_queue.get()` + spread + `review_queue.down()` to append items. TS is single-threaded (safe). PY's GIL protects against true races but the pattern is fragile under async event loops — consider reactive scan/reduce if PY ever supports async node fns. QA patch: wrapped `review_queue.down()` in `with batch():` to defer propagation until the current cycle completes, preventing re-entrant dispatch into downstream feedback nodes.
149
+ - **`data_quality_graph` baseline updater re-entrant `down()` (Phase 8.2, noted 2026-04-06 QA, patched 2026-04-06):** The `__baseline_updater` effect calls `baseline.down()` during its own compute, which triggers synchronous downstream propagation into `drift_node` and `output_node` while the source update cycle is still in progress. QA patch: wrapped `baseline.down()` in `with batch():` to defer propagation. Same pattern as review accumulator above.
150
+ - **`budget_gate` `buffer.pop(0)` is O(n) (Phase 8.1, noted 2026-04-06 QA, deferred):** `flush_buffer` in `budget_gate` calls `buffer.pop(0)` in a while loop — O(n²) for full drain. Should use `collections.deque.popleft()`. Low priority: budget_gate buffers are typically small.
151
+ - **`stratify` `pending_dirty` not reset on TEARDOWN (Phase 8.1, noted 2026-04-06 QA, deferred):** The `pending_dirty` mutable flag in `stratify`'s `on_message` handler is never cleared on TEARDOWN. Default dispatch handles TEARDOWN via `return False`, but stale dirty state persists if the node is reused. Low impact: graphs are not typically restarted without reconstruction.
152
+ - **~~Bridge `_DEFAULT_DOWN` excludes PAUSE/RESUME (Phase 8.2, noted 2026-04-06 QA, resolved 2026-04-06 parity):~~** Resolved: `DEFAULT_DOWN` now includes all 9 standard types (DATA, DIRTY, RESOLVED, COMPLETE, ERROR, TEARDOWN, PAUSE, RESUME, INVALIDATE), matching TS. `funnel()` explicitly excludes TEARDOWN via `down` filter. Renamed from `_DEFAULT_DOWN` to `DEFAULT_DOWN` (public export).
153
+ - **Unbounded review queue growth in `content_moderation_graph` (Phase 8.2, noted 2026-04-06 QA, deferred):** The `review_queue` state node accumulates items without bound — every new review item copies and extends the list. O(n²) memory churn over time. Cap/eviction is domain-specific; consider adding `max_queue_size` option or reactive scan/reduce if templates grow beyond demo use.
154
+ - **Bridge/feedback node detection in `decompile_graph` uses naming convention (Phase 8.2, noted 2026-04-06 QA, deferred):** `decompile_graph` skips internal nodes via `path.startswith("__bridge_")` and `path.startswith("__feedback_effect_")` — name-based, not metadata-based. Works because naming is convention-controlled by `bridge()`, `funnel()`, and `feedback()`. A more robust approach would tag bridge nodes via `meta={"_bridge": True}` and check that in decompile. Low priority: naming convention is stable and internally generated.
155
+ - **Feedback effect perpetually dirty status (Phase 8.1/8.2, noted 2026-04-06 QA):** The `__feedback_effect_<condition>` node forwards DIRTY via default dispatch but consumes DATA. The effect stays in "dirty" status between emissions. Cosmetic — effects don't need to settle. `describe()` may show "dirty" for feedback effects between cycles. Not a bug.
156
+ - **Reduction primitives cross-language parity (Phase 8.1, noted 2026-04-06, QA 2026-04-06, updated 8.2 2026-04-06):** Both TS and PY implement `stratify`, `funnel`, `feedback`, `budget_gate`/`budgetGate`, and `scorer` in `patterns/reduction`. All five follow the orchestration factory pattern (`_base_meta`, `_register_step`). Key alignment: (1) `stratify` buffers DIRTY until DATA arrives — on classifier miss, emits `[DIRTY, RESOLVED]` to preserve spec §1.3.1 (both). (2) `funnel` now uses `bridge()` from `core/bridge` for inter-stage wiring — graph-visible effect nodes (`__bridge_<from>→<stage>_input`) replace the old subscribe-based bridges. Visible in `describe()`, participates in two-phase push. (3) `feedback` counter node is source of truth (resettable via `graph.set()`); feedback wiring now uses a graph-registered effect node (`__feedback_effect_<condition>`) instead of raw subscribe. Counter name is `__feedback_<condition>` to support multiple loops per graph. (4) `budget_gate`/`budgetGate` force-flushes all buffered items on terminal regardless of budget; sends RESUME before terminal if paused; forwards constraint ERROR downstream, silences constraint COMPLETE, forwards unknown constraint types via default. (5) `scorer` coerces `None`/`undefined` to 0 before multiplication (no TypeError/NaN divergence). TS `ScoredItem` is a plain object; PY `ScoredItem` is a class with `__slots__` and `__eq__`. Meta keys: `reduction: True`, `reduction_type: "<name>"`. Both repos: 22 tests each.
146
157
 
147
158
  ---
148
159
 
@@ -10,6 +10,58 @@
10
10
 
11
11
  ---
12
12
 
13
+ ## Harness Engineering Sprint — Priority Build Order
14
+
15
+ > **Context:** Mirrors the TS harness engineering sprint (`graphrefly-ts/docs/roadmap.md`). Python tracks TS for core parity on the audit/accountability layer (§9.2). Eval harness is TS-primary (corpus, rubrics, runner). MCP server and framework infiltration packages are TS-only. Python focus: §9.2 parity + §8.2 rearchitecture debt + backpressure.
16
+ >
17
+ > **Design reference:** `~/src/graphrefly-ts/archive/docs/SESSION-harness-engineering-strategy.md`
18
+
19
+ ### Wave 1 (Python): Eval parity prep (Weeks 1-3)
20
+
21
+ No new Python deliverables — Wave 1 is TS-primary (eval runner, CI, blog). Python work during this window:
22
+
23
+ #### 9.0 — Architecture debt (8.2 rearchitecture) [HIGH PRIORITY]
24
+
25
+ These fix protocol violations identified post-8.2 ship. Must land before §9.2 audit layer, which assumes clean two-phase propagation on all paths.
26
+
27
+ - [x] Rearchitect `feedback()` as graph-visible bridge node (8.2 → 9.0) — replaces subscribe-based shortcut; enables proper DIRTY→DATA two-phase on reentry/counter; resolves bare-DATA protocol gap
28
+ - [x] Rearchitect `funnel()` bridges as graph-visible nodes (8.2 → 9.0) — replaces subscribe forwarding; resolves §5.9 imperative trigger violation + teardown leak
29
+ - [ ] `stratify` two-dep gating (8.2 → 9.0) — gate classification on both source and rules settling (eliminates stale-rules race)
30
+
31
+ ### Wave 2 (Python): Audit & accountability parity (Weeks 4-9)
32
+
33
+ #### 9.2 — Audit & accountability (8.4 → 9.2) — TS parity
34
+
35
+ - [ ] `explain_path(graph, from_node, to_node)` — walk backward through graph derivation chain. Human + LLM readable causal chain. (8.4 → 9.2)
36
+ - [ ] `audit_trail(graph, opts)` → Graph — wraps graph with `reactive_log` recording every mutation, actor, timestamp, causal chain. (8.4 → 9.2)
37
+ - [ ] `policy_enforcer(graph, policies)` — reactive constraint enforcement. Policies are nodes. Violations → alert subgraph. (8.4 → 9.2)
38
+ - [ ] `compliance_snapshot(graph)` — point-in-time export for regulatory archival. (8.4 → 9.2)
39
+
40
+ #### 9.2b — Backpressure protocol (8.5 → 9.2b)
41
+
42
+ TS has this shipped; Python needs parity for production reduction pipelines.
43
+
44
+ - [ ] Backpressure protocol — formalize PAUSE/RESUME for throughput control across graph boundaries (8.5 → 9.2b)
45
+
46
+ ### Wave 3 (Python): Polish & publish (Weeks 10-15)
47
+
48
+ - [ ] `llms.txt` for AI agent discovery (7 → 9.3)
49
+ - [ ] PyPI publish: `graphrefly-py` (7 → 9.3)
50
+ - [ ] Docs site at `py.graphrefly.dev` (7 → 9.3)
51
+
52
+ ### Deferred (post-Wave 3)
53
+
54
+ - §7.2 Showcase demos (Pyodide/WASM lab) — after TS demos prove the pattern
55
+ - §7.2b Universal reduction demos — after Demo 0 + Demo 6
56
+ - §7.3 Scenario tests — after demos
57
+ - §7.4 Inspection stress tests
58
+ - §7.5 Foreseen building blocks
59
+ - §8.5 `peer_graph`, `sharded_graph`, adaptive sampling — distributed scale
60
+ - §6.2 V2 schema, §6.3 V3 caps+refs — versioning depth
61
+ - Free-threaded Python 3.14 benchmark suite — perf exploration
62
+
63
+ ---
64
+
13
65
  ## Phase 0: Foundation
14
66
 
15
67
  ### 0.1 — Project scaffold
@@ -497,12 +549,12 @@ Composable building blocks between sources and sinks.
497
549
 
498
550
  Pre-wired graphs for common "info → action" domains. Users fork/extend.
499
551
 
500
- - [ ] `observability_graph(opts)` → Graph — OTel ingest → stratified reduction → correlation → SLO verification → alert prioritization → sink
501
- - [ ] `issue_tracker_graph(opts)` → Graph — findings → extraction → verifiable assertions → regression detection → distillation → prioritized queue
502
- - [ ] `content_moderation_graph(opts)` → Graph — ingest → LLM classification → human review → feedback → policy refinement
503
- - [ ] `data_quality_graph(opts)` → Graph — DB/API ingest → schema validation → anomaly detection → drift alerting → remediation suggestions
504
- - [ ] Rearchitect `feedback()` as graph-visible bridge node (replaces subscribe-based shortcut; enables proper DIRTY→DATA two-phase on reentry/counter; resolves bare-DATA protocol gap)
505
- - [ ] Rearchitect `funnel()` bridges as graph-visible nodes (replaces subscribe forwarding; resolves §5.9 imperative trigger violation + teardown leak)
552
+ - [x] `observability_graph(opts)` → Graph — OTel ingest → stratified reduction → correlation → SLO verification → alert prioritization → sink
553
+ - [x] `issue_tracker_graph(opts)` → Graph — findings → extraction → verifiable assertions → regression detection → distillation → prioritized queue
554
+ - [x] `content_moderation_graph(opts)` → Graph — ingest → LLM classification → human review → feedback → policy refinement
555
+ - [x] `data_quality_graph(opts)` → Graph — DB/API ingest → schema validation → anomaly detection → drift alerting → remediation suggestions
556
+ - [x] Rearchitect `feedback()` as graph-visible bridge node (replaces subscribe-based shortcut; enables proper DIRTY→DATA two-phase on reentry/counter; resolves bare-DATA protocol gap)
557
+ - [x] Rearchitect `funnel()` bridges as graph-visible nodes (replaces subscribe forwarding; resolves §5.9 imperative trigger violation + teardown leak)
506
558
  - [ ] `stratify` two-dep gating: gate classification on both source and rules settling (eliminates stale-rules race when both updated in same `batch()`)
507
559
 
508
560
  ### 8.3 — LLM graph composition
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "graphrefly"
3
- version = "0.9.0"
3
+ version = "0.10.0"
4
4
  description = "Reactive graph protocol for human + LLM co-operation. Composable nodes, glitch-free diamond resolution, two-phase push, durable streaming. Zero dependencies."
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -1,5 +1,6 @@
1
1
  """Core node primitives and protocol types for graphrefly."""
2
2
 
3
+ from graphrefly.core.bridge import DEFAULT_DOWN, bridge
3
4
  from graphrefly.core.cancellation import CancellationToken, cancellation_token
4
5
  from graphrefly.core.clock import monotonic_ns, wall_clock_ns
5
6
  from graphrefly.core.dynamic_node import DynamicNodeImpl, dynamic_node
@@ -80,6 +81,8 @@ from graphrefly.core.versioning import (
80
81
 
81
82
  __all__ = [
82
83
  "Actor",
84
+ "bridge",
85
+ "DEFAULT_DOWN",
83
86
  "CancellationToken",
84
87
  "cancellation_token",
85
88
  "DynamicNodeImpl",
@@ -0,0 +1,146 @@
1
+ """bridge — graph-visible message forwarding between two nodes.
2
+
3
+ Replaces ad-hoc ``subscribe()`` bridges that bypass graph topology.
4
+ The returned node is an effect that intercepts messages from ``from_node``
5
+ and forwards them to ``to_node.down()``. Register it with ``graph.add()`` to
6
+ make the bridge visible in ``describe()`` and ``snapshot()``.
7
+
8
+ **Upstream path:** The bridge node has ``from_node`` as its dep, so anything
9
+ downstream of the bridge that calls ``up()`` naturally reaches ``from_node``.
10
+ If ``to_node`` is used as a dep by other nodes and those nodes send ``up()``,
11
+ the messages reach ``to_node``'s deps (not ``from_node``). For full upstream
12
+ relay across the bridge boundary, wire the bridge as a dep of ``to_node``'s
13
+ consumers or use ``graph.connect()``.
14
+
15
+ **ABAC / guards:** ``to_node.down()`` is called without transport options,
16
+ so any ABAC guard on ``to_node`` receives ``actor = None``. Upstream
17
+ (``up()``) messages propagate through the dep chain the same way — no actor
18
+ is injected on either path. Both paths are intentionally unguarded; if
19
+ ``to_node`` requires a specific actor, provide a guarded wrapper node and
20
+ bridge to that instead.
21
+
22
+ **Default forwarding:** All standard message types are forwarded by
23
+ default, including TEARDOWN, PAUSE, RESUME, and INVALIDATE. Use the
24
+ ``down`` option to restrict which types are forwarded. Callers that need
25
+ to exclude TEARDOWN (e.g. inter-stage wiring in ``funnel()``) pass an
26
+ explicit ``down`` sequence without TEARDOWN.
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ from typing import TYPE_CHECKING, Any
32
+
33
+ if TYPE_CHECKING:
34
+ from collections.abc import Sequence
35
+
36
+ from graphrefly.core.node import NodeImpl, node
37
+ from graphrefly.core.protocol import MessageType
38
+
39
+ DEFAULT_DOWN: frozenset[MessageType] = frozenset(
40
+ {
41
+ MessageType.DATA,
42
+ MessageType.DIRTY,
43
+ MessageType.RESOLVED,
44
+ MessageType.COMPLETE,
45
+ MessageType.ERROR,
46
+ MessageType.TEARDOWN,
47
+ MessageType.PAUSE,
48
+ MessageType.RESUME,
49
+ MessageType.INVALIDATE,
50
+ }
51
+ )
52
+
53
+ # All standard message types the bridge understands. Types outside this set
54
+ # are "unknown" and must always be forwarded (spec §1.3.6).
55
+ _STANDARD_TYPES: frozenset[MessageType] = frozenset(
56
+ {
57
+ MessageType.DATA,
58
+ MessageType.DIRTY,
59
+ MessageType.RESOLVED,
60
+ MessageType.COMPLETE,
61
+ MessageType.ERROR,
62
+ MessageType.TEARDOWN,
63
+ MessageType.PAUSE,
64
+ MessageType.RESUME,
65
+ MessageType.INVALIDATE,
66
+ }
67
+ )
68
+
69
+
70
+ def bridge(
71
+ from_node: NodeImpl[Any],
72
+ to_node: NodeImpl[Any],
73
+ *,
74
+ name: str | None = None,
75
+ down: Sequence[MessageType] | None = None,
76
+ ) -> NodeImpl[Any]:
77
+ """Create a graph-visible bridge node that forwards messages.
78
+
79
+ The bridge is a real node (effect) — it shows up in ``describe()``,
80
+ participates in two-phase push, and cleans up on TEARDOWN. Register
81
+ it via ``graph.add()`` to make it part of the graph topology.
82
+
83
+ **Unknown message types** (custom domain signals not in the standard
84
+ protocol set) are always forwarded to ``to_node``, regardless of the
85
+ ``down`` option. This satisfies spec §1.3.6 ("unknown types forward
86
+ unchanged").
87
+
88
+ **COMPLETE / ERROR**: when forwarded, the bridge also transitions to
89
+ terminal state so graph-wide completion detection works correctly.
90
+
91
+ Args:
92
+ from_node: Source node to observe.
93
+ to_node: Target node to forward messages to via ``to_node.down()``.
94
+ name: Optional node name (for graph registration / describe).
95
+ down: Standard message types to forward. Default: all standard
96
+ types. Unknown (non-standard) types always forward per spec
97
+ §1.3.6 regardless of this option.
98
+
99
+ Returns:
100
+ A bridge effect node. Add to a graph with ``graph.add(name, bridge(...))``.
101
+
102
+ Example:
103
+ ```python
104
+ from graphrefly import bridge, state, Graph
105
+
106
+ a = state(0)
107
+ b = state(0)
108
+ br = bridge(a, b, name="__bridge_a_b")
109
+ g = Graph("test")
110
+ g.add("a", a)
111
+ g.add("b", b)
112
+ g.add("__bridge_a_b", br)
113
+ g.connect("a", "__bridge_a_b")
114
+ ```
115
+ """
116
+ allowed_down = frozenset(down if down is not None else DEFAULT_DOWN)
117
+
118
+ def on_message(msg: Any, dep_index: int, actions: Any) -> bool: # noqa: ARG001
119
+ t = msg[0]
120
+
121
+ # Unknown types (custom domain signals) always forward — spec §1.3.6.
122
+ if t not in _STANDARD_TYPES:
123
+ to_node.down([msg])
124
+ return True
125
+
126
+ # Known type, not in allowed_down — consume without forwarding.
127
+ if t not in allowed_down:
128
+ return True
129
+
130
+ # Forward the message to the target.
131
+ to_node.down([msg])
132
+
133
+ # For terminal types, return False so default dispatch also handles
134
+ # terminal state on the bridge itself (bridge completes when from completes).
135
+ return t is not MessageType.COMPLETE and t is not MessageType.ERROR
136
+
137
+ return node(
138
+ [from_node],
139
+ None,
140
+ on_message=on_message,
141
+ describe_kind="effect",
142
+ name=name,
143
+ )
144
+
145
+
146
+ __all__ = ["DEFAULT_DOWN", "bridge"]
@@ -3,6 +3,7 @@
3
3
  from graphrefly.patterns import (
4
4
  ai,
5
5
  cqrs,
6
+ domain_templates,
6
7
  graphspec,
7
8
  memory,
8
9
  messaging,
@@ -14,6 +15,7 @@ from graphrefly.patterns import (
14
15
  __all__ = [
15
16
  "ai",
16
17
  "cqrs",
18
+ "domain_templates",
17
19
  "graphspec",
18
20
  "memory",
19
21
  "messaging",