Skip to content

Merge it#1

Open
santyr wants to merge 276 commits intohexdaemon:mainfrom
lightning-goats:main
Open

Merge it#1
santyr wants to merge 276 commits intohexdaemon:mainfrom
lightning-goats:main

Conversation

@santyr
Copy link

@santyr santyr commented Feb 6, 2026

No description provided.

Copy link
Owner

@hexdaemon hexdaemon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Approving.

santyr and others added 29 commits February 8, 2026 07:55
- Add vitality-amboss=true to docker-entrypoint.sh config generation
- Add vitality-watch-channels=true for channel health monitoring
- Add vitality-expiring-htlcs=50 for HTLC expiry warnings
- Update Dockerfile comment to document Amboss integration
…AttributeError

Critical fixes across 5 modules:

- mcf_solver: MCFCircuitBreaker.get_status() race condition — can_execute()
  called outside lock returned stale value; refactored to _can_execute_unlocked()
  called atomically within lock
- liquidity_coordinator: 8 thread safety fixes — missing locks on get_status(),
  get_pending_mcf_assignments(), get_mcf_assignment(), update_mcf_assignment_status(),
  create_mcf_ack_message(), create_mcf_completion_message(), get_mcf_status();
  deadlock fix (non-reentrant lock + nested call); new claim_pending_assignment()
  atomic method to prevent TOCTOU double-claim race
- cl-hive.py: _send_mcf_ack() TypeError — create_mcf_ack_message() takes no
  params but was called with 4 positional args; sendcustommsg keyword args fix;
  broadcast_intent_abort NameError (plugin → safe_plugin); missing coordinator
  check in handle_mcf_completion_report; TOCTOU claim race replaced with atomic
  claim_pending_assignment()
- cost_reduction: CircularFlow AttributeError (cf.members_count → cf.cycle_count);
  hub scoring division-by-zero guard; record_mcf_ack() thread safety with
  dedicated lock and proper __init__ initialization
- intent_manager: get_intent_stats() race — _remote_intents read without lock

25 new tests covering all fixes including concurrent access verification.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…or, key mismatch

- anticipatory_liquidity.py: Lock created but NEVER used — add `with self._lock:`
  around ALL shared cache access (flow_history, pattern_cache, prediction_cache,
  kalman_velocities, peer_to_channels, intraday_cache, channel_peer_map,
  remote_patterns). Initialize _intraday_cache, _channel_peer_map, _remote_patterns
  in __init__ instead of lazy hasattr() pattern (race condition). Add per-channel
  flow sample limit (MAX_FLOW_SAMPLES_PER_CHANNEL=2000). Fix aggregate uncertainty
  denominator guard (max(0.001, inv_var_sum) prevents near-zero sqrt).

- yield_metrics.py: Add threading.Lock for _velocity_cache and _remote_yield_metrics.
  Handle missing database.get_channel_history() gracefully (hasattr check instead of
  AttributeError). Remove redundant hasattr checks for _remote_yield_metrics since
  it's now always initialized in __init__.

- liquidity_coordinator.py: Clamp health_score to [0, 100] in NNLB nnlb_priority()
  to prevent negative priority values from corrupted health data.

- cl-hive.py: Update anticipatory_liquidity_mgr channel-to-peer mappings alongside
  fee_coordination_mgr in pheromone broadcast loop (mappings were never populated).

- 35 new tests covering thread safety, init, missing methods, clamping, key names.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
splice_manager.py:
- Check create_splice_session return value (P1: silent failure on DB error
  left orphan sessions, now returns error to caller)
- Notify peer via SPLICE_ABORT on unknown session lookup (P1: peer would
  wait indefinitely when session_id mismatch occurred)
- Validate amount bounds in initiate_splice (max 21M BTC)
- Validate state transition in _proceed_to_signing (reject terminal states)

database.py:
- Validate status against VALID_SPLICE_STATUSES in update_splice_session
- Validate initiator ('local'/'remote'), splice_type, and amount_sats
  in create_splice_session

