mirror of
https://github.com/ruvnet/ruflo.git
synced 2026-10-23 03:47:00 +09:00
main
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
19a569b1a7 |
feat(federation): ADR-097 phases 2.a/2.b/3-up/4 + ADR-104 transport (#1876)
* feat(federation): ADR-097 Phase 2.a — peer state machine entity layer FederationNode entity gains a state field (ACTIVE/SUSPENDED/EVICTED) plus suspend()/evict()/reactivate() methods. All transitions flow through a canTransition() table in the new federation-node-state.ts value object — self-loops are rejected (so a duplicate suspend can't reset the auto-eviction grace timer), EVICTED is terminal under breaker flow, operator reactivate is the only escape. Also ships pure helpers isCooldownElapsed + shouldAutoEvict so the Phase 2.b breaker service (separate file, deferred) can drive transitions deterministically without owning the clock. 27 new tests pin: legal/illegal edges, no-op invariant on duplicate suspend, EVICTED terminality, props round-trip preservation, cooldown boundaries, auto-evict grace > cooldown. Phase 2.b (breaker service wiring cost-tracker → suspend; outbound short-circuit on !isActive) and Phase 4 (federation_breaker_status MCP + doctor surface) remain deferred per ADR-097 status table. Co-Authored-By: RuFlo <ruv@ruv.net> * feat(federation): ADR-097 Phase 2.b — breaker service + outbound short-circuit Two-layer breaker shipping the active-defense half of the circuit breaker: 1. **evaluatePolicy** (pure) — given rolling samples + node + policy + now, returns SUSPEND/EVICT/REACTIVATE_ELIGIBLE/NONE. Cost-takes-priority over failure-ratio when both trip (cost is the harder-to-fake signal). Auto-evict supersedes reactivate-eligible from SUSPENDED. 2. **FederationBreakerService** — owns per-peer rolling buffer (bounded, default 100k samples), wraps evaluatePolicy with entity mutation, exposes recordOutcome/evaluate/tick/snapshot/forget. Crucially does NOT auto-reactivate — the breaker only signals eligibility; the integrator's health probe owns the reactivate() call. This avoids the "false-positive recovery" failure mode where a peer flaps SUSPENDED↔ ACTIVE on transient telemetry. Coordinator wiring: sendMessage now short-circuits on !peer.isActive BEFORE session lookup, with PEER_SUSPENDED / PEER_EVICTED constant-string errors (no remaining-budget echo per the Phase 1 anti-oracle posture). Security invariants pinned by 25 new tests: - Negative tokens/usd clamped to 0 (no credit-inflation attack) - Failure ratio gated on minSamples=10 (anti-noise) - Strict > on threshold comparisons (boundary correctness) - EVICTED peers always return NONE (terminal under breaker flow) - apply=false dry-run preserves entity state Phase 4 (federation_breaker_status MCP + ruflo doctor surface) is now unblocked: snapshot() + node.stateRecord give it everything it needs. Phase 3 upstream (federation_spend bus emission) remains separate work — when it lands, the bus subscriber calls recordOutcome() and the in-memory buffer becomes a transparent cache. No breaker code changes. Witness manifest: 87 fixes (was 85), Phase2b + Phase2b-coordinator markers signed. Co-Authored-By: RuFlo <ruv@ruv.net> * chore(release): @claude-flow/plugin-agent-federation@1.0.0-alpha.6 Ships ADR-097 Phase 2.a (peer state machine) + Phase 2.b (breaker service + outbound short-circuit). Published to npm dist-tag alpha; latest is held at alpha.5 until the breaker bakes for one /loop cycle. Verified: tarball contains dist/application/federation-breaker-service.js, dist/domain/value-objects/federation-node-state.js, and PEER_EVICTED short-circuit in dist/application/federation-coordinator.js. All three witness markers present in the published artifact. Co-Authored-By: RuFlo <ruv@ruv.net> * feat(federation): ADR-097 Phase 4 — operator surface (3 MCP tools + doctor) Closes the loop on the breaker so operators can see and operate it: **3 new MCP tools** (mcp-tools.ts): - federation_breaker_status — per-peer state snapshot + bucket counts; optional nodeId filter - federation_evict — operator-initiated MANUAL_EVICT with audit log + session-termination side effect; idempotent (already-evicted returns false but still emits audit for operator visibility) - federation_reactivate — escape hatch from SUSPENDED (after probe) or EVICTED (operator override) **Coordinator additions** (federation-coordinator.ts): - getPeerStates() — projects stateRecord over discovery.listPeers() - getPeerStateCounts() — O(peers) bucket count for status lines - evictPeer() / reactivatePeer() — async wrappers that audit-log every outcome (applied:true/false in metadata) **Doctor health-check** (cli/doctor.ts): - checkFederationBreaker — probes plugin loadability + asserts FederationNodeState export presence (catches version-skew where users have a pre-Phase-2 plugin installed). Optional plugin → "not installed" is a pass, not a fail. Selectable as `--component federation`. **Plugin index exports** — FederationNodeState + breaker primitives so external integrators (and the doctor probe) can detect Phase 2/4 support. 11 new coordinator tests pin: bucket-count math, audit emission on both success/no-op evict paths, EVICTED → ACTIVE operator override, unknown-peer no-op (no audit silence). 60 plugin-level tests updated to assert the 12-tool count. Witness markers added: ADR-097-Phase4-mcp + ADR-097-Phase4-doctor. 89 signed fixes in the manifest (was 87). Published @claude-flow/plugin-agent-federation@1.0.0-alpha.7 to npm alpha tag. Tarball validated: all 7 markers (Phase 2.a + 2.b + 4) present in dist/. ADR-097 status: Phases 1, 2.a, 2.b, 3-consumer, and 4 all Implemented. Only Phase 3 upstream (federation_spend bus emission) remains deferred — when it lands, the breaker's in-memory rolling buffer becomes a transparent cache with zero breaker code changes. Co-Authored-By: RuFlo <ruv@ruv.net> * feat(federation): ADR-097 Phase 3 upstream — closes ADR-097 100% The last remaining "Deferred" entry from ADR-097's status table. With this, the breaker has end-to-end telemetry: integrators call coordinator.reportSpend() after each downstream agent completes, and the event fans out to TWO subscribers in parallel: 1. SpendReporter — pushes to whatever durable backend the integrator wires (cost-tracker memory namespace, Datadog, accounting DB). The event shape matches the cost-tracker consumer in plugins/ruflo-cost-tracker/scripts/federation.mjs verbatim: { peerId, taskId, tokensUsed, usdSpent, ts, success } 2. FederationBreakerService.recordOutcome — feeds the breaker's in-memory rolling buffer so cost/failure-ratio thresholds fire correctly. Without this fan-out, integrators would have to wire two parallel pipelines for the same telemetry. Both integrations are constructor-optional. A coordinator with neither still accepts reportSpend() calls (silent no-op) so callers don't branch on configuration. **New files:** - application/spend-reporter.ts — SpendReporter interface, FederationSpendEvent type, InMemorySpendReporter reference impl - __tests__/unit/coordinator-spend-reporting.test.ts — 10 tests pinning fan-out, contract shape, ts auto-fill, throw-bubble **New MCP tool:** federation_report_spend (13 tools total) **Coordinator constructor:** added optional 9th arg `integrations: { spendReporter?, breakerService? }` — backward-compat since omission preserves the existing 8-arg shape. **Anti-coupling design:** federation doesn't own model pricing; the caller owns it. reportSpend() is an explicit POST-completion call, not auto-emitted from federation_send. Future enhancement could auto-emit a "predicted cost" placeholder, but deferred until a concrete request justifies the additional state. **Witness:** 90 signed fixes (was 89). Phase 3 upstream marker added. **Published:** @claude-flow/plugin-agent-federation@1.0.0-alpha.8 to npm alpha tag. Tarball validated — all 8 markers (Phase 2.a + 2.b + 3-upstream + 4) present. ADR-097 status: ALL FIVE PHASES IMPLEMENTED (1, 2.a, 2.b, 3-consumer + 3-upstream, 4). Future work is integrator-side (production SpendReporter adapter that persists to ruflo memory; auto-emit predicted-cost placeholder). Co-Authored-By: RuFlo <ruv@ruv.net> * chore(release): umbrella alpha.20 — ships ADR-097 Phase 4 doctor surface Bumps + publishes the 3-package umbrella chain: - @claude-flow/cli@3.7.0-alpha.20 - claude-flow@3.7.0-alpha.20 - ruflo@3.7.0-alpha.20 All three on alpha + latest + v3alpha tags per CLAUDE.md publishing rules. Static check confirms `checkFederationBreaker` ships in the published @claude-flow/cli@alpha dist (3 occurrences in package/dist/src/commands/doctor.js). Now visible to users: - `npx ruflo@latest doctor --component federation` reports plugin loadability + version-skew detection (catches users running an older pre-Phase-2 federation plugin against the new MCP tools) Federation plugin already at @claude-flow/plugin-agent-federation @1.0.0-alpha.8 (published earlier this session) — installs via `npm install @claude-flow/plugin-agent-federation@alpha`. Witness manifest re-signed with new release versions; 90 fixes still verified. No new fix entries since this is a release-only bump (the underlying code already shipped in the prior 3 commits today). Co-Authored-By: RuFlo <ruv@ruv.net> * docs(adr): ADR-104 — federation wire transport (agentic-flow loader, WS today, QUIC roadmap) Documents the transport selection for ADR-097 federation. Smoke test mac↔ruvultra over tailscale revealed agentic-flow's published QUIC is API-only — loadWasmModule returns {}, encode/decode are placeholders, crates/agentic-flow-quic/src/wasm.rs explicitly notes the WASM build is a stub since browsers can't do raw UDP. Decision: adopt the loadQuicTransport() loader pattern with WebSocket fallback. Same AgentTransport interface today (WS) and tomorrow (native QUIC when it lands — set AGENTIC_FLOW_QUIC_NATIVE=1). Federation code stays transport-agnostic. Validated: real mac→ruvultra:9101 over tailscale, 125ms send latency (real I/O, not the prior 0ms stub). Upstream contribution: - ruvnet/agentic-flow#153 — adds quic-loader.ts to the published inner package, with WebSocketServer-backed real fallback - Patched build published as agentic-flow@2.0.12-fix.1 (`fix` dist-tag) so federation can consume the working transport without waiting for upstream merge 12h verification routine updated with Check 8 (ADR-104 transport smoke): - Pulls agentic-flow@fix - Stands up real WebSocketServer + client send round-trip on localhost - Asserts real send latency (>0, <5000ms) — catches if a future regression re-introduces the no-op stub behavior - SEVERITY:HIGH if stub regression detected Re-open this ADR when: - ruvnet/agentic-flow ships native QUIC binding - Federation traffic exceeds ~100 RPS to any single peer - Federation peers cross outside tailnet boundary - We have measured WS-vs-QUIC switch data Co-Authored-By: RuFlo <ruv@ruv.net> * feat(federation): wire ADR-104 transport — real mac↔ruvultra peering ADR-104 documented the design (agentic-flow loader pattern, WebSocket fallback today, native QUIC roadmap). This commit wires it into the plugin so federation_send actually moves bytes on the wire. Changes to src/plugin.ts: - New dep: agentic-flow@2.0.12-fix.2 (PR ruvnet/agentic-flow#153 patched build that fixes the published QUIC stub) - initialize() loads transport via loadQuicTransport(), registers it as federation:transport service - If config.port is set, transport.listen(port, host='0.0.0.0') binds an inbound listener (peers that only initiate outbound leave port unset) - sendToNode callback now resolves peer.endpoint via discovery.getPeer, strips the protocol prefix (ws://, tailscale://), and calls transport.send(address, AgentMessage{id, type, payload, metadata}) - shutdown() closes the transport (terminates outbound + inbound clients, awaits server close cb) - Graceful degrade: if transport load fails, plugin logs a warn and continues with the legacy in-process noop (preserves backward compat for tests/sandboxes without ws available) Validated end-to-end on real hardware: ruvultra (linux/x64): plugin booted, listening on 0.0.0.0:9101 mac (darwin/arm64): plugin booted, sent envelope in 150ms over tailscale Witness manifest: 91 fixes (was 90), ADR-104-transport marker added. Service-count assertion bumped 7 → 8 (added federation:transport). All 450 federation tests still pass (transport load is async + safe in the mocked test PluginContext). Published: @claude-flow/plugin-agent-federation@1.0.0-alpha.9 to npm alpha tag. Tarball validated: dist/plugin.js contains 'loadQuicTransport' + 'agentic-flow/transport/loader' import + 'federation:transport' service registration + dep agentic-flow@2.0.12-fix.2. Co-Authored-By: RuFlo <ruv@ruv.net> * fix(deps): regenerate pnpm-lock — was out of sync since alpha.9 (agentic-flow dep adds) * chore: bump agentic-flow dep to 2.0.12-fix.8 to match lockfile (CI fix) --------- Co-authored-by: Reuven <cohen@ruv-mac-mini.local> |
||
|
|
3f35608a98 |
docs(adr): ADR-097/098/099/100 status — Proposed → actual implementation state
- ADR-097: Partially Implemented — Phase 1 (budget envelope + hop counter, commit |
||
|
|
62a6fc5fb0 |
docs(adr): ADR-097 — federation-wide budget circuit breaker (#1723)
Closes design half of #1723 (and dup #1724). Builds on ADR-086 (Agent
Federation) and the G2 work that landed real Ed25519 signing in
|