Skip to content

Commit 24083fc

Browse files
Your Nameclaude
andcommitted
fix(test): repair CI regressions from state.db rewiring + daemon preset flake
edit_lines_aborts_when_txn_begin_fails and format_files_skips_one_file_when_txn_begin_fails_without_aborting_the_batch chmod'd index.db read-only to force a txn::begin failure, but the 2026-08-05 state.db rewiring (86f56ab) moved edit_transactions/tx_events into a separate .calm/state.db. txn::begin's BEGIN IMMEDIATE now runs against state.db, so the old fixture no longer triggers TRANSACTION_INIT_FAILED -- the write proceeds and only the later reindex against read-only index.db fails, surfacing as a soft index_stale warning instead. Point the read-only chmod at state.db, which is what txn::begin actually writes to now. daemon_respects_per_connection_preset failed intermittently in CI (2 of the last 4 runs) but never locally (4/4 clean runs). It's the only test in the file where connection B attaches to a daemon A is concurrently still spawning, leaving less timing headroom than other wait_for call sites here. Widen its wait_for from 8s to 20s, same class of fix as the already-merged watch_supervisor wait_until widening (b4c43d7). Verified locally: calm-server --lib 341/341, calm-cli daemon_integration 10/10 x3, fmt/clippy clean on both crates. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 86f56ab commit 24083fc

2 files changed

Lines changed: 28 additions & 14 deletions

File tree

crates/calm-cli/tests/daemon_integration.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,14 @@ fn daemon_respects_per_connection_preset() {
408408
let watcher_b = StdoutWatcher::spawn(stdout_b);
409409
stdin_b.write_all(initialize).unwrap();
410410
stdin_b.write_all(list_tools).unwrap();
411-
watcher_b.wait_for("\"id\":2", Duration::from_secs(8));
411+
// 20s, not the usual 8s (see other `wait_for` call sites in this file): this
412+
// is the one test where B must attach to a daemon A is *concurrently still
413+
// spawning/initializing* rather than one already confirmed live, so the full
414+
// round trip (connect_live_and_current's own up-to-5s budget, plus B's real
415+
// tools/list) has less headroom than elsewhere -- flaky in CI under load
416+
// (missing content in stdout_b_text) but never locally, same class of fix as
417+
// the watch_supervisor wait_until widening in b4c43d7.
418+
watcher_b.wait_for("\"id\":2", Duration::from_secs(20));
412419
drop(stdin_b);
413420
let mut stderr_b = Vec::new();
414421
if let Some(mut stderr) = child_b.stderr.take() {

crates/calm-server/src/tools.rs

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9908,18 +9908,22 @@ mod tests {
99089908
// WS-1 enforce transition (docs/plans/2026-08-02-ws1-enforce-and-critical-
99099909
// risk-execution-plan.md §2): a txn::begin failure must abort the write
99109910
// entirely rather than proceed with no journal. Forced deterministically by
9911-
// making the DB file read-only at the OS level after setup -- open_writer
9912-
// requires write access, so the very first `open_writer` inside
9913-
// `begin_result`'s construction fails, exercising the same enforce path a
9914-
// real disk-full/permission problem would.
9911+
// making state.db read-only at the OS level after setup -- since the
9912+
// 2026-08-05 state.db rewiring (docs/plans/2026-08-05-state-db-rewiring-
9913+
// execution-plan.md), `edit_transactions`/`tx_events` live in state.db, not
9914+
// index.db, so it's `txn::begin`'s own `BEGIN IMMEDIATE` against state.db
9915+
// that must fail here, exercising the same enforce path a real
9916+
// disk-full/permission problem would. (index.db itself is untouched -- a
9917+
// read-only *index.db* only surfaces later, as a non-fatal `index_stale`
9918+
// warning on an otherwise-applied write, not a TRANSACTION_INIT_FAILED.)
99159919
let (dir, server) = test_server("txn_begin_failure_aborts_write");
99169920
std::fs::write(dir.join("a.py"), "def helper():\n return 1\n").unwrap();
99179921
let hash = calm_core::edit::range_checksum("def helper():\n return 1\n", 2, 2).unwrap();
99189922

9919-
let db_path = dir.join("index.db");
9920-
let mut perms = std::fs::metadata(&db_path).unwrap().permissions();
9923+
let state_db_path = dir.join(".calm").join("state.db");
9924+
let mut perms = std::fs::metadata(&state_db_path).unwrap().permissions();
99219925
perms.set_readonly(true);
9922-
std::fs::set_permissions(&db_path, perms).unwrap();
9926+
std::fs::set_permissions(&state_db_path, perms).unwrap();
99239927

99249928
let out = jv(
99259929
server.edit_lines(rmcp::handler::server::wrapper::Parameters(
@@ -9952,7 +9956,7 @@ mod tests {
99529956
{
99539957
use std::os::unix::fs::PermissionsExt;
99549958
let perms = std::fs::Permissions::from_mode(0o644);
9955-
std::fs::set_permissions(&db_path, perms).unwrap();
9959+
std::fs::set_permissions(&state_db_path, perms).unwrap();
99569960
}
99579961
let _ = std::fs::remove_dir_all(&dir);
99589962
}
@@ -9961,18 +9965,21 @@ mod tests {
99619965
fn format_files_skips_one_file_when_txn_begin_fails_without_aborting_the_batch() {
99629966
// Same enforce posture as edit_lines, but format_files processes a batch --
99639967
// a per-file begin failure must surface as that file's own "error" result,
9964-
// not a batch-wide abort of files that would otherwise succeed.
9968+
// not a batch-wide abort of files that would otherwise succeed. See
9969+
// edit_lines_aborts_when_txn_begin_fails's comment above for why state.db
9970+
// (not index.db) is the file that must be read-only to force this path
9971+
// since the 2026-08-05 state.db rewiring.
99659972
let (dir, server) = test_server("format_files_txn_begin_failure_per_file");
99669973
std::fs::write(
99679974
dir.join("ugly.rs"),
99689975
"fn main( ) { let x=1 ;println!(\"{}\",x);}\n",
99699976
)
99709977
.unwrap();
99719978

9972-
let db_path = dir.join("index.db");
9973-
let mut perms = std::fs::metadata(&db_path).unwrap().permissions();
9979+
let state_db_path = dir.join(".calm").join("state.db");
9980+
let mut perms = std::fs::metadata(&state_db_path).unwrap().permissions();
99749981
perms.set_readonly(true);
9975-
std::fs::set_permissions(&db_path, perms).unwrap();
9982+
std::fs::set_permissions(&state_db_path, perms).unwrap();
99769983

99779984
let out = jv(
99789985
server.format_files(rmcp::handler::server::wrapper::Parameters(
@@ -9999,7 +10006,7 @@ mod tests {
999910006
{
1000010007
use std::os::unix::fs::PermissionsExt;
1000110008
let perms = std::fs::Permissions::from_mode(0o644);
10002-
std::fs::set_permissions(&db_path, perms).unwrap();
10009+
std::fs::set_permissions(&state_db_path, perms).unwrap();
1000310010
}
1000410011
let _ = std::fs::remove_dir_all(&dir);
1000510012
}

0 commit comments

Comments
 (0)