cl-hive.py:
- Add ban check (database.is_banned) to all splice handlers, not just
  SPLICE_INIT_REQUEST — banned peers were able to continue in-progress
  splices via SPLICE_UPDATE, SPLICE_SIGNED, SPLICE_ABORT

Fund ownership: Verified funds remain separate — each node controls
only its own PSBT via CLN HSM, no cross-node fund movement possible.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…read safety

CRITICAL: Add is_banned() checks to GOSSIP, INTENT, STATE_HASH, FULL_SYNC
handlers that previously only verified membership but not ban status.

HIGH: Reject ban votes from banned voters. Clear intent locks when member
is banned. Prevent marker depositor attribution spoofing by forcing depositor
to match authenticated reporter_id. Use config snapshot in process_ready_intents
to avoid reading mutable config mid-cycle.

MEDIUM: Fix marker strength race condition in read_markers (acquire lock).
Bound marker strength to [0,1] on gossip receipt. Bound pheromone level_weight
to prevent extreme values. Protect bridge _policy_last_change dict with lock
and guard min() against empty dict crash.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix governance modes: autonomous → failsafe across all docs
- Add 22 missing modules to CLAUDE.md (now 39 total)
- Add missing features: stigmergic markers, settlement, idempotent delivery, routing intelligence, budget manager
- Update database tables (9 → 46), background loops (4 → 8)
- Update test count (1,340 tests, 46 files), Python prereq (3.10+)
- Add CHANGELOG entries for 10 recent bug fix commits
- Add recent hardening summary to README

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
These settings must be in the CLN config at startup because runtime
setconfig on plugin-owned options crashes CLN v25.12.1 with a segfault
in configvar_finalize_overrides.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
P0 fix: Add failure_reason parameter to hive-report-rebalance-outcome
RPC handler — cl_revenue_ops sends this kwarg but cl-hive didn't accept
it, causing TypeError crashes in production.

Replace raw invoice/sendpay/waitsendpay execution in
execute_hive_circular_rebalance() with bridge.safe_call() delegation to
cl-revenue-ops, gaining sling's retries, parallelism, and budget
enforcement. Dry-run route preview preserved.

Add max_fee_sats parameter to bridge.trigger_rebalance() so fleet
zero-fee routes can pass a nominal fee cap through to sling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The fleet_path list contains intermediate hive members that bridge
from_peer to to_peer, but those members aren't necessarily our direct
peers — so cl_revenue_ops couldn't resolve them to channel SCIDs for
sling source candidate injection.

Now compute source_eligible_members: fleet members we have channels with
AND that are connected to to_peer in the fleet topology. These create
ideal 2-hop zero-fee routes (us -> fleet_member -> to_peer -> us) that
sling's pathfinding will discover.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ed zeros, missing data

Critical fixes to the data pipeline that was causing the advisor DB to record
incorrect metrics:

- Fix channels_by_class key mismatch: profitability.get("channels") →
  profitability.get("channels_by_class") with proper nested dict flattening
  (advisor snapshot, revenue profitability handler, fleet snapshot)
- Fix hardcoded forward_count: 0 in advisor snapshot — now extracts real
  forward counts from profitability response
- Fix flow_ratio key: roi_annual_pct → roi_percentage (correct field name)
- Fix channel_deep_dive profitability: parse single-channel response format
  {channel_id, profitability: {...}} instead of non-existent {channels: [...]}
- Fix fleet_snapshot issue detection: "bleeder" classification doesn't exist
  in revenue-profitability — use underwater/zombie/stagnant_candidate instead
- Fix fee field access in channel_deep_dive and hive-monitor: fees are nested
  under channel.updates.local, not at top level of channel object
- Fix opex_breakdown: extract real rebalance/closure/splice costs from
  dashboard period data instead of hardcoding zeros
- Replace silent except:pass with logged warnings across 4 exception handlers
- Enrich handle_channels with forward_count, fees_earned, volume_routed

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…recation warnings

