Skip to content

Fix/postures debloquees - #337

Merged
bbstudioapp merged 10 commits into
developfrom
fix/postures-debloquees
Aug 12, 2026
Merged

Fix/postures debloquees#337
bbstudioapp merged 10 commits into
developfrom
fix/postures-debloquees

Conversation

@bbstudioapp

Copy link
Copy Markdown
Owner

No description provided.

Les notes de version 0.6.0 annoncent les postures imposées et les pauses
scénarisées, mais leur tirage était conditionné à un toggle de la section
debug de l'écran SONS, off par défaut : la fonctionnalité était invisible
pour tout le monde depuis deux versions.

Le réglage devient une préférence utilisateur assumée (`pref.scripted_breaks`,
on par défaut, section « Déroulé de séance » du Profil) au lieu d'un flag
debug. L'ancienne clé `debug.scripted_breaks` est abandonnée plutôt que
migrée : la reprendre rendrait le off historique collant.

Les presets de debug `reset` et `mi_carriere_profondeur`, qui recopiaient les
défauts d'usine, sont réalignés sur le nouveau défaut.
…vancé

Le coach Marc a coûté un correctif en 0.6.1 : son palier de déblocage était
franchissable une seule fois, et qui l'avait dépassé avant sa sortie restait
verrouillé définitivement. Les 5 milestones `intro_posture_*` (levels 4 à 12)
sont exposées au même risque pour un profil déjà au-delà.

Ces tests tournent sur le vrai catalogue `assets/career/milestones.json` et
établissent que `level` est un plancher et non une fenêtre, que les postures
remontent en tête de file (règle overdue), et qu'il faut 3 séances
moyenne/longue — 5 courtes — pour les débloquer toutes. Ils caractérisent
aussi l'exigence de capacité de `intro_posture_on_back`
(`hold.full.streak ≥ 5`), seul palier qui attend une preuve physique.
… une pause

