Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions crates/edda-bridge-claude/src/dispatch/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::fs;
use std::path::{Path, PathBuf};

use super::read_workspace_config_bool;
use crate::render;

// ── Active Plan ──

Expand Down Expand Up @@ -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;
Expand All @@ -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(
Expand Down
49 changes: 1 addition & 48 deletions crates/edda-bridge-claude/src/dispatch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ pub fn hook_entrypoint_from_stdin(stdin: &str) -> anyhow::Result<HookResult> {
// 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" => {
Expand Down Expand Up @@ -316,53 +316,6 @@ pub fn hook_entrypoint_from_stdin(stdin: &str) -> anyhow::Result<HookResult> {
_ => 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<bool> {
render::config_bool(cwd, key)
}

pub(crate) fn read_hot_pack(project_id: &str) -> Option<String> {
let pack_path = edda_store::project_dir(project_id)
.join("packs")
Expand Down
28 changes: 14 additions & 14 deletions crates/edda-bridge-claude/src/dispatch/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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": {
Expand All @@ -210,7 +210,7 @@ pub(super) fn dispatch_user_prompt_submit(
transcript_path: &str,
cwd: &str,
) -> anyhow::Result<HookResult> {
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.
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
Expand Down
9 changes: 6 additions & 3 deletions crates/edda-bridge-claude/src/dispatch/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
31 changes: 15 additions & 16 deletions crates/edda-bridge-claude/src/dispatch/tools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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<String> {
// 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;
}
Expand All @@ -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;
}

Expand Down Expand Up @@ -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 {
Expand All @@ -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!({
Expand Down Expand Up @@ -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;
Expand Down
Loading