- Filter non-CHANNELD_NORMAL channels from fleet snapshots (fixes zero balance recording)
- Add measure_decision_outcomes() call to Phase 8 (fixes NULL outcome_success in ai_decisions)
- Include outcome columns in advisor_get_recent_decisions response
- Replace deprecated datetime.utcfromtimestamp/utcnow with timezone-aware alternatives (6 occurrences)
- Add division-by-zero protection in channel velocity calculations
- Use COALESCE for NULL-safe aggregation in fleet trend queries
- Fix get_context_brief to use latest snapshot instead of MAX aggregate
- Fix asyncio deprecation in tests (get_event_loop → new_event_loop)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Skip recording goat feeder snapshot when LNbits returns error (prevents
  zero-value entries from polluting historical data)
- Add error detection when revenue-profitability RPC returns error response
- Log warnings when profitability classification data is empty
- Skip channels with empty short_channel_id in snapshot recording

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Goat feeder is not part of cl-hive or cl-revenue-ops. Removed:
- LNbits configuration variables and validation
- get_goat_feeder_revenue() function
- revenue_outgoing, goat_feeder_history, goat_feeder_trends tools and handlers
- Goat feeder P&L section from revenue_dashboard response
- Goat feeder report section from advisor run script

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds hive_node_diagnostic, revenue_ops_health, advisor_validate_data,
advisor_dedup_status, and rebalance_diagnostic to detect regressions
in the advisor/revenue-ops data pipeline.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…giene

- Expand _measure_single_outcome type filter to include fee_change,
  rebalance, config_change, flag_for_review (was only measuring
  flag_channel/approve/reject, leaving all others at outcome_success=0)
- Add 24h dedup to record_decision() keyed on (type, node, channel)
  with node_name normalization to lowercase
- Add expire_stale_decisions(48h) and cleanup_decisions(cap=200) to
  prevent unbounded queue growth
- Add should_skip_action() learning check in _queue_for_approval() so
  repeatedly-failing actions (e.g. rebalances) stop being re-queued
- Call housekeeping at cycle start to expire/cap stale decisions
- Expand cleanup_old_data() to prune expired decisions and old outcomes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 4: Fix broken predicted_benefit data pipeline — values from
opportunity_scanner now flow through proactive_advisor → MCP server →
advisor_db → learning_engine, enabling meaningful prediction_error
feedback. Uses existing snapshot_metrics column (no schema migration).

Phase 7: Add 90 tests across 4 previously untested critical modules:
budget_manager (17), health_aggregator (15), network_metrics (17),
cooperative_expansion (24).

Phase 8: Parallelize 5 sequential RPC call sites in MCP server with
asyncio.gather() — fleet snapshot (5 calls), peer search (3), channel
deep dive (4), onboard members (3), topology analysis (3).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fee measurement: replace broken forward_count*ppm formula with
fees_earned_sats revenue-based comparison; inactive channels get neutral
outcomes instead of false failures.

Rejection tracking: store rejection reason through full MCP→RPC→DB
pipeline so planner and advisor can learn from past rejections.

Expansion loop: add MAX_CONSECUTIVE_REJECTIONS=50 hard cap to prevent
infinite propose→reject→24h→repeat cycles; log rejection reasons at
info level for operator visibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…02-10

Thread safety (H-1, H-2, M-2, M-3):
- Add threading.Lock to routing_intelligence._path_stats with snapshot pattern
- Fix hive-bump-version to use state_manager.update_local_state() instead of direct dict write
- Add _map_lock to contribution._channel_map and _lookup_peer
- Add _rate_lock to liquidity_coordinator rate dicts

Database integrity (H-3, H-8, H-9, M-11, M-12, M-13):
- Add composite indexes on pending_actions(status, expires_at) and (action_type, proposed_at)
- Wrap prune_old_settlement_data() in transaction for atomic 3-table delete
- Rewrite sync_uptime_from_presence() as JOIN query (eliminates N+1)
- Wrap update_presence() in transaction to prevent TOCTOU race
- Wrap log_planner_action() in transaction for atomic ring-buffer
- Remove inert PRAGMA foreign_keys=ON (no FK constraints exist)

