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.
- {graphrefly-0.16.0 → graphrefly-0.18.0}/.claude/skills/dev-dispatch/SKILL.md +1 -1
- {graphrefly-0.16.0 → graphrefly-0.18.0}/CHANGELOG.md +16 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/CLAUDE.md +2 -1
- {graphrefly-0.16.0 → graphrefly-0.18.0}/PKG-INFO +1 -1
- {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/optimizations/qa-design-decisions.jsonl +1 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/optimizations/summary-table.jsonl +2 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/docs/optimizations.md +13 -7
- {graphrefly-0.16.0 → graphrefly-0.18.0}/pyproject.toml +1 -1
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/__init__.py +0 -1
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/node.py +8 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/__init__.py +0 -2
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/adapters.py +9 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/composite.py +8 -9
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/data_structures.py +164 -179
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/tier2.py +5 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/graph/graph.py +23 -2
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/ai.py +26 -52
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/cqrs.py +3 -6
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/harness/__init__.py +2 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/harness/loop.py +119 -58
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/harness/strategy.py +13 -7
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/harness/types.py +12 -4
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/messaging.py +24 -33
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/orchestration.py +1 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/reduction.py +2 -2
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_extra_composite.py +8 -8
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_extra_data_structures.py +38 -40
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_patterns_cqrs.py +3 -4
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_patterns_messaging.py +2 -2
- graphrefly-0.18.0/website/src/content/docs/api/ReactiveListBundle.md +24 -0
- graphrefly-0.18.0/website/src/content/docs/api/ReactiveLogBundle.md +24 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/ReactiveMapBundle.md +7 -6
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/flat_map.md +5 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/index.md +0 -1
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/node.md +3 -4
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/reactive_list.md +2 -2
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/reactive_log.md +1 -1
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/reactive_map.md +2 -2
- graphrefly-0.16.0/website/src/content/docs/api/ReactiveListBundle.md +0 -25
- graphrefly-0.16.0/website/src/content/docs/api/ReactiveLogBundle.md +0 -25
- {graphrefly-0.16.0 → graphrefly-0.18.0}/.claude/skills/parity/SKILL.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/.claude/skills/qa/SKILL.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/.gemini/skills/dev-dispatch/SKILL.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/.gemini/skills/parity/SKILL.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/.github/workflows/pages.yml +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/.github/workflows/release.yml +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/.gitignore +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/.mise.toml +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/CONTRIBUTING.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/GEMINI.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/LICENSE +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/README.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/TRASH/EmitStrategy.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/TRASH/emit_with_batch.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/TRASH-FILES.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/docs/DESIGN-ARCHIVE-INDEX.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/docs/SESSION-access-control-actor-guard.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/docs/SESSION-cross-repo-implementation-audit.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/docs/SESSION-demo-test-strategy.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/docs/SESSION-graphrefly-spec-design.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/docs/SESSION-serialization-memory-footprint.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/docs/SESSION-tier2-parity-nonlocal-forward-inner.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/docs/SESSION-universal-reduction-layer.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/docs/design-archive-index.jsonl +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/optimizations/built-in-optimizations.jsonl +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/optimizations/cross-language-notes.jsonl +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/optimizations/parity-fixes.jsonl +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/optimizations/resolved-decisions.jsonl +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/roadmap/phase-0-foundation.jsonl +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/roadmap/phase-1-graph-container.jsonl +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/roadmap/phase-2-extra.jsonl +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/roadmap/phase-3-resilience-data.jsonl +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/roadmap/phase-4-domain-layers.jsonl +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/roadmap/phase-5-framework-distribution.jsonl +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/roadmap/phase-6-versioning.jsonl +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/roadmap/phase-7-polish.jsonl +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/roadmap/phase-8-reduction-layer.jsonl +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/archive/roadmap/phase-9-harness-sprint.jsonl +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/benchmarks/py-baseline.json +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/docs/ADAPTER-CONTRACT.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/docs/benchmark.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/docs/docs-guidance.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/docs/roadmap.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/docs/test-guidance.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/examples/README.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/examples/basic_counter.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/llms.txt +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/__init__.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/compat/__init__.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/compat/async_utils.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/compat/asyncio_runner.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/compat/trio_runner.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/bridge.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/cancellation.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/clock.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/dynamic_node.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/guard.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/meta.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/protocol.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/runner.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/subgraph_locks.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/sugar.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/timer.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/core/versioning.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/backoff.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/backpressure.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/cascading_cache.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/checkpoint.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/cron.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/resilience.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/sources.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/extra/tier1.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/graph/__init__.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/integrations/__init__.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/integrations/django.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/integrations/fastapi.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/__init__.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/domain_templates.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/graphspec.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/harness/bridge.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/memory.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/reactive_layout/__init__.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/reactive_layout/measurement_adapters.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/reactive_layout/reactive_block_layout.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/patterns/reactive_layout/reactive_layout.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/src/graphrefly/py.typed +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/bench_core.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/conftest.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_adapter_contract.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_adapters_ingest.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_adapters_storage.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_backpressure.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_bridge.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_cascading_cache.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_concurrency.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_core.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_django.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_domain_templates.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_dynamic_node.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_edge_cases.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_extra_resilience.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_extra_sources.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_extra_sources_http.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_extra_tier1.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_extra_tier2.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_fastapi.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_graph.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_graphspec.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_guard.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_measurement_adapters.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_operator_protocol_matrix.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_patterns_ai.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_patterns_harness.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_patterns_memory.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_patterns_orchestration.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_patterns_reduction.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_perf_smoke.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_protocol.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_reactive_block_layout.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_reactive_layout.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_reduction.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_regressions.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_runner.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_smoke.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_sugar.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/tests/test_versioning.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/.gitignore +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/README.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/astro.config.mjs +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/content.config.ts +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/package.json +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/pnpm-lock.yaml +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/public/llms.txt +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/py-api-sidebar.mjs +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/scripts/gen_api_docs.py +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/scripts/sync-docs.mjs +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/components/GraphreflyHero.astro +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/components/Header.astro +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/components/MobileMenuFooter.astro +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/components/PyodidePlayground.tsx +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/components/Sidebar.astro +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/components/SiteTitle.astro +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/BackoffPreset.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/BackoffStrategy.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/CheckpointAdapter.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/CircuitBreaker.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/CircuitOpenError.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/CompactEntry.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/DeferWhen.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/DictCheckpointAdapter.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/DistillBundle.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/DownStrategy.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/Extraction.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/FileCheckpointAdapter.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/HttpBundle.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/JitterMode.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/MemoryCheckpointAdapter.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/Message.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/MessageType.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/Messages.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/NodeActions.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/NodeFn.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/NodeImpl.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/NodeStatus.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/PipeOperator.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/PubSubHub.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/ReactiveIndexBundle.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/SqliteCheckpointAdapter.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/SubscribeHints.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/TimeoutError.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/TokenBucket.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/VerifiableBundle.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/Versioned.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/WithBreakerBundle.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/WithStatusBundle.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/audit.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/batch.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/buffer.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/buffer_count.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/buffer_time.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/cache.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/cached.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/checkpoint_node_value.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/circuit_breaker.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/combine.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/concat.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/concat_map.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/constant.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/debounce.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/decorrelated_jitter.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/delay.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/derived.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/dispatch_messages.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/distill.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/distinct_until_changed.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/down_with_batch.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/effect.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/element_at.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/empty.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/exhaust_map.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/exponential.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/fallback.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/fibonacci.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/filter.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/find.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/first.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/first_value_from.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/for_each.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_any.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_async_iter.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_awaitable.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_cron.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_event_emitter.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_fs_watch.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_git_hook.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_http.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_iter.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_mcp.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_timer.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_webhook.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/from_websocket.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/gate.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/interval.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/is_batching.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/is_phase2_message.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/is_terminal_message.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/last.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/linear.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/log_slice.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/map.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/merge.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/message_tier.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/never.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/of.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/operator.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/pairwise.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/partition_for_batch.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/pausable.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/pipe.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/producer.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/propagates_to_meta.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/pubsub.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/race.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/rate_limiter.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/reactive_index.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/reduce.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/repeat.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/replay.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/rescue.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/resolve_backoff_preset.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/restore_graph_checkpoint.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/retry.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/sample.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/save_graph_checkpoint.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/scan.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/share.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/skip.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/start_with.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/state.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/subscribe.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/switch_map.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/take.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/take_until.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/take_while.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/tap.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/throttle.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/throw_error.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/timeout.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/timeout_node.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/to_array.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/to_list.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/to_sse.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/to_websocket.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/token_bucket.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/token_tracker.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/valve.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/verifiable.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/window.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/window_count.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/window_time.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/with_breaker.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/with_latest_from.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/with_max_attempts.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/with_status.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/api/zip.md +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/index.mdx +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content/docs/lab/python.mdx +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/content.config.ts +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/env.d.ts +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/src/styles/custom.css +0 -0
- {graphrefly-0.16.0 → graphrefly-0.18.0}/website/theme-prototypes.html +0 -0
- {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
|
|
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** —
|
|
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.
|
|
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:
|
|
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 —
|
|
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
|
|
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
|
|
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
|
|
69
|
-
| **`fromRedisStream` / `from_redis_stream` does not disconnect client** | Documented
|
|
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.
|
|
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"
|
|
@@ -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.
|
|
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.
|
|
219
|
+
[store.entries, context_node],
|
|
222
220
|
lambda deps, _a: _pack_compact(
|
|
223
|
-
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.
|
|
232
|
-
lambda deps, _a: len(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
|
-
|
|
264
|
-
|
|
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):
|