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.
- {graphrefly-0.9.0 → graphrefly-0.10.0}/CHANGELOG.md +8 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/PKG-INFO +1 -1
- {graphrefly-0.9.0 → graphrefly-0.10.0}/docs/optimizations.md +13 -2
- {graphrefly-0.9.0 → graphrefly-0.10.0}/docs/roadmap.md +58 -6
- {graphrefly-0.9.0 → graphrefly-0.10.0}/pyproject.toml +1 -1
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/__init__.py +3 -0
- graphrefly-0.10.0/src/graphrefly/core/bridge.py +146 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/patterns/__init__.py +2 -0
- graphrefly-0.10.0/src/graphrefly/patterns/domain_templates.py +721 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/patterns/graphspec.py +6 -2
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/patterns/reduction.py +60 -65
- graphrefly-0.10.0/tests/test_bridge.py +164 -0
- graphrefly-0.10.0/tests/test_domain_templates.py +353 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/.claude/skills/dev-dispatch/SKILL.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/.claude/skills/parity/SKILL.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/.claude/skills/qa/SKILL.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/.gemini/skills/dev-dispatch/SKILL.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/.gemini/skills/parity/SKILL.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/.github/workflows/pages.yml +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/.github/workflows/release.yml +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/.gitignore +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/.mise.toml +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/CLAUDE.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/CONTRIBUTING.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/GEMINI.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/LICENSE +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/README.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/archive/docs/DESIGN-ARCHIVE-INDEX.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/archive/docs/SESSION-access-control-actor-guard.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/archive/docs/SESSION-cross-repo-implementation-audit.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/archive/docs/SESSION-demo-test-strategy.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/archive/docs/SESSION-graphrefly-spec-design.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/archive/docs/SESSION-serialization-memory-footprint.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/archive/docs/SESSION-tier2-parity-nonlocal-forward-inner.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/archive/docs/SESSION-universal-reduction-layer.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/benchmarks/py-baseline.json +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/docs/ADAPTER-CONTRACT.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/docs/benchmark.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/docs/docs-guidance.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/docs/test-guidance.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/examples/README.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/examples/basic_counter.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/llms.txt +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/__init__.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/compat/__init__.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/compat/async_utils.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/compat/asyncio_runner.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/compat/trio_runner.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/cancellation.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/clock.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/dynamic_node.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/guard.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/meta.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/node.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/protocol.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/runner.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/subgraph_locks.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/sugar.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/timer.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/core/versioning.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/__init__.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/adapters.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/backoff.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/backpressure.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/cascading_cache.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/checkpoint.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/composite.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/cron.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/data_structures.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/resilience.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/sources.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/tier1.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/extra/tier2.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/graph/__init__.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/graph/graph.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/integrations/__init__.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/integrations/django.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/integrations/fastapi.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/patterns/ai.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/patterns/cqrs.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/patterns/memory.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/patterns/messaging.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/patterns/orchestration.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/patterns/reactive_layout/__init__.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/patterns/reactive_layout/measurement_adapters.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/patterns/reactive_layout/reactive_block_layout.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/patterns/reactive_layout/reactive_layout.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/src/graphrefly/py.typed +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/bench_core.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/conftest.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_adapter_contract.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_adapters_ingest.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_adapters_storage.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_backpressure.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_cascading_cache.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_concurrency.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_core.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_django.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_dynamic_node.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_edge_cases.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_extra_composite.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_extra_data_structures.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_extra_resilience.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_extra_sources.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_extra_sources_http.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_extra_tier1.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_extra_tier2.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_fastapi.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_graph.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_graphspec.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_guard.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_measurement_adapters.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_patterns_ai.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_patterns_cqrs.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_patterns_memory.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_patterns_messaging.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_patterns_orchestration.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_patterns_reduction.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_perf_smoke.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_protocol.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_reactive_block_layout.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_reactive_layout.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_reduction.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_regressions.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_runner.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_smoke.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_sugar.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/tests/test_versioning.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/.gitignore +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/README.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/astro.config.mjs +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/content.config.ts +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/package.json +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/pnpm-lock.yaml +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/public/llms.txt +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/py-api-sidebar.mjs +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/scripts/gen_api_docs.py +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/scripts/sync-docs.mjs +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/components/GraphreflyHero.astro +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/components/Header.astro +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/components/MobileMenuFooter.astro +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/components/PyodidePlayground.tsx +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/components/Sidebar.astro +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/components/SiteTitle.astro +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/BackoffPreset.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/BackoffStrategy.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/CheckpointAdapter.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/CircuitBreaker.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/CircuitOpenError.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/DeferWhen.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/DictCheckpointAdapter.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/DistillBundle.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/EmitStrategy.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/Extraction.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/FileCheckpointAdapter.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/HttpBundle.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/JitterMode.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/MemoryCheckpointAdapter.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/Message.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/MessageType.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/Messages.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/NodeActions.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/NodeFn.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/NodeImpl.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/NodeStatus.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/PipeOperator.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/PubSubHub.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/ReactiveIndexBundle.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/ReactiveListBundle.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/ReactiveLogBundle.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/ReactiveMapBundle.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/SqliteCheckpointAdapter.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/SubscribeHints.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/TokenBucket.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/VerifiableBundle.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/Versioned.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/WithBreakerBundle.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/WithStatusBundle.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/audit.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/batch.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/buffer.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/buffer_count.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/buffer_time.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/cached.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/checkpoint_node_value.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/circuit_breaker.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/combine.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/concat.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/concat_map.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/constant.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/debounce.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/decorrelated_jitter.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/delay.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/derived.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/dispatch_messages.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/distill.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/distinct_until_changed.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/effect.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/element_at.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/emit_with_batch.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/empty.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/exhaust_map.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/exponential.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/fibonacci.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/filter.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/find.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/first.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/first_value_from.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/flat_map.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/for_each.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_any.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_async_iter.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_awaitable.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_cron.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_event_emitter.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_fs_watch.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_git_hook.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_http.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_iter.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_mcp.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_timer.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_webhook.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/from_websocket.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/gate.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/index.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/interval.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/is_batching.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/is_phase2_message.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/is_terminal_message.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/last.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/linear.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/log_slice.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/map.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/merge.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/message_tier.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/never.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/node.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/of.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/operator.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/pairwise.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/partition_for_batch.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/pausable.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/pipe.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/producer.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/propagates_to_meta.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/pubsub.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/race.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/rate_limiter.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/reactive_index.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/reactive_list.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/reactive_log.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/reactive_map.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/reduce.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/repeat.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/replay.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/rescue.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/resolve_backoff_preset.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/restore_graph_checkpoint.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/retry.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/sample.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/save_graph_checkpoint.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/scan.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/share.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/skip.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/start_with.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/state.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/subscribe.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/switch_map.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/take.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/take_until.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/take_while.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/tap.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/throttle.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/throw_error.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/timeout.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/to_array.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/to_list.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/to_sse.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/to_websocket.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/token_bucket.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/token_tracker.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/verifiable.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/window.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/window_count.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/window_time.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/with_breaker.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/with_latest_from.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/with_max_attempts.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/with_status.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/api/zip.md +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/index.mdx +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content/docs/lab/python.mdx +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/content.config.ts +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/env.d.ts +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/src/styles/custom.css +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/theme-prototypes.html +0 -0
- {graphrefly-0.9.0 → graphrefly-0.10.0}/website/tsconfig.json +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: graphrefly
|
|
3
|
-
Version: 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
|
-
-
|
|
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
|
-
- **
|
|
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
|
-
- [
|
|
501
|
-
- [
|
|
502
|
-
- [
|
|
503
|
-
- [
|
|
504
|
-
- [
|
|
505
|
-
- [
|
|
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.
|
|
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",
|