Wire missing cleanup (H-4, H-5, H-6, M-25):
- Call prune_peer_events() and new prune_budget_tracking() from maintenance loop
- Call advisor_db.cleanup_old_data() at end of advisor cycle
- Bound FleetMonitor.alerts to 1000 entries

Protocol & misc (M-4, M-16, M-23, M-26):
- Guard serialize()/create_hello() None returns before .hex() calls
- Add recover_stuck_intents() for committed intents older than 5 min
- Make 1ML TLS bypass opt-in via HIVE_1ML_SKIP_TLS_VERIFY env var
- Add member permission check to create_close_actions()

Tests: 30 new tests across 3 new files + protocol additions (1463 total pass)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The intent_locks table uses `timestamp`, not `created_at`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…d safety)

- Version-guarded DB writes prevent late-arriving concurrent updates from
  overwriting newer state (ON CONFLICT WHERE excluded.version > hive_state.version)
- load_from_database() now checks versions so stale DB data won't overwrite
  newer in-memory gossip state, and reports correct loaded count
- cleanup_stale_states() now also deletes from DB so stale entries don't
  reappear after restart (adds delete_hive_state() to database.py)
- apply_full_sync() applies entire batch under single lock so concurrent
  hash calculations never see partially-applied sync state
- get_peer_state() and get_all_peer_states() return defensive copies to
  prevent callers from mutating shared state without holding the lock

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…S hardening)

- Validate intent_type against IntentType enum on creation (reject typos)
- Enforce valid status transitions via state machine (pending→committed, etc.)
- Thread-safe callback registration with _callback_lock
- Audit trail: add reason column to intent_locks, pass reasons from all callers
- Insertion-order cache eviction prevents attacker-controlled DoS eviction
- Immediate failure marking on callback exception (no 300s stuck wait)
- Soft-delete expired intents (24h audit retention before purge)
- Honor config intent_expire_seconds instead of hardcoded hold_seconds*2

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ering, missing integration)

- Remove dead _is_exchange_target method (always returned False, zero callers)
- Batch listpeerchannels in get_underserved_targets (single RPC + set lookup
  replaces O(n) per-target calls on mainnet)
- Fix _get_hive_capacity_to_target to use public channel capacity directly
  instead of incorrectly clamping against gossip capacity_sats (which is
  total hive capacity, not per-target)
- Move budget validation before intent creation in _propose_expansion to
  prevent wasting intent slots and orphaned intents on insufficient budget
- Wire CooperativeExpansionManager into Planner so topology analysis
  delegates to fleet-wide election protocol before falling back to raw intents
- Add 16 new tests: 11 for ChannelSizer, 5 for quality score filtering

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…anner deadlock

count_consecutive_expansion_rejections() had no time window, so ancient
rejections permanently inflated the counter. With 36+ consecutive rejections
the exponential backoff maxed at 24h, but after the cooldown cleared, the
next proposal would get rejected and the all-time counter stayed at 37+,
re-triggering 24h backoff indefinitely.

Add REJECTION_LOOKBACK_HOURS (7 days) so old rejections age out naturally,
allowing the planner to resume proposing after the lookback window passes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…alls, unbounded gossip, winner-take-all)

- Replace hardcoded our_fee_ppm=100 with actual channel fees from listpeerchannels
- Add missing thread locks to 6 methods in AdaptiveFeeController and MyceliumDefenseSystem
- Wire record_fee_change() into get_fee_recommendation() so salience cooldown activates
- Wire update_velocity() into fee_intelligence_loop so adaptive evaporation works
- Bound gossip pheromone fee_ppm to fleet floor/ceiling and level to [0,100]
- Cap local marker strength to [0,1] matching gossip bounds; replace winner-take-all
  with strength-weighted average in calculate_coordinated_fee()
- Cross-wire FeeIntelligenceManager into FeeCoordinationManager for blended recommendations
- Implement multi-factor weighted fee calculation using all 4 defined weight constants

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ation, thread safety)

- Rewrite _add_edges_from_topology: infer hive-to-hive full-mesh from
  available_sats instead of iterating disjoint external peer topology
  (was producing zero edges in production)
