10 KiB
SESSION_V2_SCRATCHPAD_SAVE_AND_RESTORE_V1
Travail local du 22 octobre 2026 dans
labfy-session-v2-scratchpad-20261022, branche
feat/session-v2-scratchpad-20261022, base 5aeff41d.
Les correctifs focus/recovery préexistants restent présents. Aucun commit,
push, déploiement, reload QuickShell ou changement de configuration utilisateur.
Résultat et fichiers
session_v2/snapshot.py,schema.py,schema-v2.json,model.py: capture version 2, scratchpadnone/hidden/shown, rectangle IPC, focus de slot et workspace focalisé explicite. Une fenêtre cachée aworkspace=null; l’espace interne__i3_scratchn’est jamais persisté comme workspace cible. Les groupes de fenêtres scratchpad restent refusés. Aucun titre ni argv n’est capturé.schema-v1.jsonreste inchangé.layout.py,slots.py,executor.py,cli.py: phase de placements descriptive, mapping conservant la visibilité des slots interchangeables, staging évité pour les scratchpads existants, commandes con_id ciblées, géométrie et état final vérifiés. Les nouvelles fenêtres arrivent par le prewarm existant, depuis le catalogue statique de confiance.recovery.py: nouveaux journaux version 2 avec appartenance scratchpad, lecture conservatrice des journaux V1, prédiction de focus avant chaque commande, refus d’une feuille dont la commande affecterait un ancêtre scratchpad non admis. Aucun conflit ni pending n’est effacé pour progresser.controlcenter/SessionExitGate.qml:reasonJSON visible, explications, action de sortie toujours bloquée après échec jusqu’à un choix humain. Le ControlCenter fournit déjà ce JSON ; le diagnostic se perdait dans Gate qui privilégiait le stderr générique.tools/build-session-v2.py: inclusion des deux schémas dans le bundle.- Tests :
test_scratchpad.py,scratchpad_runtime.py,validate_scratchpad.py, extension deqml/tst_exit_v2.qml.
Les fonctions existantes Guard.prepare, les frontières de vérification finale,
la normalisation canonique des chemins et les tests focus/recovery initiaux
sont conservés. Le présent delta ne change pas la politique d’autotiling.
Preuves et couverture
Le SwayFX installé échoue au démarrage headless dans ce sandbox faute de DRM.
Un paquet Sway 1.12 Arch a été extrait sous /tmp/labfy-scratchpad-tool, sans
installation système ni substitution de bibliothèque. ldd résout son
libwlroots-0.20.so installé sans dépendance manquante. Son renderer Pixman a
permis la campagne privée. Provenance :
paquet Arch.
La prédiction du nouvel ajout scratchpad est étayée par le
code Sway 1.12 root.c
et les observations privées : l’ajout initial refocalise le workspace source,
même si la cible était inactive. Le
code scratchpad.c
explique également pourquoi scratchpad show est un toggle à ne jamais
émettre sans preuve de visibilité préalable.
La suite backend comprend 83 tests, dont 12 nouveaux tests scratchpad :
- capture/relecture déterministes, Store nommé et checkpoint automatique temporaires, compatibilité des snapshots V1 ;
- Kitty caché + autre Kitty visible, montré et focalisé, plusieurs cachés, workspace vide focalisé, splits et flottants ;
- références, types et versions stricts, JSON Schema V2, focus caché interdit ;
- scratchpad modifié entre lectures, fausse identité PID, output déplacé, focus ambigu, intention interrompue conservée et groupe non admis ;
- mapping sans exposition d’un pair caché et prédiction de focus avant IPC.
Autres suites : 29 tests focus Python, 13 tests QML via PySide6, 6 résultats
QtTest (4 tests métier et initialisation/nettoyage). Les tests QML vérifient
notamment CAPABILITY_UNSUPPORTED, SNAPSHOT_CHANGED, RECOVERY_REQUIRED et
un code inconnu, avec zéro commande de sortie après échec.
La campagne scratchpad_runtime.py utilise des Kitty réels uniquement sous
le socket enfant privé. Elle conserve arbres JSON et PNG avant/après :
- deux Kitty du même app_id (un caché et un en split), un scratchpad montré, un flottant ordinaire et un autre split ;
- capture, Store temporaire, perturbation puis restauration ; égalité exacte des géométries et états, focus final vérifié ;
- show/hide explicite après restauration, sans perte d’appartenance ;
- reconstitution de l’appartenance depuis des fenêtres ordinaires ;
- toutes les fenêtres cachées et workspace vide focalisé ;
- disparition puis relancement réel des deux Kitty par le catalogue statique TEST_ONLY, restauration cachée et second apply sans double lancement.
Les PNG avant/après de la campagne privée ont été inspectés. Ils montrent le
même arrangement ; ce sont des terminaux synthétiques noirs exécutant sleep,
pas une preuve de contenu applicatif ou d’une session utilisateur physique.
La trace de commandes vérifie qu’aucun move vers un workspace ni scratchpad show ne cible la feuille destinée à rester cachée pendant le premier apply.
Régressions privées supplémentaires retenues sous
/tmp/labfy-scratchpad-regressions-mivwhgzo/ : scénario Limusic V1 et refus
absent/dupliqué ; quatre scénarios de stabilité des chemins ; huit scénarios
login/focus (normal, workspace vide, fenêtre tierce, deux focus tardifs,
map dans le gap IPC, placement provider, crash en milieu d’intention).
Tous ont leurs succès ou refus attendus. Seul le nettoyage du harness a été
adapté dans un wrapper temporaire pour ne jamais envoyer SIGCONT.
Aucun scénario envoyant réellement SIGSTOP/SIGCONT à autotiling n’a été
exécuté. Son protocole est testé par simulation ; les tests headless réels
utilisent Guard sans autotiling. Aucun socket utilisateur ni fichier sous
/run/user/1000/labfy-sway n’est utilisé par les validations.
Limites assumées
- Enregistrer Kitty ne ressuscite pas btop. Le processus interne, la commande,
le titre et le contenu du terminal ne font pas partie du snapshot. Le lancement
de production reste
/usr/bin/kittyissu du catalogue. Lesleepdu test vient exclusivement d’un catalogue statique TEST_ONLY, jamais des données capturées. - Les slots de même application sans provider exact restent interchangeables, sans promesse de retrouver le rôle sémantique d’un terminal particulier.
- Les groupes scratchpad, fullscreen et géométries refusées par le client ou le compositeur produisent un échec précis, pas un succès partiel.
- Pour une feuille cachée, le rectangle sauvegardé est le rectangle IPC caché.
L’ancienne transformation d’output interne à Sway n’est pas persistée. Un
futur
showmanuel peut adapter ses coordonnées à l’output alors actif. - Le
showintermédiaire d’une feuille explicitement montrée laisse Sway convertir le rectangle à l’output ; la géométrie sauvegardée est ensuite appliquée et vérifiée exactement. Le délai de 300 ms des transitions Wayland ne remplace aucune preuve. Une modification tardive reste refusée. - Une reprise après incident ne reconstruit pas automatiquement un scratchpad
dont le placement diffère de l’origine prouvée : reçu
SCRATCHPAD_RECOVERY_REQUIRES_REVIEW, fenêtres et journal conservés. - Pas de validation de login physique, de multi-output physique, de SwayFX avec DRM, ni de contenu Firefox/btop. Les contrôles IPC ne constituent pas un CAS.
- Aucun nouveau checkpoint réel n’a été écrit. L’ancien état utilisateur
RECOVERY_REQUIREDdemeure bloquant ; ce patch ne l’acquitte pas.
Reproduction sûre et future validation
Depuis ce worktree, avec un binaire Sway compatible disponible :
python -B tests/session_v2/validate_scratchpad.py \
--sway /tmp/labfy-scratchpad-tool/usr/bin/sway --renderer pixman
Le validateur crée ses HOME/XDG sous /tmp, retire les variables graphiques
héritées et lance exclusivement un compositeur headless avec configuration
TEST_ONLY. Il conserve les logs et le chemin des arbres/PNG dans son répertoire
de résultats. Aucun accès privilégié n’est requis. Les clients et compositeurs
enfants sont arrêtés par le nettoyage du laboratoire ; aucun signal de
suspension/reprise n’est envoyé.
Pour la future recette humaine : examiner d’abord le diff et les preuves privées ; reproduire avec plusieurs fenêtres de terminal identifiables et un scratchpad montré/caché dans un compositeur privé. Toute recette sur le bureau réel exigera une autorisation séparée et la résolution humaine préalable du journal ancien. Ne pas effacer ce journal pour débloquer apply, ne pas tester via logout/reboot et ne pas recharger QuickShell pour cette vérification. Vérifier séparément la sauvegarde d’une fenêtre Kitty et l’absence de promesse sur le redémarrage de btop.
Résultats finaux conservés
- Validateur reproductible :
/tmp/labfy-scratchpad-validation-imopv2no/; tous les codes retour sont zéro (backend, focus, QML Python/Qt, runtime, diff-check). - Campagne réelle finale :
/tmp/labfy-session-v2-TEST_ONLY-1gtxan7m/; snapshots, arbres, commandes, résultats, PNG avant/après/show/hide, nouvelles appartenances, toutes cachées et lancement Kitty à froid. - Bundle local candidat :
/tmp/labfy-scratchpad-final-evidence/bundle/; compilation C17 stricte, schémas V1/V2 embarqués, non installé/non signé. - Relecture locale du delta et vérification des patches initiaux : les diffs initiaux hors executor/recovery restent identiques octet pour octet ; dans ces deux fichiers, les mécanismes initiaux sont conservés et les suites de non-régression passent. Cela ne remplace pas la revue humaine avant commit.
qmllintdu composant Gate, parsing AST Python, contrôle des deux JSON Schemas etgit diff --check: succès.- Paquet Sway de test, SHA-256 :
5704cfc9eba9804116c665cb4450352bf5a240c1a8300b36aca3218116648443.
Revue complémentaire du focus de layer-shell
Le dialogue de sortie QuickShell peut rendre le focus de GET_TREE non observable. Dans ce cas, le snapshot V2 garde explicitement focus=null et focus_workspace=null, sans inférer de fenêtre depuis les piles internes. Plusieurs noeuds simultanément marqués focused restent refusés avec SNAPSHOT_FOCUS_AMBIGUOUS. Les tests vérifient cette distinction et le round-trip du Store temporaire.