Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7feb5f6
docs: add implementation plan for main-green-sixpack
danshapiro Sep 19, 2026
5337be3
docs(plan): stage-2 load-bearing corrections — 59nb census is 26 site…
danshapiro Sep 19, 2026
3e5443f
docs(plan): round-1 review remediation — bin-target cargo selectors, …
danshapiro Sep 19, 2026
0a7f11b
docs(plan): round-2 review remediation — selector pin corrected to cl…
danshapiro Sep 19, 2026
fe8d6ac
docs(plan): round-3 review remediation — Task 5 premise corrected for…
danshapiro Sep 19, 2026
724fad2
fix(titles): explicit creator tab names outrank mirrored/auto pane ti…
danshapiro Sep 19, 2026
f42b475
test(e2e): pin creator-title precedence in remote-tab-linkage; harden…
danshapiro Sep 19, 2026
20c49f2
plan: D3 amendment — sidebar-opencode-rail reshape (registry auto-tit…
danshapiro Sep 19, 2026
4930a39
test(e2e): reshape sidebar-opencode-rail banner to the pane's registr…
danshapiro Sep 19, 2026
01913fc
test(ws): process-global OnceLock capture with per-test-unique filter…
danshapiro Sep 19, 2026
7fc3249
test(freshagent): delete the merge-repair-resurrected records_no_bind…
danshapiro Sep 19, 2026
cd46b40
docs(plan): tick Task 4 (kata hsrh part 1) steps complete
danshapiro Sep 19, 2026
22a9682
test(freshagent): either-order created+session.init drain plus bindin…
danshapiro Sep 19, 2026
c9f5e60
docs(plan): tick Task 5 (kata hsrh part 2) steps complete
danshapiro Sep 19, 2026
effb314
revert(server): restore fabricated live-terminal placeholder titles (…
danshapiro Sep 19, 2026
b7f0717
fix(sidebar): title-less running rows compose the fallback row's name…
danshapiro Sep 19, 2026
ad66d80
test(e2e): cite the fallback-row cwd source in the rail leaf comment …
danshapiro Sep 19, 2026
6fb00c5
docs(plan): round-1 delta dispositions — honest T4/T5 annotations, D4…
danshapiro Sep 19, 2026
2c8c350
docs(plan): Task 6 gate record — GATE-DONE under the D4-amended crite…
danshapiro Sep 19, 2026
3ba73a1
Merge remote-tracking branch 'origin/main' into main-green-sixpack
danshapiro Sep 19, 2026
187ea0e
ci(rust-tests): install npm deps so Node test fixtures boot on the ru…
danshapiro Sep 19, 2026
5bb9f58
docs(plan): repair D6 note text (backtick-substitution artifact)
danshapiro Sep 19, 2026
6eb05af
docs(plan): D7 amendment — fix the #795-landed freshagent red family …
danshapiro Sep 19, 2026
65f2994
fix(fresh-agent): restore the send-time session.metadata broadcast #7…
danshapiro Sep 19, 2026
fec4fc6
test(fresh-agent): reshape stale-family fixtures to the post-r30 sema…
danshapiro Sep 19, 2026
d1d9926
test(fresh-agent): route the old-rebound codex handoff target through…
danshapiro Sep 19, 2026
2027393
docs(plan): Task 6 addendum — D7 checklist ticked (freshagent red fam…
danshapiro Sep 19, 2026
ed649d1
test(fresh-agent): make the two restore-shaped stale-fence tests exer…
danshapiro Sep 19, 2026
5f3a8e0
test(fresh-agent): free the 7 codex crash-recovery fixtures from the …
Sep 19, 2026
ae4230d
test(fresh-agent): make the D8 attach-recovery test prove recovery + …
Sep 19, 2026
8e12ca3
test(fresh-agent): apply the round-4 residuals to the D8 codex fixtur…
Sep 19, 2026
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
9 changes: 9 additions & 0 deletions .github/workflows/rust-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ jobs:
with:
toolchain: 1.96.0
- uses: Swatinem/rust-cache@v2
# The Rust suite spawns Node test fixtures (the codex fake app-server
# imports `ws`), so this job needs npm dependencies installed or those
# tests die at fixture boot ("fake app-server exited before listening").
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- name: Install npm dependencies (Rust test fixtures)
run: npm ci
- name: Workspace tests
run: cargo test --workspace --exclude freshell-tauri --locked

Expand Down
364 changes: 233 additions & 131 deletions crates/freshell-freshagent/src/claude.rs

Large diffs are not rendered by default.

527 changes: 399 additions & 128 deletions crates/freshell-freshagent/src/codex.rs

Large diffs are not rendered by default.

26 changes: 15 additions & 11 deletions crates/freshell-freshagent/src/opencode_ws.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11367,8 +11367,10 @@ mod tests {

// ── b8ke ext r15 F1: the map-hit/Vacant compatibility claims are FENCED ──

/// The F1 fixture: a live in-map session row over a coordinator key the
/// test controls (wired ownership + the session row).
/// The F1 fixture: a live in-map MATERIALIZED session row (the
/// post-first-send shape — `real_session_id: Some`, per gate-C) over a
/// coordinator key the test controls (wired ownership + the session
/// row).
async fn fenced_state_with_map_row(
durable_id: &str,
) -> (
Expand All @@ -11384,15 +11386,17 @@ mod tests {
let registry = Arc::new(freshell_ownership::RuntimeOwnershipRegistry::new());
let mut st = FreshOpencodeState::new(fresh_agent);
st.set_ownership(Arc::clone(&registry));
st.sessions.lock().await.insert(
durable_id.to_string(),
Arc::new(TokioMutex::new(OpencodeSession::new(
durable_id.to_string(),
None,
None,
None,
))),
);
// Gate-C (7824b5aab): an attach finding an UNMATERIALIZED row
// (`real_session_id: None`, a fresh pane's placeholder) is
// observation-only — snapshot + return — and never reaches the r15
// F1 fenced map-hit adopt claim these tests exist to cover. Seed the
// POST-materialization shape instead: a real `ses_*` id.
let mut session = OpencodeSession::new(durable_id.to_string(), None, None, None);
session.real_session_id = Some(durable_id.to_string());
st.sessions
.lock()
.await
.insert(durable_id.to_string(), Arc::new(TokioMutex::new(session)));
(st, rx, registry)
}

Expand Down
13 changes: 13 additions & 0 deletions crates/freshell-freshagent/src/session_handoff/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4443,6 +4443,19 @@ async fn an_abort_with_a_published_target_pid_outliving_the_reap_budget_fences()
#[tokio::test(flavor = "multi_thread")]
async fn a_codex_handoff_on_an_old_rebound_reference_resolves_the_permanent_alias() {
let _guard = ENV_LOCK.lock().await;
// Hermeticity: the handoff's codex TARGET spawn (resume_for_handoff →
// spawn_sidecar) launches whatever `CODEX_CMD` names — without an
// override the test depends on a host-installed `codex` binary and
// fails on the CI runner with TARGET_SPAWN_FAILED (ENOENT). Route the
// spawn through the committed fake app-server (the same CODEX_CMD
// wrapper codex.rs's own fixtures use; its `thread/resume` echoes
// whatever id it is asked to resume, so the canonical-key target
// resumes cleanly). The codex env guard is held for the same reason
// as the mismatch-refusal sibling's: CODEX_CMD is codex.rs's
// process-global surface, and the guard snapshots/restores every
// mutated variable on drop.
let _codex_env = crate::codex::tests::ENV_LOCK.lock().await;
crate::codex::tests::configure_fake_codex_cmd("{}");
let old_tid = format!("old-thread-{}", uuid::Uuid::new_v4());
let new_tid = format!("new-thread-{}", uuid::Uuid::new_v4());
let rig = build_rig(None);
Expand Down
32 changes: 22 additions & 10 deletions crates/freshell-ws/src/claude_signal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,11 +433,11 @@ mod tests {
// `JustOne` rebuilder computes interest from the REGISTERING
// thread's default subscriber -- none here would cache
// `Interest::never` globally and silently swallow the warn that
// drain_warns_on_rejected_files asserts on. Holding a capture
// guard (the opencode lane's idiom: every test that can hit a
// capture-asserted callsite holds one) makes registration always
// see a live subscriber.
let (_events, _guard) = crate::invariants::capture::capture();
// drain_warns_on_rejected_files asserts on. Installing the
// process-global capture (kata 59nb: one global subscriber; every
// thread's events land in the shared vec) makes registration always
// see a live subscriber, whichever thread registers first.
let _ = crate::invariants::capture::capture();
let dir = tempfile::tempdir().unwrap();
write_file(
dir.path(),
Expand Down Expand Up @@ -595,7 +595,7 @@ mod tests {

#[test]
fn drain_warns_on_rejected_files() {
let (events, _guard) = crate::invariants::capture::capture();
let events = crate::invariants::capture::capture();
let dir = tempfile::tempdir().unwrap();
write_file(dir.path(), "junk__1.json", "not json");
let watcher = ClaudeSignalWatcher::new(dir.path().to_path_buf());
Expand All @@ -605,12 +605,24 @@ mod tests {
!dir.path().join("junk__1.json").exists(),
"malformed files stay single-shot (consumed)"
);
let events = events.lock().unwrap();
assert!(
// Collect-then-assert (never hold the shared vec's guard across an
// assert); the path filter scopes the presence check to THIS test's
// junk file — the vec carries every test's events (kata 59nb).
let want_path = format!("{}", dir.path().join("junk__1.json").display());
let hits: Vec<crate::invariants::capture::CapturedEvent> = {
let events = events.lock().unwrap_or_else(|p| p.into_inner());
events
.iter()
.any(|e| e.message.contains("claude_signal_rejected")),
"parse rejects must be warn-logged for detectability (A8)"
.filter(|e| {
e.message.contains("claude_signal_rejected")
&& e.fields.get("path").map(String::as_str) == Some(want_path.as_str())
})
.cloned()
.collect()
};
assert!(
!hits.is_empty(),
"parse rejects must be warn-logged for detectability (A8); got: {hits:?}"
);
}
}
85 changes: 32 additions & 53 deletions crates/freshell-ws/src/create_dedupe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -904,49 +904,15 @@ mod tests {
/// contract on the waiter path.
#[test]
fn settle_logs_a_waiter_join_event_with_the_waiters_connection_id() {
use std::collections::BTreeMap;
use tracing::field::{Field, Visit};
use tracing::{Event, Subscriber};
use tracing_subscriber::layer::{Context, SubscriberExt};
use tracing_subscriber::Layer;

#[derive(Default)]
struct V {
message: String,
fields: BTreeMap<String, String>,
}
impl Visit for V {
fn record_debug(&mut self, f: &Field, v: &dyn std::fmt::Debug) {
if f.name() == "message" {
self.message = format!("{v:?}");
} else {
self.fields.insert(f.name().to_string(), format!("{v:?}"));
}
}
fn record_str(&mut self, f: &Field, v: &str) {
if f.name() == "message" {
self.message = v.to_string();
} else {
self.fields.insert(f.name().to_string(), v.to_string());
}
}
fn record_u64(&mut self, f: &Field, v: u64) {
self.fields.insert(f.name().to_string(), v.to_string());
}
}
type CapturedEvent = (String, BTreeMap<String, String>);
struct L(Arc<Mutex<Vec<CapturedEvent>>>);
impl<S: Subscriber> Layer<S> for L {
fn on_event(&self, e: &Event<'_>, _ctx: Context<'_, S>) {
let mut v = V::default();
e.record(&mut v);
self.0.lock().unwrap().push((v.message, v.fields));
}
}

let events = Arc::new(Mutex::new(Vec::new()));
let subscriber = tracing_subscriber::registry().with(L(Arc::clone(&events)));
let _guard = tracing::subscriber::set_default(subscriber);
// Kata 59nb: this test previously ran its own inline thread-local
// `set_default` capture; it now consumes the binary-wide process-global
// capture (invariants.rs OnceLock) and filters by this test's
// grep-unique terminal_id + path fields. The connection_id assertion
// transfers unchanged: the production emission records it as u64, and
// tracing's DEFAULT `Visit::record_u64` delegates to `record_debug`,
// so the global capture's Debug fallback renders `2u64` as "2" —
// identical to the old inline visitor's Display render.
let events = crate::invariants::capture::capture();

let d = CreateDedupe::default();
let (origin, _origin_frames) = recording_sink();
Expand All @@ -966,21 +932,34 @@ mod tests {
1,
"waiter control: the reply frame itself must still be forwarded"
);
let captured = events.lock().expect("capture lock").clone();
let join = captured
.iter()
.find(|(msg, fields)| {
msg == "ws.terminal.create.settled"
&& fields.get("terminal_id").map(String::as_str) == Some("tX")
&& fields.get("path").map(String::as_str) == Some("duplicate_in_flight_waiter")
})
// Collect-then-assert (never hold the shared vec's guard across an
// assert); terminal_id "tX" + path "duplicate_in_flight_waiter" are
// grep-unique to this one test across the whole lib.
let hits: Vec<crate::invariants::capture::CapturedEvent> = {
let events = events.lock().unwrap_or_else(|p| p.into_inner());
events
.iter()
.filter(|e| {
e.message == "ws.terminal.create.settled"
&& e.fields.get("terminal_id").map(String::as_str) == Some("tX")
&& e.fields.get("path").map(String::as_str)
== Some("duplicate_in_flight_waiter")
})
.cloned()
.collect()
};
let join = hits
.first()
.expect("settle must log a ws.terminal.create.settled join for the waiter");
assert_eq!(
join.1.get("connection_id").map(String::as_str),
join.fields.get("connection_id").map(String::as_str),
Some("2"),
"the join event must name the WAITER's connection id, not the origin's"
);
assert_eq!(join.1.get("request_id").map(String::as_str), Some("rX"));
assert_eq!(
join.fields.get("request_id").map(String::as_str),
Some("rX")
);
}

fn terminal_created_frame(request_id: &str, terminal_id: &str) -> ServerMessage {
Expand Down
Loading
Loading