- Fix cost rounding: banker's rounding prevents sub-sat truncation to 0
  in MCFEdge.unit_cost() and SSPSolver.solve()
- Add negative cycle warning logging instead of silent return
- Cap Bellman-Ford iterations with min(n, MAX_BELLMAN_FORD_ITERATIONS)
- Add _solution_lock protecting _last_solution/_last_solution_time from
  concurrent coordinator thread and message handler writes
- Cache coordinator election with 60s TTL to avoid repeated DB queries
- Reject stale/replayed MCF solutions via timestamp validation in both
  cl-hive.py handler and MCFCoordinator.receive_solution()
- Upgrade SSP solver: first iteration uses Bellman-Ford to set Johnson
  potentials, subsequent iterations use Dijkstra (O(E log V) per path)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Prevent duplicate edges between coordinator's hive peers: channels
  method returns added pairs, topology method skips those pairs
- get_status() now uses _cached_coordinator from is_coordinator()
  instead of calling elect_coordinator() (redundant DB query)
- Update module docstring to reflect Dijkstra+Johnson hybrid algorithm

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tub coordination, thread safety)

Critical (dead code):
- Monthly pattern detection now loads 30 days (was 14, making it unreachable)
- Pattern matcher handles day_of_month patterns (monthly patterns were never used)

Correctness:
- Intra-day velocity uses actual channel capacity instead of hardcoded 10M sats
- Fleet coordination uses remote patterns instead of pass-through stub
- total_predicted_demand_sats uses velocity*hours*capacity instead of pct*1M
- Pattern velocity adjustment has effect when base_velocity is zero
- receive_pattern_from_fleet uses single lock block (was split, race condition)

Algorithm:
- Kalman consensus uses proper inverse-variance weighting (1/sigma^2)
- Risk combination uses weighted sum (0.4/0.3/0.3) instead of max()
- Long-horizon predictions (>6h) step through hourly patterns

Performance:
- Flow history eviction uses timestamp tracker instead of O(n) scan
- Flow history trims by time window first, then enforces hard limit
- Kalman velocity status batches consensus check in single lock scope

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…unts, RPC batching, dead constants)

- _pattern_name handles day_of_month patterns (day15_drain, eom_inflow)
- get_patterns_summary counts monthly patterns separately
- _get_channel_info accepts peer_id for server-side listpeerchannels filter
- get_intraday_summary batches channel capacity lookup in single RPC call
- Remove unused PREDICTION_HORIZONS and INTRADAY_PATTERN_DECAY_DAYS constants
- Wire INTRADAY_REGIME_CHANGE_THRESHOLD (2.5) into regime detection (was hardcoded 2)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
hexdaemon and others added 30 commits March 2, 2026 07:06
…execute, hive zero-fee guards, and 60 more

5 Critical: wrong rebalance param names, bulk_policy fail-open, channel close
auto_execute_safe=True, cross-transaction integrity, executescript auto-commit.
9 High: zero-fee guard on set_fee, hive member filtering in scanners/advisor,
swapped routing share fields, opportunity truncation, None guards, JSON config
deserialization, peer score div-by-zero, mcp arg splitting.
32 Medium: format string crashes, budget leaks, wrong dict keys, dead code
removal, msat parsing, hoisted RPC calls, rebalance cost condition, and more.
20 Low: error coercion, allowlist warnings, peer search, async singleton lock,
retry flag, velocity None checks, record_decision normalization, channel
velocity cleanup, truncated pubkey length, dead profitability check, integer
division precision, deadline div-by-zero, priority bounds, updates None guard.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ename, msat parsing, and more

Critical: _execute_fee_change hive guard bypassed when peer_id is None —
now always resolves peer and checks membership.
High: handle_revenue_policy missing zero-fee guard, _scan_velocity_alerts
missing hive member filter, handle_set_fees/handle_revenue_set_fee guards
fail open on RPC error dicts.
Medium: "bleeder"→"underwater" in deep dive and bulk_policy, int()→
_extract_msat() for askrene and snapshot msat values, routing_intelligence
stigmergic section missing hive filter, FLEET_DEFENSIVE_ACTION silently
dropped, new_fee_ppm falsy-check hiding valid 0, topology-reset sum() crash.
Low: splice budget double-release, new_fee falsy→None check, dead variables,
wrong default success rate, LND connect split args, fragile data variable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Prevents unbounded response payload when many auto_execute_safe
opportunities exist beyond the top-20 scored results.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…esponse

