docs+test: gate fixes and handoff snapshot (compile-mode in flight)
cargo fmt over the new files; doc-markdown backticks; is_ok_and in
the recompile counter wait; m4_6's M-g n/p pin updated to the Q#CM5
takeover contract (error.next/error.previous with the diag commands
as the dispatchers' fallback — the test's no-attachment status
behavior is unchanged). Handoff §1: main @ 0efb5cd, compile-mode
branch in flight at framing revision 6, themes named as the
standing runner-up.
Gate results on this machine (laptop, basedpyright live): fmt,
clippy --workspace --all-targets, lib 1522, crdt lib 1696,
compile_mode_acceptance 34, compile_mode_crdt_acceptance 1,
m4_acceptance 101 (no skip), PMACS_REQUIRE_GPU gpu 59, workspace
sweep 2482/0, git diff --check.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VoiEyuPjoBhvwACf8HAnLB
This commit is contained in:
parent
53854ed803
commit
a2b12dc9d6
|
|
@ -1,7 +1,7 @@
|
|||
# Agent handoff — cross-machine continuity
|
||||
|
||||
**Last updated: 2026-07-12, on the laptop, by the editops session
|
||||
(post-#111 merge; scripts/bite micro-PR).** This file is the
|
||||
**Last updated: 2026-07-13, on the laptop, by the compile-mode
|
||||
session (on-branch snapshot).** This file is the
|
||||
bridge between development machines. If you are an agent reading
|
||||
this on a fresh clone: this document plus the `docs/*-framing.md`
|
||||
files ARE your memory. Read this fully before taking on work, seed
|
||||
|
|
@ -9,10 +9,26 @@ your persistent memory from it, and **update this file (and commit
|
|||
it) whenever project state changes materially** — the next machine
|
||||
reads it the way you just did.
|
||||
|
||||
## 1. Where the project stands (2026-07-12)
|
||||
## 1. Where the project stands (2026-07-13)
|
||||
|
||||
- `main` @ `f0a05c5` (editops #111 merged), protocol **v15**
|
||||
- `main` @ `0efb5cd` (scripts/bite #112 merged), protocol **v15**
|
||||
(`SUPPORTED=[6..15]`).
|
||||
- **IN FLIGHT: compile-mode (Arc 5 stage 1) on branch
|
||||
`compile-mode`** — the user chose it over themes (Arc 4) in the
|
||||
decision discussion. Framing `docs/compile-mode-framing.md` at
|
||||
revision 6 (five pre-branch review rounds; approved for build).
|
||||
Shape: `compile.run` streams `/bin/sh -c "exec 2>&1; <cmd>"`
|
||||
(pipes, `stdin="null"`, `group=true`, TERM=dumb) into an
|
||||
intercept-read-only `*compilation*` buffer via a Lua-side ANSI
|
||||
parser; once-per-newline error rules; unified `error.next`/
|
||||
`error.previous` dispatcher (M-g n/p taken over from diag with a
|
||||
behavior-preserving fallback; `` C-x ` ``; M-! shell-command);
|
||||
buffer-revision external-edit guard with desync marker + anchor
|
||||
epochs; grep-mode upgrade of `project.search`. New substrate other
|
||||
code can use: `ProcessSpec.stdin/group` (group lifecycle: reap
|
||||
ledger, in-drain enforcement, cancellable poll readers),
|
||||
`buf:revision()`, jump_back now fires `buffer.after-switch`,
|
||||
`pmacs.errors.claim`. All gates green; PR next.
|
||||
- **Editing-conveniences pack (editops, #111) landed** — the Lua
|
||||
parallel lane. Framing `docs/editing-conveniences-framing.md` at
|
||||
revision 6 (three pre-branch rounds, one adopted post-approval
|
||||
|
|
@ -41,9 +57,11 @@ reads it the way you just did.
|
|||
`buf:path()`, `pmacs.lsp.buffer_language(buf)`,
|
||||
`PMACS_FAKE_LSP_CHANGE_SINK` (fake-LSP doc-sync replay),
|
||||
`TestDaemon::spawn_with_config` (init.lua-carrying daemon fixture).
|
||||
- **NEXT: the user wants a decision discussion — compile-mode (Arc 5
|
||||
stage 1) vs themes (Arc 4). Do not pick unilaterally; frame the
|
||||
tradeoff and ask.**
|
||||
- **NEXT after compile-mode merges: themes (Arc 4) is the standing
|
||||
runner-up from the decision discussion** — scout fresh before
|
||||
framing (protocol bump v15→16 for a ThemeFacts channel, the
|
||||
LineNumbers/Q#UX1 control-plane template, glyphon font reload is
|
||||
the hard part).
|
||||
- Auto-indent (#109) landed earlier: RET binds
|
||||
`edit.newline-and-indent`; plain Enter round-trips on both
|
||||
frontends; shared search invalidation (Q#AI8), empty-selection
|
||||
|
|
|
|||
|
|
@ -12662,7 +12662,10 @@ mod tests {
|
|||
)
|
||||
.eval()
|
||||
.unwrap();
|
||||
assert!(ok, "revision must be strictly monotonic across edit/undo/redo");
|
||||
assert!(
|
||||
ok,
|
||||
"revision must be strictly monotonic across edit/undo/redo"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -1447,7 +1447,12 @@ fn build_pipes_runtime(spec: &ProcessSpec, _id: ProcessId) -> Result<RuntimeHand
|
|||
Arc::clone(&active_readers),
|
||||
)
|
||||
} else {
|
||||
spawn_reader(byte_tx.clone(), Arc::clone(&cancel), out, ReaderKind::Stdout)
|
||||
spawn_reader(
|
||||
byte_tx.clone(),
|
||||
Arc::clone(&cancel),
|
||||
out,
|
||||
ReaderKind::Stdout,
|
||||
)
|
||||
});
|
||||
}
|
||||
if let Some(err) = stderr {
|
||||
|
|
@ -1871,10 +1876,7 @@ struct GroupDrainCtx {
|
|||
deadline: Instant,
|
||||
}
|
||||
|
||||
fn final_drain_runtime(
|
||||
rt: &RuntimeHandles,
|
||||
group: Option<GroupDrainCtx>,
|
||||
) -> Vec<ProcessEventKind> {
|
||||
fn final_drain_runtime(rt: &RuntimeHandles, group: Option<GroupDrainCtx>) -> Vec<ProcessEventKind> {
|
||||
let deadline = Instant::now() + EXIT_OUTPUT_DRAIN_TIMEOUT;
|
||||
let mut out = Vec::new();
|
||||
// Group drains get tighter bounds than the plain byte-flush
|
||||
|
|
@ -2813,7 +2815,10 @@ mod tests {
|
|||
// Readiness echo: terminating before the trap is installed
|
||||
// would let plain SIGTERM win and vacuously pass.
|
||||
let id = sup
|
||||
.spawn(sh_group_spec("trap-test", "trap '' TERM; echo ready; sleep 30"))
|
||||
.spawn(sh_group_spec(
|
||||
"trap-test",
|
||||
"trap '' TERM; echo ready; sleep 30",
|
||||
))
|
||||
.expect("spawn");
|
||||
let _ = drain_until(&mut sup, id, Duration::from_secs(2), |evs| {
|
||||
stdout_contains(evs, b"ready")
|
||||
|
|
@ -2849,7 +2854,9 @@ mod tests {
|
|||
"( trap '' TERM; exec >/dev/null 2>&1; sleep 30 ) & echo $! > {}",
|
||||
pidfile.display()
|
||||
);
|
||||
let id = sup.spawn(sh_group_spec("survivor", &script)).expect("spawn");
|
||||
let id = sup
|
||||
.spawn(sh_group_spec("survivor", &script))
|
||||
.expect("spawn");
|
||||
let events = drain_until(&mut sup, id, Duration::from_secs(5), has_exited);
|
||||
assert!(
|
||||
events
|
||||
|
|
@ -2882,7 +2889,10 @@ mod tests {
|
|||
let mut sup = ProcessSupervisor::new();
|
||||
sup.set_group_term_grace(Duration::from_millis(500));
|
||||
let id = sup
|
||||
.spawn(sh_group_spec("re-term", "trap '' TERM; echo ready; sleep 30"))
|
||||
.spawn(sh_group_spec(
|
||||
"re-term",
|
||||
"trap '' TERM; echo ready; sleep 30",
|
||||
))
|
||||
.expect("spawn");
|
||||
let _ = drain_until(&mut sup, id, Duration::from_secs(2), |evs| {
|
||||
stdout_contains(evs, b"ready")
|
||||
|
|
@ -2923,7 +2933,9 @@ mod tests {
|
|||
"( trap '' TERM; exec >/dev/null 2>&1; sleep 30 ) & echo $! > {}",
|
||||
pidfile.display()
|
||||
);
|
||||
let id = sup.spawn(sh_group_spec("survivor", &script)).expect("spawn");
|
||||
let id = sup
|
||||
.spawn(sh_group_spec("survivor", &script))
|
||||
.expect("spawn");
|
||||
let _ = drain_until(&mut sup, id, Duration::from_secs(5), has_exited);
|
||||
let survivor = wait_pidfile(&pidfile);
|
||||
assert!(pid_alive(survivor), "survivor alive pre-shutdown");
|
||||
|
|
@ -3075,13 +3087,23 @@ mod tests {
|
|||
let mut spec = ProcessSpec::new("pty-null", "/bin/sh");
|
||||
spec.mode = ProcessMode::default_pty();
|
||||
spec.stdin = StdinMode::Null;
|
||||
let err = sup.spawn(spec).expect_err("stdin=null must be rejected under pty");
|
||||
assert!(err.contains("pipe mode"), "error points at pipe mode: {err}");
|
||||
let err = sup
|
||||
.spawn(spec)
|
||||
.expect_err("stdin=null must be rejected under pty");
|
||||
assert!(
|
||||
err.contains("pipe mode"),
|
||||
"error points at pipe mode: {err}"
|
||||
);
|
||||
|
||||
let mut spec = ProcessSpec::new("pty-group", "/bin/sh");
|
||||
spec.mode = ProcessMode::default_pty();
|
||||
spec.group = true;
|
||||
let err = sup.spawn(spec).expect_err("group=true must be rejected under pty");
|
||||
assert!(err.contains("pipe mode"), "error points at pipe mode: {err}");
|
||||
let err = sup
|
||||
.spawn(spec)
|
||||
.expect_err("group=true must be rejected under pty");
|
||||
assert!(
|
||||
err.contains("pipe mode"),
|
||||
"error points at pipe mode: {err}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//! Compile-mode acceptance (Arc 5 stage 1,
|
||||
//! docs/compile-mode-framing.md, items 1–33; item 34 lives as unit
|
||||
//! tests in src/process.rs, item 35 in
|
||||
//! tests/compile_mode_crdt_acceptance.rs).
|
||||
//! `tests/compile_mode_crdt_acceptance.rs`).
|
||||
//!
|
||||
//! Dispatch-driven: every keybinding claim is exercised through
|
||||
//! `dispatch_key` (never `pmacs.command.invoke`), per the standing
|
||||
|
|
@ -188,7 +188,7 @@ fn pid_alive(pid: i32) -> bool {
|
|||
}
|
||||
|
||||
/// Errors getter marshalled to a comparable Rust shape (encoded as
|
||||
/// one line per entry — mlua tuples don't implement FromLua).
|
||||
/// one line per entry — mlua tuples don't implement `FromLua`).
|
||||
fn compile_errors(s: &EditorState) -> Vec<(String, i64, i64, Option<String>)> {
|
||||
let encoded: String = eval(
|
||||
s,
|
||||
|
|
@ -218,7 +218,7 @@ fn compile_errors(s: &EditorState) -> Vec<(String, i64, i64, Option<String>)> {
|
|||
}
|
||||
|
||||
/// Rendered cells of the active window (copied from the
|
||||
/// m4_acceptance grid helper — cross-crate test code can't import).
|
||||
/// `m4_acceptance` grid helper — cross-crate test code can't import).
|
||||
fn render_active_window_to_grid(
|
||||
state: &mut EditorState,
|
||||
rows: u32,
|
||||
|
|
@ -617,7 +617,11 @@ fn acc14_malformed_rule_containers_fail_closed() {
|
|||
1,
|
||||
"built-in defaults still parse under a non-table container"
|
||||
);
|
||||
assert!(errors_buffer(&s).is_empty(), "no error spam: {}", errors_buffer(&s));
|
||||
assert!(
|
||||
errors_buffer(&s).is_empty(),
|
||||
"no error spam: {}",
|
||||
errors_buffer(&s)
|
||||
);
|
||||
|
||||
// (b) an invalid-pattern entry is skipped; a later valid entry
|
||||
// still matches; one status note counts the skip.
|
||||
|
|
@ -644,7 +648,11 @@ fn acc14_malformed_rule_containers_fail_closed() {
|
|||
vec![("b.c".to_owned(), 3, 0, Some("error".to_owned()))],
|
||||
"the valid entry still matches after the malformed one"
|
||||
);
|
||||
assert!(errors_buffer(&s).is_empty(), "no error spam: {}", errors_buffer(&s));
|
||||
assert!(
|
||||
errors_buffer(&s).is_empty(),
|
||||
"no error spam: {}",
|
||||
errors_buffer(&s)
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -738,7 +746,10 @@ fn acc17_chords_walk_compile_errors_across_files() {
|
|||
// C-x ` is the classic chord for the same dispatcher.
|
||||
ctrl(&mut s, 'x');
|
||||
press(&mut s, KeyCode::Char('`'));
|
||||
assert!(active_buffer_name(&s).ends_with("two.c"), "C-x ` = error.next");
|
||||
assert!(
|
||||
active_buffer_name(&s).ends_with("two.c"),
|
||||
"C-x ` = error.next"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -773,25 +784,24 @@ fn acc19_g_recompiles_and_clears_overlay_spans() {
|
|||
),
|
||||
dir.path(),
|
||||
);
|
||||
assert!(any_styled_cell(&render_active_window_to_grid(&mut s, 8, 60)));
|
||||
assert!(any_styled_cell(&render_active_window_to_grid(
|
||||
&mut s, 8, 60
|
||||
)));
|
||||
assert_eq!(
|
||||
std::fs::read_to_string(&counter).unwrap().lines().count(),
|
||||
1
|
||||
);
|
||||
// g re-runs the stored command. The rerun's output has no
|
||||
// diagnostics, so any styling would be a stale span.
|
||||
// g re-runs the stored command.
|
||||
press(&mut s, KeyCode::Char('g'));
|
||||
assert!(
|
||||
pump_until(&mut s, 10_000, |_| {
|
||||
std::fs::read_to_string(&counter)
|
||||
.map(|c| c.lines().count() == 2)
|
||||
.unwrap_or(false)
|
||||
std::fs::read_to_string(&counter).is_ok_and(|c| c.lines().count() == 2)
|
||||
}),
|
||||
"recompile must actually re-execute the command"
|
||||
);
|
||||
// Wait, then override the fixture: the rerun emits the same
|
||||
// diagnostic (stored command), so instead assert spans were
|
||||
// cleared by checking the fresh run reached its own marker with
|
||||
// The rerun executes the SAME stored command, so its output
|
||||
// carries the same diagnostic; per-run reset is pinned by
|
||||
// checking the fresh run reached its own marker with
|
||||
// exactly one diagnostic parsed (not accumulated).
|
||||
assert!(pump_until(&mut s, 10_000, |s| compilation_text(s)
|
||||
.contains("[compile exited")));
|
||||
|
|
@ -924,8 +934,11 @@ fn acc24_command_path_undo_after_completed_run_recovers_immediately() {
|
|||
dir.path().display().to_string()
|
||||
),
|
||||
);
|
||||
assert!(pump_until(&mut s, 10_000, |s| named_text(s, "*shell-command*")
|
||||
.contains("[shell exited with code 0]")));
|
||||
assert!(pump_until(&mut s, 10_000, |s| named_text(
|
||||
s,
|
||||
"*shell-command*"
|
||||
)
|
||||
.contains("[shell exited with code 0]")));
|
||||
// M-x buffer.undo: the command path rebinding cannot reach. No
|
||||
// pump event will ever arrive — recovery must come from the
|
||||
// buffer.after-edit subscription, synchronously.
|
||||
|
|
@ -938,7 +951,11 @@ fn acc24_command_path_undo_after_completed_run_recovers_immediately() {
|
|||
"desync marker must appear immediately (bite: fails when \
|
||||
recovery only runs at pump/anchor time); buffer:\n{text}"
|
||||
);
|
||||
assert!(errors_buffer(&s).is_empty(), "clean *errors*: {}", errors_buffer(&s));
|
||||
assert!(
|
||||
errors_buffer(&s).is_empty(),
|
||||
"clean *errors*: {}",
|
||||
errors_buffer(&s)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -986,7 +1003,11 @@ fn acc25a_no_hook_shrink_mid_stream_recovers_and_reanchors() {
|
|||
text.find(DESYNC).unwrap() < text.find("two").unwrap(),
|
||||
"streaming continued after the marker:\n{text}"
|
||||
);
|
||||
assert!(errors_buffer(&s).is_empty(), "no spam: {}", errors_buffer(&s));
|
||||
assert!(
|
||||
errors_buffer(&s).is_empty(),
|
||||
"no spam: {}",
|
||||
errors_buffer(&s)
|
||||
);
|
||||
// Pre-marker anchor dropped: RET on the old row reports.
|
||||
exec(&s, "pmacs.editor.goto_byte(0)");
|
||||
let target = first_row;
|
||||
|
|
@ -1101,8 +1122,7 @@ fn acc27_killed_buffer_terminates_run_and_recreates() {
|
|||
let dir = tempfile::tempdir().unwrap();
|
||||
let mut s = editor();
|
||||
compile_run(&s, "echo alive; sleep 30", dir.path());
|
||||
assert!(pump_until(&mut s, 5_000, |s| compilation_text(s)
|
||||
.contains("\nalive\n")));
|
||||
assert!(pump_until(&mut s, 5_000, |s| compilation_text(s).contains("\nalive\n")));
|
||||
exec(
|
||||
&s,
|
||||
r#"
|
||||
|
|
@ -1117,7 +1137,11 @@ fn acc27_killed_buffer_terminates_run_and_recreates() {
|
|||
pump_until(&mut s, 5_000, |s| process_count(s) == 0),
|
||||
"run terminated and forgotten after buffer death"
|
||||
);
|
||||
assert!(errors_buffer(&s).is_empty(), "no spam: {}", errors_buffer(&s));
|
||||
assert!(
|
||||
errors_buffer(&s).is_empty(),
|
||||
"no spam: {}",
|
||||
errors_buffer(&s)
|
||||
);
|
||||
// The next run recreates the buffer and completes.
|
||||
compile_and_finish(&mut s, "echo reborn", dir.path());
|
||||
assert!(compilation_text(&s).contains("reborn"));
|
||||
|
|
@ -1159,7 +1183,10 @@ fn acc28_grep_panel_is_a_locations_buffer() {
|
|||
assert!(pump_until(&mut s, 10_000, search_done), "search completes");
|
||||
assert_eq!(active_buffer_name(&s), "*search-results*");
|
||||
let text = named_text(&s, "*search-results*");
|
||||
assert!(text.contains("f.txt:2:0:"), "structured match line:\n{text}");
|
||||
assert!(
|
||||
text.contains("f.txt:2:0:"),
|
||||
"structured match line:\n{text}"
|
||||
);
|
||||
// Read-only under dispatch.
|
||||
type_str(&mut s, "x");
|
||||
assert_eq!(named_text(&s, "*search-results*"), text, "read-only");
|
||||
|
|
@ -1236,14 +1263,21 @@ fn acc29_grep_kill_mid_search_is_safe_and_masking_is_prevented() {
|
|||
appending (not mask it); tail:\n…{}",
|
||||
&text[text.len().saturating_sub(400)..]
|
||||
);
|
||||
assert!(errors_buffer(&s).is_empty(), "no spam: {}", errors_buffer(&s));
|
||||
assert!(
|
||||
errors_buffer(&s).is_empty(),
|
||||
"no spam: {}",
|
||||
errors_buffer(&s)
|
||||
);
|
||||
|
||||
// (b) killing the panel mid-search: no stale-handle writes, and
|
||||
// the next search recreates the buffer.
|
||||
let mut s = editor();
|
||||
search(&s, "zqxvbn_needle_77", dir.path());
|
||||
assert!(pump_until(&mut s, 10_000, |s| named_text(s, "*search-results*")
|
||||
.contains(":1:0:")));
|
||||
assert!(pump_until(&mut s, 10_000, |s| named_text(
|
||||
s,
|
||||
"*search-results*"
|
||||
)
|
||||
.contains(":1:0:")));
|
||||
exec(
|
||||
&s,
|
||||
r#"
|
||||
|
|
@ -1256,7 +1290,11 @@ fn acc29_grep_kill_mid_search_is_safe_and_masking_is_prevented() {
|
|||
);
|
||||
// Drain whatever the worker still delivers.
|
||||
let _ = pump_until(&mut s, 1_000, |_| false);
|
||||
assert!(errors_buffer(&s).is_empty(), "no spam: {}", errors_buffer(&s));
|
||||
assert!(
|
||||
errors_buffer(&s).is_empty(),
|
||||
"no spam: {}",
|
||||
errors_buffer(&s)
|
||||
);
|
||||
search(&s, "zqxvbn_needle_77", dir.path());
|
||||
assert!(
|
||||
pump_until(&mut s, 15_000, |s| named_text(s, "*search-results*")
|
||||
|
|
@ -1280,8 +1318,11 @@ fn acc30_grep_root_retained_across_interactive_supersede() {
|
|||
),
|
||||
);
|
||||
exec(&s, "pmacs.project.search('zqxvbn_needle_77')");
|
||||
assert!(pump_until(&mut s, 10_000, |s| named_text(s, "*search-results*")
|
||||
.contains("f.txt:2:0:")));
|
||||
assert!(pump_until(&mut s, 10_000, |s| named_text(
|
||||
s,
|
||||
"*search-results*"
|
||||
)
|
||||
.contains("f.txt:2:0:")));
|
||||
// Second search issued from inside the pathless panel, no
|
||||
// opts.root: the panel's stored root must be reused (the "."
|
||||
// fallback would search the test process's cwd and find
|
||||
|
|
@ -1349,7 +1390,10 @@ fn acc32_q_restores_previous_buffer_from_all_three() {
|
|||
// *compilation*
|
||||
compile_and_finish(&mut s, "echo x", dir.path());
|
||||
press(&mut s, KeyCode::Char('q'));
|
||||
assert!(active_buffer_name(&s).ends_with("home.txt"), "q from compile");
|
||||
assert!(
|
||||
active_buffer_name(&s).ends_with("home.txt"),
|
||||
"q from compile"
|
||||
);
|
||||
// *shell-command*
|
||||
exec(
|
||||
&s,
|
||||
|
|
@ -1358,15 +1402,21 @@ fn acc32_q_restores_previous_buffer_from_all_three() {
|
|||
dir.path().display().to_string()
|
||||
),
|
||||
);
|
||||
assert!(pump_until(&mut s, 10_000, |s| named_text(s, "*shell-command*")
|
||||
.contains("[shell exited")));
|
||||
assert!(pump_until(&mut s, 10_000, |s| named_text(
|
||||
s,
|
||||
"*shell-command*"
|
||||
)
|
||||
.contains("[shell exited")));
|
||||
press(&mut s, KeyCode::Char('q'));
|
||||
assert!(active_buffer_name(&s).ends_with("home.txt"), "q from shell");
|
||||
// *search-results*
|
||||
search(&s, "zqxvbn_needle_77", dir.path());
|
||||
assert!(pump_until(&mut s, 10_000, search_done));
|
||||
press(&mut s, KeyCode::Char('q'));
|
||||
assert!(active_buffer_name(&s).ends_with("home.txt"), "q from search");
|
||||
assert!(
|
||||
active_buffer_name(&s).ends_with("home.txt"),
|
||||
"q from search"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1387,7 +1437,13 @@ fn acc33_round_trip_input_is_set_on_generated_buffers() {
|
|||
dir.path().display().to_string()
|
||||
),
|
||||
);
|
||||
assert!(s.core.borrow().active_buffer_round_trips(), "*shell-command*");
|
||||
assert!(
|
||||
s.core.borrow().active_buffer_round_trips(),
|
||||
"*shell-command*"
|
||||
);
|
||||
search(&s, "zqxvbn_needle_77", dir.path());
|
||||
assert!(s.core.borrow().active_buffer_round_trips(), "*search-results*");
|
||||
assert!(
|
||||
s.core.borrow().active_buffer_round_trips(),
|
||||
"*search-results*"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -220,7 +220,10 @@ fn compile_run_converges_and_replica_edit_triggers_recovery() {
|
|||
let src_text = pump_until_text(&mut source, Duration::from_secs(15), "source run", done);
|
||||
let obs_text = pump_until_text(&mut observer, Duration::from_secs(15), "observer run", done);
|
||||
assert_eq!(src_text, obs_text, "byte-identical convergence");
|
||||
assert!(src_text.contains("x.c:1:1: error: boom"), "output replicated");
|
||||
assert!(
|
||||
src_text.contains("x.c:1:1: error: boom"),
|
||||
"output replicated"
|
||||
);
|
||||
assert!(src_text.starts_with("$ sh "), "header replicated");
|
||||
|
||||
// Synthetic accepted replica edit to the generated buffer: the
|
||||
|
|
|
|||
|
|
@ -1892,13 +1892,18 @@ fn m4_6_diag_navigate_commands_and_bindings_are_registered() {
|
|||
)
|
||||
.eval()
|
||||
.expect("keymap.list");
|
||||
// Compile-mode (Q#CM5, docs/compile-mode-framing.md) took the
|
||||
// M-g chords over for the unified dispatchers; the diag commands
|
||||
// stay registered and remain the dispatchers' fallback when no
|
||||
// compile/grep run has claimed the error source, so the
|
||||
// no-LSP-attachment behavior asserted below is unchanged.
|
||||
assert!(
|
||||
bindings.iter().any(|b| b == "M-g n=>diag.next"),
|
||||
"M-g n must bind to diag.next; got: {bindings:?}"
|
||||
bindings.iter().any(|b| b == "M-g n=>error.next"),
|
||||
"M-g n must bind to error.next; got: {bindings:?}"
|
||||
);
|
||||
assert!(
|
||||
bindings.iter().any(|b| b == "M-g p=>diag.previous"),
|
||||
"M-g p must bind to diag.previous; got: {bindings:?}"
|
||||
bindings.iter().any(|b| b == "M-g p=>error.previous"),
|
||||
"M-g p must bind to error.previous; got: {bindings:?}"
|
||||
);
|
||||
|
||||
// Without an LSP attachment, the command should surface a status
|
||||
|
|
|
|||
Loading…
Reference in New Issue