From ec05328a4413e38ebdcb84365938345e9cf47431 Mon Sep 17 00:00:00 2001 From: Mark Jaquith Date: Sat, 5 Sep 2026 17:08:19 -0400 Subject: fix: prevent OpenCode child prompts from leaving status blocked (#3669) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Child permission and question reports omitted the session ID, clearing Herdr’s root session identity and preventing later lifecycle reports from clearing the blocked state. Track child-to-parent relationships and report prompt transitions with their root session ID, including for nested children. This preserves identity and lets existing ownership checks reject unrelated sessions. Add regression coverage for prompt routing and lifecycle transitions, and bump the OpenCode integration version to 11. --- src/terminal/state.rs | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'src/terminal') diff --git a/src/terminal/state.rs b/src/terminal/state.rs index ee170198..dc28af10 100644 --- a/src/terminal/state.rs +++ b/src/terminal/state.rs @@ -4785,6 +4785,60 @@ mod tests { .contains_key("herdr:opencode")); } + #[test] + fn opencode_child_prompt_reports_with_root_id_preserve_lifecycle_authority() { + let mut terminal = test_terminal(); + let root = crate::agent_resume::AgentSessionRef::id("opencode-root").unwrap(); + anchor_full_lifecycle_session( + &mut terminal, + Agent::OpenCode, + "herdr:opencode", + "opencode", + root.clone(), + ); + + // The plugin projects child permission/question events onto their root. + for (seq, state) in [ + (20, AgentState::Working), + (21, AgentState::Blocked), + (22, AgentState::Working), + (23, AgentState::Idle), + ] { + let mutation = terminal + .set_hook_authority_with_session_ref( + "herdr:opencode".into(), + "opencode".into(), + state, + None, + Some(root.clone()), + Some(seq), + ) + .expect("root-scoped lifecycle report should be accepted"); + assert!(!mutation.session_ref_changed); + assert_eq!(terminal.state, state); + assert_eq!( + terminal + .hook_authority + .as_ref() + .unwrap() + .session_ref + .as_ref(), + Some(&root) + ); + } + + let foreign_child_prompt = terminal.set_hook_authority_with_session_ref( + "herdr:opencode".into(), + "opencode".into(), + AgentState::Blocked, + None, + crate::agent_resume::AgentSessionRef::id("opencode-other-root"), + Some(24), + ); + assert!(foreign_child_prompt.is_none()); + assert_eq!(terminal.state, AgentState::Idle); + } + #[test] fn opencode_tui_selection_reanchors_full_lifecycle_authority() { let mut terminal = test_terminal(); -- cgit v1.2.3