proactive_advisor: _execute_fee_change checked result.get("success") but
CLN setchannel returns {"channels": [...]}, never "success". Changed to
check for absence of "error" key. This fixes budget tracking, learning
engine outcome recording, and auto-execute reporting for fee changes.

mcp-hive-server: splice budget reservation leaked if node.call returned
a non-dict value. Added early release for non-dict responses before the
existing dict-handling block.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Zero-fee periods were never marked as settled, causing the backlog scan
to retry them every cycle. Now auto-settles zero-fee periods with 0 sats
distributed. Also fix week-number parsing in test_previous_period (int
slice was wrong for 2-digit week numbers).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…kip reasons, state contamination, cleanup format

- Delete expired proposals before re-creation instead of rejecting them
- Auto-settle no_contributions periods alongside zero_total_fees
- Block state_manager fallback for old periods to prevent current fees contaminating historical settlements
- Fix cleanup_old_fee_reports YYYY-WNN format mismatch (should be YYYY-NN)
- Add delete_settlement_proposal DB method for expired proposal cleanup

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- hive-remove-member was missing bridge.set_hive_policy(is_member=False),
  leaving stale hive strategy in cl-revenue-ops after removal
- Add startup sync to detect and revert hive policies for non-members
  (safety net for failed bridge calls during removal/ban)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Members not seen for 14+ days whose pubkey is no longer in the
network graph are automatically removed from hive_members and their
fee policy reverted. Prevents ghost members from polluting settlement
calculations and hive policies.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…e policy sync

Previous approach used a 14-day last_seen threshold which missed recently-
disconnected peers. The gossip graph already has ~2 week retention, so
absence from listnodes is sufficient signal that a node is gone.

- Extract _cleanup_ghost_members() as shared function
- Run it on startup BEFORE _sync_member_policies() so ghost members
  don't get hive strategy re-applied
- Also run hourly in maintenance loop
- Drop last_seen threshold — graph absence is the definitive check

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract _execute_member_removal() shared function that does complete cleanup:
DB removal, state_manager purge, bridge policy revert, and forced gossip
broadcast. Used by hive-remove-member, _cleanup_ghost_members, and ban
execution. Adds GossipManager.force_next_broadcast() to trigger immediate
state propagation after membership changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add _get_broadcast_targets() as single gatekeeper for all outbound member
broadcasts — filters out banned peers and self. Applied across all 20+
broadcast loops: gossip, FULL_SYNC, fee reports, MCF solutions, fee
intelligence, stigmergic markers, pheromones, yield metrics, circular flow
alerts, temporal patterns, corridor values, positioning proposals, physarum
recommendations, coverage analysis, close proposals, health reports,
liquidity needs, intent aborts, and reliable delivery outbox.

Also hardens:
- Ban execution now uses _execute_member_removal() for full cleanup
- on_peer_connected rejects banned peers (no STATE_HASH exchange)
- Auto-connect skips banned peers
- FULL_SYNC membership list excludes banned peers
- Policy sync reverts banned peers to dynamic strategy
- Relay and outbox member lists filter banned peers
- Zero-fee verification skips banned peers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
cl-hive.py passes this kwarg during init but the field was never added to
the HiveConfig dataclass, HiveConfigSnapshot, CONFIG_FIELD_TYPES, or
CONFIG_FIELD_RANGES. Caused TypeError on plugin startup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Systematic audit of expansion proposal pipeline identified 6 issues:
wrong channel counts fed to AI advisor, approval criteria mismatch,
incomplete payloads, permanent rejection backoff stall, fragile cache
consumers, and missing profitability awareness.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… tasks)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…counting

Computes pre-filtered channel counts from listpeerchannels so the AI advisor
gets accurate active/pending/closing counts instead of deriving from raw data.
Includes underwater channel metrics from cl-revenue-ops bridge.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…cache dedup, criteria alignment

Fix 1+3: Inject pre-computed node_summary into both pending_action paths
  (DecisionEngine context and advisor fallback payload). Fallback path
  now includes target_channel_count, quality_score, quality_recommendation.

Fix 2: Align approval_criteria.md >50 channel threshold to ESCALATE
  (matching code behavior) instead of REJECT.

Fix 4: Remove 24h backoff cap that caused permanent rejection stall.
  Use REJECTION_LOOKBACK_HOURS (168h) as natural ceiling so backoff
  windows can grow past 24h and old rejections age out.

Fix 6: Add get_unique_channels_for() deduplicating accessor for network
  cache. Update _get_public_capacity_to_target() to use it, preventing
  double-counting from bidirectional cache indexing.

13 new tests covering all fixes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix 5: Block expansion proposals when >40% of channels are underwater,
  matching approval_criteria.md DEFER threshold. Prevents proposals doomed
  to rejection from incrementing the backoff counter.

Fix 1 (MCP): auto_evaluate_proposal now prefers pre-computed node_summary
  from the proposal payload over RPC-derived channel counts. Falls back to
  hive-getinfo for legacy proposals without node_summary.

3 new profitability gate tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
7 fixes across cl-hive (4) and cl_revenue_ops (3) to distinguish
between channels we opened vs. channels others opened to us.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…mmary()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
H2: Planner allows expansion to peers who opened channels to us.
    _has_existing_or_pending_channel() now returns opener as 4th tuple element.
    get_underserved_targets() only skips locally-opened peers.

H3: Quality scorer gives +0.05 bonus per remote channel open (cap +0.10).
    database.get_peer_event_summary() now includes remote_open_count.

H4: MCP channel deep-dive exposes opener field to AI advisor.

10 new tests across planner and quality scorer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Systematic audit covering correctness fixes (cooldown TOCTOU, pending
swap budget reservation, auto-cycle error counter), comprehensive test
coverage, hive integration (approval criteria, temporal awareness,
settlement visibility), and fleet coordination (gossip, pre-flight
check, dashboard).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
13-task plan across 4 phases: correctness fixes (cooldown TOCTOU,
pending swap budget reservation, auto-cycle error counter), comprehensive
test coverage, hive integration (approval criteria, temporal awareness,
yield reporting), and fleet coordination (gossip, pre-flight check,
dashboard MCP tool).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Structured APPROVE/REJECT/DEFER criteria for AI advisor evaluation
of Boltz swap proposals, consistent with the fallback-last design
principle (hive routes first, then market, then Boltz).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
F1 fix: Fleet members now share Boltz swap activity (pending swaps,
daily spend, last swap timestamp) via gossip protocol. This enables
fleet coordination to avoid duplicate Boltz swaps when free hive
rebalances are available.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
F3 fix: New MCP tool aggregates Boltz swap activity from gossip state
across all fleet members. Shows per-member pending swaps, daily spend,
and last swap timestamps plus fleet totals.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add boltz_activity field to HivePeerState dataclass
- Store boltz_activity from gossip data in update_peer_state()
- Add hive-fleet-boltz-status RPC that aggregates from gossip state
- Fix fleet_boltz_status MCP tool to use new RPC instead of hive-members

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The H3 fix added Boltz cost reporting from cl-revenue-ops, but the
receiving RPC handler didn't accept the new parameter, causing a
TypeError on the production plugin.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Period strings like "2026-10" were ambiguous — could mean ISO week 10
or October. Now uses "2026-W10" format for clarity. Database layer
handles both formats via _period_aliases() for backward compatibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two bugs caused every node to flag itself as "needs onboarding":

1. membership_dashboard didn't skip the local node in onboarding check
   (unlike handle_onboard_new_members which had the skip at line 8278)

2. opportunity_scanner and proactive_advisor extracted our_pubkey as
   node_info.get("id") but hive_node_info wraps getinfo in {"info": {...}},
   so our_pubkey was always "" and the self-skip never fired

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants