Contexte
Suite des travaux qualité de complétion (session 2026-06-19). Déjà livré sur main :
bbc69ba — fix panneau QML (font.families → font.family) : déblocage des frappes avalées.
7685f71 — langue stricte : lang=fr exclut réellement l'anglais (ré-étiquetage par dominance + exclusion + filtre des mots appris).
ca4369b — accord grammatical nombre/genre via Lefff (morph.tsv) + contexte de phrase via SurroundingText (fenêtre 8). Spec + plan : docs/superpowers/specs/2026-06-19-sentence-context-agreement-design.md et docs/superpowers/plans/2026-06-19-sentence-context-agreement.md.
3db29a4 — élisions combinées (j'ai, c'est, qu'il, d'un, aujourd'hui…) ajoutées au vocab (scripts/extract_elisions.py), donc proposées ET exploitées par les n-grammes.
Ce ticket regroupe 4 améliorations restantes (audit UX), classées par priorité retenue.
Méthode de test (rappel)
- Daemon en direct :
printf '{"context":["..."],"prefix":"..."}\n' | nc -U /tmp/ime-predictord.sock (réponse JSON candidates). {"stats":true} → état. Daemon de test isolé : predictord <model>/words.tsv /tmp/test.sock (~3 s de chargement).
- e2e :
kwrite + wtype "...", vérifier le texte committé.
- Build :
nix build .#predictord / .#model ; e2e via nix build .#nixosConfigurations.thorfinn.config.i18n.inputMethod.package --override-input predictive-ime path:..
A — Mots appris trop dominants (priorité haute)
Problème. Un mot appris est offert avec un score 1e18 + count qui court-circuite la fréquence → un mot tapé 1-2 fois (j'aimerai) coiffe un mot du modèle bien plus fréquent (j'ai, 1,6M).
Code. daemon/predictord.cpp :
completePrefix : boucle userUni → offer(kv.first, 1e18 + double(kv.second)).
predictNext : boucle userBi → push(p.first) (priorité absolue).
Approche proposée. Mettre le boost appris sur la même échelle que les scores du modèle au lieu d'un plancher écrasant : p.ex. multiplier le prior fréquence du mot par un facteur fonction de la confiance (userTrusted/count) plutôt que d'ajouter 1e18. Un mot appris fréquent reste devant ; un mot appris rare ne bat plus un mot modèle massivement plus courant. Garder une borne min pour que l'apprentissage reste visible.
Test. {"context":[],"prefix":"j'a"} → j'ai devant j'aimerai (si j'aimerai peu appris). Non-régression : un mot vraiment appris souvent reste prioritaire.
B — Proclitique nu rétrogradé (priorité moyenne, petit)
Problème. Taper j' propose j' (le proclitique seul) en tête, avant j'ai/j'aime. Idem c', qu', d', n', s', t', m', l'.
Code. daemon/predictord.cpp completePrefix (classement des candidats exacts).
Approche. Rétrograder (petit ÷K, pas d'exclusion) les candidats qui sont un proclitique d'élision nu (forme = proclitique + apostrophe finale, rien après) quand le préfixe vaut ce proclitique — ils sont rarement le mot final voulu. Liste des proclitiques déjà connue (cf determiner/extract_elisions.py).
Test. {"context":[],"prefix":"j'"} → j'ai/j'aime remontent au-dessus de j' nu.
C — Espacement typographique français (priorité moyenne)
Problème. frenchSpacing=false : pas d'espace fine insécable (U+202F) avant ; : ! ? ni à l'intérieur des guillemets « … ». Convention typographique FR.
Code. engine/predict.cpp — il existe déjà un usage de U+202F (ic->commitString("\xE2\x80\xAF")) et le réglage frenchSpacing (config). À câbler : sur insertion de ; : ! ? », préfixer d'une fine insécable ; après «, en ajouter une.
Approche. Implémenter le comportement derrière frenchSpacing (défaut à décider — proposer true mais réglable ; certaines apps/contextes code n'en veulent pas). Gérer le cas où l'utilisateur a déjà tapé l'espace.
Test. e2e kwrite : taper bonjour ! → bonjour ! avec fine insécable (U+202F) avant !.
D — Majuscule automatique (priorité moyenne, petit)
Problème. autoCapitalize=false : pas de capitale en début de champ ni après . ! ?.
Code. engine/predict.cpp — capFirst/applyCase existent déjà ; commitWord capitalise en début de phrase uniquement quand state->ctx est vide. Étendre : détecter une fin de phrase (. ! ?) dans le contexte/SurroundingText et capitaliser le mot suivant.
Approche. Derrière autoCapitalize (réglable) : si le dernier token de contexte non vide se termine par . ! ? (ou contexte vide), capitaliser la 1ʳᵉ lettre du mot committé. Ne pas toucher aux mots déjà capitalisés ni aux sigles.
Test. bonjour. comment → Comment capitalisé.
Notes transverses
- Tous les boosts/pénalités restent multiplicatifs, jamais de filtre dur (cohérent avec l'accord et la langue stricte ; ne jamais renvoyer 0 sauf exclusion de langue explicite).
- Réglages exposés dans
config.json (hot-reload) + documentés au README, comme langBoost/agreeBoost.
- Suivi annexe (hors de ce ticket) : ajouter des tests committés dans
daemon/test_predict.py pour accord/élisions/langue (jusqu'ici vérifiés via socket/e2e ad-hoc) ; et l'edge « les petits cha → chaque » (déterminant singulier non pénalisé).
🤖 Generated with Claude Code
Contexte
Suite des travaux qualité de complétion (session 2026-06-19). Déjà livré sur
main:bbc69ba— fix panneau QML (font.families→font.family) : déblocage des frappes avalées.7685f71— langue stricte :lang=frexclut réellement l'anglais (ré-étiquetage par dominance + exclusion + filtre des mots appris).ca4369b— accord grammatical nombre/genre via Lefff (morph.tsv) + contexte de phrase viaSurroundingText(fenêtre 8). Spec + plan :docs/superpowers/specs/2026-06-19-sentence-context-agreement-design.mdetdocs/superpowers/plans/2026-06-19-sentence-context-agreement.md.3db29a4— élisions combinées (j'ai,c'est,qu'il,d'un,aujourd'hui…) ajoutées au vocab (scripts/extract_elisions.py), donc proposées ET exploitées par les n-grammes.Ce ticket regroupe 4 améliorations restantes (audit UX), classées par priorité retenue.
Méthode de test (rappel)
printf '{"context":["..."],"prefix":"..."}\n' | nc -U /tmp/ime-predictord.sock(réponse JSONcandidates).{"stats":true}→ état. Daemon de test isolé :predictord <model>/words.tsv /tmp/test.sock(~3 s de chargement).kwrite+wtype "...", vérifier le texte committé.nix build .#predictord/.#model; e2e vianix build .#nixosConfigurations.thorfinn.config.i18n.inputMethod.package --override-input predictive-ime path:..A — Mots appris trop dominants (priorité haute)
Problème. Un mot appris est offert avec un score
1e18 + countqui court-circuite la fréquence → un mot tapé 1-2 fois (j'aimerai) coiffe un mot du modèle bien plus fréquent (j'ai, 1,6M).Code.
daemon/predictord.cpp:completePrefix: boucleuserUni→offer(kv.first, 1e18 + double(kv.second)).predictNext: boucleuserBi→push(p.first)(priorité absolue).Approche proposée. Mettre le boost appris sur la même échelle que les scores du modèle au lieu d'un plancher écrasant : p.ex. multiplier le prior fréquence du mot par un facteur fonction de la confiance (
userTrusted/count) plutôt que d'ajouter1e18. Un mot appris fréquent reste devant ; un mot appris rare ne bat plus un mot modèle massivement plus courant. Garder une borne min pour que l'apprentissage reste visible.Test.
{"context":[],"prefix":"j'a"}→j'aidevantj'aimerai(sij'aimeraipeu appris). Non-régression : un mot vraiment appris souvent reste prioritaire.B — Proclitique nu rétrogradé (priorité moyenne, petit)
Problème. Taper
j'proposej'(le proclitique seul) en tête, avantj'ai/j'aime. Idemc',qu',d',n',s',t',m',l'.Code.
daemon/predictord.cppcompletePrefix(classement des candidats exacts).Approche. Rétrograder (petit
÷K, pas d'exclusion) les candidats qui sont un proclitique d'élision nu (forme = proclitique + apostrophe finale, rien après) quand le préfixe vaut ce proclitique — ils sont rarement le mot final voulu. Liste des proclitiques déjà connue (cfdeterminer/extract_elisions.py).Test.
{"context":[],"prefix":"j'"}→j'ai/j'aimeremontent au-dessus dej'nu.C — Espacement typographique français (priorité moyenne)
Problème.
frenchSpacing=false: pas d'espace fine insécable (U+202F) avant;:!?ni à l'intérieur des guillemets« … ». Convention typographique FR.Code.
engine/predict.cpp— il existe déjà un usage de U+202F (ic->commitString("\xE2\x80\xAF")) et le réglagefrenchSpacing(config). À câbler : sur insertion de; : ! ? », préfixer d'une fine insécable ; après«, en ajouter une.Approche. Implémenter le comportement derrière
frenchSpacing(défaut à décider — proposertruemais réglable ; certaines apps/contextes code n'en veulent pas). Gérer le cas où l'utilisateur a déjà tapé l'espace.Test. e2e kwrite : taper
bonjour !→bonjour !avec fine insécable (U+202F) avant!.D — Majuscule automatique (priorité moyenne, petit)
Problème.
autoCapitalize=false: pas de capitale en début de champ ni après. ! ?.Code.
engine/predict.cpp—capFirst/applyCaseexistent déjà ;commitWordcapitalise en début de phrase uniquement quandstate->ctxest vide. Étendre : détecter une fin de phrase (. ! ?) dans le contexte/SurroundingText et capitaliser le mot suivant.Approche. Derrière
autoCapitalize(réglable) : si le dernier token de contexte non vide se termine par. ! ?(ou contexte vide), capitaliser la 1ʳᵉ lettre du mot committé. Ne pas toucher aux mots déjà capitalisés ni aux sigles.Test.
bonjour. comment→Commentcapitalisé.Notes transverses
config.json(hot-reload) + documentés au README, commelangBoost/agreeBoost.daemon/test_predict.pypour accord/élisions/langue (jusqu'ici vérifiés via socket/e2e ad-hoc) ; et l'edge « les petits cha → chaque » (déterminant singulier non pénalisé).🤖 Generated with Claude Code