fix(application,frontend): restauration correcte d'une cellule agent en CLI custom après changement de projet — #322 (QA verte)

Symptôme: après changement de projet puis retour, une cellule en CLI
custom retombait en TUI native et un nouveau switch CLI custom repartait
à neuf (aucune conversation reprenable) car le mode préféré de la
cellule n'était pas persisté dans l'arbre de layout.

- domaine frontend: type PreferredView (tui | chat) et champ
  preferredView optionnel sur LeafCell; opération de layout
  setCellPreferredView persistant la surface préférée (tui = défaut,
  champ omis pour garder les arbres existants compacts)
- LayoutGrid: le mode de présentation d'une cellule agent dérive de
  preferredView persisté; le cache localStorage du mode custom n'est
  plus qu'un cache de migration secondaire; remount par clé
  projectId:layoutId:nodeId à chaque changement de projet pour éviter
  tout état résiduel inter-projets; la conversation/engine session/
  session/agentWasRunning de la cellule sont préservés par les
  bascules chat <-> TUI
- useLayout: setCellPreferredView exposé au view model
- tests: layout_usecases (persistance chat puis retour TUI en
  conservant session/conversation/engine/running), layout.test
  (setCellPreferredView chat persiste, tui omet le champ),
  LayoutGrid.chat et cellControlsLayering alignés

QA verte sur fix/batch-322-323-session-restore-prompt-history. Aucun push.
This commit is contained in:
2026-09-11 18:22:57 +02:00
parent 791e068d2e
commit b0d68c2d14
8 changed files with 322 additions and 134 deletions

View File

@ -940,6 +940,16 @@ async fn mutate_set_cell_conversation_missing_leaf_is_not_found() {
#[tokio::test]
async fn mutate_set_cell_preferred_view_persists_chat_then_tui_default() {
let env = mut_env(pid(54)).await;
let original = LeafCell {
id: nid(1),
session: Some(sid(54)),
agent: Some(aid(54)),
conversation_id: Some("conversation-54".to_owned()),
engine_session_id: Some("engine-session-54".to_owned()),
agent_was_running: true,
preferred_view: PreferredView::Tui,
};
seed_layouts(&env.fs, lid(1), &LayoutTree::single(original.clone()));
env.mutate
.execute(MutateLayoutInput {
@ -955,6 +965,16 @@ async fn mutate_set_cell_preferred_view_persists_chat_then_tui_default() {
let tree_json = active_tree_json(&env.fs);
assert_eq!(tree_json["root"]["node"]["preferredView"], "chat");
assert_eq!(
tree_json["root"]["node"]["conversationId"],
"conversation-54"
);
assert_eq!(
tree_json["root"]["node"]["engineSessionId"],
"engine-session-54"
);
assert_eq!(tree_json["root"]["node"]["session"], sid(54).to_string());
assert_eq!(tree_json["root"]["node"]["agentWasRunning"], true);
let out = env
.mutate
@ -970,7 +990,13 @@ async fn mutate_set_cell_preferred_view_persists_chat_then_tui_default() {
.expect("set_cell_preferred_view restores tui");
match &out.layout.root {
LayoutNode::Leaf(l) => assert_eq!(l.preferred_view, PreferredView::Tui),
LayoutNode::Leaf(l) => {
assert_eq!(l.preferred_view, PreferredView::Tui);
assert_eq!(l.session, original.session);
assert_eq!(l.conversation_id, original.conversation_id);
assert_eq!(l.engine_session_id, original.engine_session_id);
assert_eq!(l.agent_was_running, original.agent_was_running);
}
_ => panic!("expected leaf root"),
}
let tree_json = active_tree_json(&env.fs);