fix(TASK-035): close agent-bracket leaks and prove compression/role-hook seams

- Open the top-level agent bracket only at committed dispatch sites so
  inspection-flag early returns (--list-sessions, --info, --macro,
  --rebuild-rag, --init-*, --build-tools) never leak agent.started
- Close the agent bracket ahead of process::exit in shell_execute's
  execute branch, where the dispatch wrapper's close is unreachable
- Extract the ask-loop auto-compression seam and cover the
  session.compressed success path at both call sites with sink tests
- Prove Session.role_hooks never serializes and that injected
  role_hooks YAML is ignored on deserialization
This commit is contained in:
Dark-Alex-17 committed 2026-09-16 13:44:35 -06:00
1 parent 8710737964
commit 0c6b7c8594
3 files changed
+174 -6

No files matched your search

+31
View File
@@ -1266,6 +1266,37 @@ mod tests {
assert!(!yaml.contains("token_usage"));
}
#[test]
fn session_role_hooks_is_not_serialized() {
let content = "---\nhooks:\n turn.completed:\n - name: role-probe\n command: role-cmd\n---\nPrompt";
let mut session = Session::default();
session.set_role(Role::new("hooked", content));
assert!(session.role_hooks().is_some());
let yaml = serde_yaml::to_string(&session).unwrap();
assert!(
!yaml.contains("role_hooks"),
"the role-hooks snapshot must never reach session YAML"
);
assert!(
!yaml.contains("role-cmd"),
"no hook command text may leak into session YAML under any key"
);
}
#[test]
fn session_deserialization_ignores_injected_role_hooks() {
// A session file is attacker-writable data: a `role_hooks:` key
// crafted into it must never deserialize into executable hook
// config — hooks are only ever re-read from the role file itself.
let yaml = "model: ''\nmessages: []\nrole_hooks:\n session.started:\n - name: injected\n command: touch /tmp/pwned\n";
let session: Session = serde_yaml::from_str(yaml).unwrap();
assert!(
session.role_hooks().is_none(),
"injected role_hooks in session YAML must be ignored"
);
}
#[test]
fn session_cost_accumulates_and_survives_yaml_round_trip() {
let mut session = Session::default();