La sortie d'une pause scénarisée qui impose une nouvelle posture arme un gate
de mise en place : `_checkSteps` ne consomme plus de step et `_onTick` gèle
l'horloge jusqu'à la validation. Trois chemins traversaient cette fenêtre sans
jamais passer par `_exitBreak`, donc sans lever le gate — il survivait au saut
de timeline et le gel délibéré devenait définitif (ticker actif, plus rien qui
avance, jusqu'au timeout de 90 s) :

- « je peux pas » pendant la pause ou pendant l'attente de mise en place — le
  bouton n'est masqué par aucun des deux états ;
- « Utilise-moi » et le retry milestone, via `requestUpgrade` (qui remettait
  déjà l'état de pause à zéro, mais pas le gate) ;
- la régénération post-défi, par symétrie.

Le bouton reste actif dans les deux états : c'est la seule sortie visible en
séance, et une pause de 60 à 120 s suivie d'une attente n'est pas un moment où
la priver serait défendable. Le flow d'échec annule la pause au lieu de la
subir — sans appliquer sa posture, dont l'annonce ne sera pas prononcée.

Corrige aussi le retour d'arrière-plan pendant une pause : `resume()` relançait
l'audio d'effort coupé par l'entrée dans la pause. Chemin atteignable par tout
le monde, une notification suffit.

Les 4 tests runtime ajoutés sont rouges sans ces correctifs.
Le gel de mise en place était un drapeau armé à la sortie d'une pause et
levé par un seul chemin nominal. Quatre chemins distincts l'ont laissé
orphelin derrière eux, et chaque correctif ciblé n'a jamais rien dit du
suivant : on cesse de fermer des chemins.

`PostureGate` porte désormais ce qui *justifie* le gel — identité de la
timeline, tête de lecture, décalage d'horloge, numéro de flow d'échec — et
`awaitingPostureReady` rejoue cette justification à chaque lecture. Les
trois premières composantes sont les seules variables qui portent la
progression d'une séance : rebattre la timeline sans en toucher aucune est
impossible. Un chemin qui la rebat n'a donc rien à lever, aujourd'hui comme
demain — les trois levées explicites de la passe 2 sont retirées.

Une garde centrale enterre à chaque battement une ancre dont la
justification est tombée (timeout de sécurité compris), pour qu'un gel
enterré ne ressuscite pas si sa justification redevenait vraie.

Ferme au passage le quatrième chemin, jamais corrigé : un défi armé au même
tick qu'un ordre de posture n'empile plus son bandeau sur lui, et sa clôture
ne laisse plus la séance gelée.
Les horaires de pause et de défi étaient calculés par deux fonctions
indépendantes qui visaient toutes deux le milieu de la séance. Le trou
d'effort de la pause avalait le trigger du défi médian, que la boucle de
génération réémettait à `break.endTime` : l'ordre de se mettre en position
et l'annonce du défi tombaient au même instant.

Le planificateur de pauses reçoit les créneaux de défi et cède le passage —
les horaires de défi ne bougent pas. Sans défi, les horaires nominaux sont
inchangés.

Balayage sur le générateur réel (8 durées × 4 comptes de défis × 60 graines,
1920 générations) : 175 collisions avant, 0 après.
`_excisChallengeFromSession` reconstruit la séance sans passer `breaks:` ni
`initialPose:` — le défaut par défaut de `Session.breaks` est `const []`,
donc toute la mise en scène restante disparaissait dès qu'un défi avait eu
lieu, sur la fonctionnalité qu'on est en train d'allumer. La liste garde sa
longueur (`_nextBreakIndex` pointe dedans) et ses horaires suivent le même
décalage que les steps.
…éance

`stop()` et `_finish()` remettaient la timeline à zéro mais laissaient courir
le `Timer` de sécurité du gel de posture : ~90 s plus tard, il réveillait un
contrôleur devenu inactif ou l'écran de fin. Il n'y a pas de gel à lever —
il est tombé de lui-même — juste une ressource à libérer, aux trois bornes
de vie d'une séance (`start` le faisait déjà). Entre elles, c'est la garde du
battement qui s'en charge.
…le levait

La garde ne cherchait que la chaîne `confirmPostureReady`. Tous les
fichiers du contrôleur sont des `part of` : écrire `_postureGate = null`
depuis n'importe lequel d'entre eux est valide et passait au travers sans
un mot — exactement le motif que ces passes ont servi à éliminer.

Elle scanne désormais les écritures du champ dans toute sa library privée
et exige que l'ensemble des membres qui y touchent soit exactement les six
sites légitimes : naissance de l'ancre, validation joueuse, ramasse-miettes
du battement, et les trois bornes de vie d'une séance.
La clause d'identité protège contre le remplacement de la séance, pas
contre une mutation en place de ses listes. Aucun code ne le fait
aujourd'hui, rien ne l'empêche demain et rien ne le détecterait : le
noter là où la décision vit plutôt que de laisser « par définition »
porter plus qu'il ne prouve.
Intègre le lot « durée de séance faussée par les défis » (13 commits) avant
que celui-ci ne parte vers develop, pour que la demande de fusion des postures
soit propre une fois la première intégrée.

Deux conflits, résolus en gardant de chaque côté ce dont il a raison.

session_controller_challenge.dart — bloc de reconstruction après un défi :
`initialPose` et `breaks` viennent des postures (sans ce report, `Session.breaks`
retombe à `const []` et la mise en scène restante disparaît dès qu'un défi se
termine). Le commentaire vient des défis : celui des postures affirmait que
l'excision retire « 2 steps (breath + défi) », faux depuis la refonte des défis
en streaming où un seul step tombe dans la timeline.

career_session_generator.dart — appel à `_computeBreakTriggerTimes` : les deux
paramètres survivent, `genUntil: ctx.genUntil` (accesseur dérivé) **et**
`challengeWindows`. À ce point du code la réserve de défis vaut encore 0, donc
`ctx.genUntil` égale la valeur qu'utilisait la branche postures — pas de
changement de sens. `_estimatedChallengeDuration` gardant sa signature, les
fenêtres de défi héritent d'office de la durée désormais plafonnée par format.

Vérifié sur l'arbre fusionné : 978 tests verts (818 base + 29 postures +
131 défis, égalité exacte : aucun test perdu), `flutter analyze` propre, format
inchangé, l10n générés conformes à `gen-l10n`. Collisions pauses/défis : 0 sur
4800 générations à défis courts et 0 sur les formats du catalogue, hors le seul
couple `longue` × 4 défis au plafond (4 cas sur 500) — identiques avant fusion.
Durées avec défis : 10,8 min en express, 39,7 min en moyenne.
@bbstudioapp
bbstudioapp merged commit 25657d5 into develop Aug 12, 2026
5 checks passed
@bbstudioapp
bbstudioapp deleted the fix/postures-debloquees branch August 12, 2026 07:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant