diff --git a/crates/edda-bridge-claude/src/dispatch/helpers.rs b/crates/edda-bridge-claude/src/dispatch/helpers.rs index 155799f..0820a10 100644 --- a/crates/edda-bridge-claude/src/dispatch/helpers.rs +++ b/crates/edda-bridge-claude/src/dispatch/helpers.rs @@ -1,7 +1,7 @@ use std::fs; use std::path::{Path, PathBuf}; -use super::read_workspace_config_bool; +use crate::render; // ── Active Plan ── @@ -139,7 +139,7 @@ pub(super) fn run_auto_digest( // Check if auto_digest is enabled (default: true) let enabled = match std::env::var("EDDA_BRIDGE_AUTO_DIGEST") { Ok(val) => val != "0", - Err(_) => read_workspace_config_bool(cwd, "bridge.auto_digest").unwrap_or(true), + Err(_) => render::config_bool(cwd, "bridge.auto_digest").unwrap_or(true), }; if !enabled { return None; @@ -152,7 +152,7 @@ pub(super) fn run_auto_digest( let digest_failed_cmds = match std::env::var("EDDA_BRIDGE_DIGEST_FAILED_CMDS") { Ok(val) => val != "0", - Err(_) => read_workspace_config_bool(cwd, "bridge.digest_failed_cmds").unwrap_or(true), + Err(_) => render::config_bool(cwd, "bridge.digest_failed_cmds").unwrap_or(true), }; match crate::digest::digest_previous_sessions_with_opts( diff --git a/crates/edda-bridge-claude/src/dispatch/mod.rs b/crates/edda-bridge-claude/src/dispatch/mod.rs index bdbc105..9205c85 100644 --- a/crates/edda-bridge-claude/src/dispatch/mod.rs +++ b/crates/edda-bridge-claude/src/dispatch/mod.rs @@ -215,7 +215,7 @@ pub fn hook_entrypoint_from_stdin(stdin: &str) -> anyhow::Result { // Also set compact_pending flag so the next UserPromptSubmit // re-ingests (keeping state fresh) instead of lightweight workspace-only. ingest_and_build_pack(&project_id, &session_id, &transcript_path, &cwd); - set_compact_pending(&project_id); + state::set_compact_pending(&project_id); Ok(HookResult::empty()) } "SessionEnd" => { @@ -316,53 +316,6 @@ pub fn hook_entrypoint_from_stdin(stdin: &str) -> anyhow::Result { _ => Ok(HookResult::empty()), } } -// ── State Management (delegates to state module) ── - -fn set_compact_pending(project_id: &str) { - state::set_compact_pending(project_id); -} - -fn take_compact_pending(project_id: &str) -> bool { - state::take_compact_pending(project_id) -} - -fn should_nudge(project_id: &str, session_id: &str) -> bool { - state::should_nudge(project_id, session_id) -} - -fn mark_nudge_sent(project_id: &str, session_id: &str) { - state::mark_nudge_sent(project_id, session_id); -} - -fn increment_counter(project_id: &str, session_id: &str, name: &str) { - state::increment_counter(project_id, session_id, name); -} - -fn read_counter(project_id: &str, session_id: &str, name: &str) -> u64 { - state::read_counter(project_id, session_id, name) -} - -fn is_same_as_last_inject(project_id: &str, session_id: &str, content: &str) -> bool { - state::is_same_as_last_inject(project_id, session_id, content) -} - -fn write_inject_hash(project_id: &str, session_id: &str, content: &str) { - state::write_inject_hash(project_id, session_id, content); -} - -fn read_peer_count(project_id: &str, session_id: &str) -> usize { - state::read_peer_count(project_id, session_id) -} - -fn write_peer_count(project_id: &str, session_id: &str, count: usize) { - state::write_peer_count(project_id, session_id, count); -} -// ── Config Helpers (delegates to render module) ── - -fn read_workspace_config_bool(cwd: &str, key: &str) -> Option { - render::config_bool(cwd, key) -} - pub(crate) fn read_hot_pack(project_id: &str) -> Option { let pack_path = edda_store::project_dir(project_id) .join("packs") diff --git a/crates/edda-bridge-claude/src/dispatch/session.rs b/crates/edda-bridge-claude/src/dispatch/session.rs index 7da8644..2a66f46 100644 --- a/crates/edda-bridge-claude/src/dispatch/session.rs +++ b/crates/edda-bridge-claude/src/dispatch/session.rs @@ -7,11 +7,11 @@ use super::helpers::{ extract_prior_session_last_message, inject_karvi_brief, read_project_state, render_active_plan, render_skill_guide_directive, run_auto_digest, }; +use crate::state; + use super::{ - apply_context_budget, context_budget, is_same_as_last_inject, read_counter, read_hot_pack, - read_peer_count, read_workspace_config_bool, render_workspace_section, - render_write_back_protocol, take_compact_pending, wrap_context_boundary, write_inject_hash, - write_peer_count, HookResult, + apply_context_budget, context_budget, read_hot_pack, render_workspace_section, + render_write_back_protocol, wrap_context_boundary, HookResult, }; pub(super) fn ingest_and_build_pack( @@ -145,9 +145,9 @@ pub(super) fn dispatch_with_workspace_only( // On 0→N transition, inject the full coordination protocol instead of // lightweight peer updates so the agent learns L2 commands and sees // peer scope claims. - let prev_count = read_peer_count(project_id, session_id); + let prev_count = state::read_peer_count(project_id, session_id); let first_peers = prev_count == 0 && !peers.is_empty(); - write_peer_count(project_id, session_id, peers.len()); + state::write_peer_count(project_id, session_id, peers.len()); if first_peers { // First time seeing peers — inject full coordination protocol @@ -182,11 +182,11 @@ pub(super) fn dispatch_with_workspace_only( if let Some(ws) = ws { let wrapped = wrap_context_boundary(&ws); // Dedup: skip if identical to last injection - if !session_id.is_empty() && is_same_as_last_inject(project_id, session_id, &wrapped) { + if !session_id.is_empty() && state::is_same_as_last_inject(project_id, session_id, &wrapped) { return Ok(HookResult::empty()); } if !session_id.is_empty() { - write_inject_hash(project_id, session_id, &wrapped); + state::write_inject_hash(project_id, session_id, &wrapped); } let output = serde_json::json!({ "hookSpecificOutput": { @@ -210,7 +210,7 @@ pub(super) fn dispatch_user_prompt_submit( transcript_path: &str, cwd: &str, ) -> anyhow::Result { - let post_compact = take_compact_pending(project_id); + let post_compact = state::take_compact_pending(project_id); if post_compact { // Re-ingest so state files are fresh for future hooks. @@ -271,9 +271,9 @@ pub(super) fn dispatch_session_end( let _ = run_auto_digest(project_id, session_id, cwd); // 2b. Read recall rate counters before cleanup - let nudge_count = read_counter(project_id, session_id, "nudge_count"); - let decide_count = read_counter(project_id, session_id, "decide_count"); - let signal_count = read_counter(project_id, session_id, "signal_count"); + let nudge_count = state::read_counter(project_id, session_id, "nudge_count"); + let decide_count = state::read_counter(project_id, session_id, "decide_count"); + let signal_count = state::read_counter(project_id, session_id, "signal_count"); // 2c. Snapshot session digest for next session's "## Previous Session" crate::digest::write_prev_digest_from_store( @@ -621,7 +621,7 @@ pub(super) fn dispatch_session_start( let pack = read_hot_pack(project_id); let guide_mode = match std::env::var("EDDA_SKILL_GUIDE") { Ok(val) => val == "1", - Err(_) => read_workspace_config_bool(cwd, "skill_guide").unwrap_or(false), + Err(_) => crate::render::config_bool(cwd, "skill_guide").unwrap_or(false), }; let mut content = if guide_mode { let directive = render_skill_guide_directive(); @@ -735,7 +735,7 @@ pub(super) fn dispatch_session_start( // Seed peer count so UserPromptSubmit knows the baseline and doesn't // re-inject the full protocol on the first prompt after SessionStart (#11). let peers = crate::peers::discover_active_peers(project_id, session_id); - write_peer_count(project_id, session_id, peers.len()); + state::write_peer_count(project_id, session_id, peers.len()); // Seed coordination offset so first UserPromptSubmit only diffs new events (#146). let coord_path = crate::peers::coordination_path(project_id); diff --git a/crates/edda-bridge-claude/src/dispatch/tests.rs b/crates/edda-bridge-claude/src/dispatch/tests.rs index f309e93..bf0a7ed 100644 --- a/crates/edda-bridge-claude/src/dispatch/tests.rs +++ b/crates/edda-bridge-claude/src/dispatch/tests.rs @@ -3,11 +3,14 @@ use std::fs; // Imports from dispatch/mod.rs use super::{ apply_context_budget, context_budget, has_active_peers, hook_entrypoint_from_stdin, - increment_counter, is_same_as_last_inject, mark_nudge_sent, read_counter, - render_workspace_section, render_write_back_protocol, set_compact_pending, - take_compact_pending, wrap_context_boundary, write_inject_hash, write_peer_count, HookResult, + render_workspace_section, render_write_back_protocol, wrap_context_boundary, HookResult, EDDA_BOUNDARY_END, EDDA_BOUNDARY_START, }; +use crate::state; +use crate::state::{ + increment_counter, is_same_as_last_inject, mark_nudge_sent, read_counter, + set_compact_pending, take_compact_pending, write_inject_hash, write_peer_count, +}; // Imports from sub-modules use super::events::{ extract_task_id, is_karvi_project, try_write_commit_event, try_write_merge_event, diff --git a/crates/edda-bridge-claude/src/dispatch/tools.rs b/crates/edda-bridge-claude/src/dispatch/tools.rs index e1a1155..69ce58d 100644 --- a/crates/edda-bridge-claude/src/dispatch/tools.rs +++ b/crates/edda-bridge-claude/src/dispatch/tools.rs @@ -9,10 +9,9 @@ use crate::parse::*; use super::events::{ is_karvi_project, try_post_karvi_signal, try_write_commit_event, try_write_merge_event, }; -use super::{ - increment_counter, mark_nudge_sent, read_counter, read_peer_count, read_workspace_config_bool, - should_nudge, wrap_context_boundary, HookResult, -}; +use crate::state; + +use super::{wrap_context_boundary, HookResult}; pub(super) fn dispatch_pre_tool_use( raw: &serde_json::Value, @@ -60,7 +59,7 @@ pub(super) fn dispatch_pre_tool_use( // ── Off-limits enforcement: block Edit/Write on peer-claimed files ── let enforce_offlimits = match std::env::var("EDDA_ENFORCE_OFFLIMITS") { Ok(val) => val == "1", - Err(_) => read_workspace_config_bool(cwd, "bridge.enforce_offlimits").unwrap_or(false), + Err(_) => crate::render::config_bool(cwd, "bridge.enforce_offlimits").unwrap_or(false), }; if enforce_offlimits { let tool_name_ol = get_str(raw, "tool_name"); @@ -175,12 +174,12 @@ pub(super) fn dispatch_pre_tool_use( /// Skips all I/O for solo sessions (no peers). pub(super) fn check_pending_requests(project_id: &str, session_id: &str) -> Option { // Solo gate: skip counter I/O entirely when no peers are active. - if read_peer_count(project_id, session_id) == 0 { + if state::read_peer_count(project_id, session_id) == 0 { return None; } - let counter = read_counter(project_id, session_id, "request_nudge_count"); - increment_counter(project_id, session_id, "request_nudge_count"); + let counter = state::read_counter(project_id, session_id, "request_nudge_count"); + state::increment_counter(project_id, session_id, "request_nudge_count"); if !counter.is_multiple_of(3) { return None; } @@ -207,7 +206,7 @@ pub(super) fn check_offlimits( file_path: &str, ) -> Option<(String, String)> { // Solo gate: skip when no peers are active. - if read_peer_count(project_id, session_id) == 0 { + if state::read_peer_count(project_id, session_id) == 0 { return None; } @@ -339,7 +338,7 @@ pub(super) fn dispatch_post_tool_use( }; // Count every detected signal (including SelfRecord and cooldown-suppressed ones). - increment_counter(project_id, session_id, "signal_count"); + state::increment_counter(project_id, session_id, "signal_count"); // Auto-write events to workspace ledger (best-effort, try-lock). match &signal { @@ -358,19 +357,19 @@ pub(super) fn dispatch_post_tool_use( // Agent is recording a decision → increment counter, but do NOT suppress future nudges. // This allows the agent to receive nudges for subsequent signals after cooldown. if signal == crate::nudge::NudgeSignal::SelfRecord { - increment_counter(project_id, session_id, "decide_count"); + state::increment_counter(project_id, session_id, "decide_count"); return Ok(HookResult::empty()); } // Check cooldown - if !should_nudge(project_id, session_id) { + if !state::should_nudge(project_id, session_id) { return Ok(HookResult::empty()); } - let decide_count = read_counter(project_id, session_id, "decide_count"); + let decide_count = state::read_counter(project_id, session_id, "decide_count"); let nudge_text = crate::nudge::format_nudge(&signal, decide_count); - mark_nudge_sent(project_id, session_id); - increment_counter(project_id, session_id, "nudge_count"); + state::mark_nudge_sent(project_id, session_id); + state::increment_counter(project_id, session_id, "nudge_count"); let wrapped = wrap_context_boundary(&nudge_text); let output = serde_json::json!({ @@ -511,7 +510,7 @@ pub(super) fn match_tool_patterns(raw: &serde_json::Value, cwd: &str) -> Option< // Check if patterns feature is enabled let enabled = match std::env::var("EDDA_PATTERNS_ENABLED") { Ok(val) => val == "1", - Err(_) => read_workspace_config_bool(cwd, "patterns_enabled").unwrap_or(false), + Err(_) => crate::render::config_bool(cwd, "patterns_enabled").unwrap_or(false), }; if !enabled { return None;