Conversation
) The v110 8dp gutters never landed visually : the global Surface(padding(horizontal=8dp)) around the NavHost added up to 16dp per side (spotted by XaTriX, dogfooding v111). List now adds 0 ; host 8dp = intended gutter. Tech debt #398. Merge --admin (exception mono-maintainer documentée dans AGENTS.md : pas de reviewer sûr disponible, CI verte + validation locale 2 parts). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ur dev (#401) * chore(release): bump versionName to 0.9.0 + changelog v113 (candidate) Frozen promotion candidate from dev bb3ee57 (dev build 112) : the night run keeps merging on dev, this branch is what Codex reviews and what ships to beta if the review is clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(changelog): v113 lot = 7 PR, pas 8 (review Codex du candidat) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(changelog): statut v113/0.9.0 → open (bêta shippée) Promotion #400 mergée no-ff 0313e8f, run beta 27310478252 vert (Play open testing + F-Droid .beta), tag app-v113. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
… profile (#403) * feat(search): author filter (pseud=) through model, network and data layers SearchRequest.pseudo -> HfrClient searchTopics pseud= query param -> DefaultSearchRepository forward. Contract verified live 2026-06-11 : author-only search works in all three shapes (explicit cat titre=1 = existing fixture, explicit cat titre=3, all-cats titre=3 via the 302 multi-cat pivot redirect OkHttp follows). Diagnostics log a presence flag only, same redaction contract as the query. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(search): author field in the search form + prefilled author-only entry SearchUiState.pseudo + PseudoChanged intent (same invalidation contract as QueryChanged), submit legal when query OR pseudo is non-blank, retry and pivot re-scoping keep the author filter. SearchViewModel moves to @AssistedInject (ProfileViewModel pattern) so a nav route can hand an initialPseudo at construction : non-blank -> author-only search fired in init. Screen grows the « Auteur (pseudo) » field, an optional back affordance for pushed entries, and 7 new VM tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(profile): enable « Derniers messages » -> author-only search route New SearchUserPostsRoute(pseudo) pushed onto the current tab's stack (separate route so the SearchRoute tab root stays a data object and the tab's idle state is untouched). ProfileFullContent enables the button with the loaded profile's canonical pseudo. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(search): cover the pseud wire contract + author-only fixtures (Codex review) Codex review of #403 (0 blocking) flagged three coverage gaps, all addressed : HfrClientTest author-filter encoding on the anonymous client (empty query), SearchResultParserTest on the existing pseud fixture + a NEW live-captured all-categories pivot fixture (the exact wire shape of the profile button, post-302), and DefaultSearchRepositoryTest forwarding + pseud redaction in rebranded IOExceptions. Also guards the profile button against ProfileParser's defensive "?" pseudo sentinel (minor finding). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat(messages): new-MP composer contract through network, domain and data layers HfrClient.getPrivateMessageComposePage GETs the standalone composer (message.php?cat=prive without post=, dest= prefillable server-side). PrivateMessageWriteRepository grows fetchComposeForm + submitNewMessage ; buildComposeFormBody overrides hash_check/verifrequet/content_form/dest/ sujet and forwards the composer's hidden routing verbatim (cat=prive, empty post/numrep, parents, stickold, MsgIcon, pseudo — never password). Contract captured live 2026-06-11 (fixtures mp_compose_form.html + dest-prefilled variant, scrubbed) ; the POST response was deliberately never exercised, so an unrecognised answer stays the non-destructive Unknown. 8 new tests (2 parser fixtures cases, 6 repository MockWebServer cases incl. dest/sujet override-once and blank-field short-circuits). Refs #301 (follow-up écriture : composition) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(messages): new-conversation composer screen + shared editor components PrivateMessageComposeScreen/ViewModel/UiState : recipients (dest, commas = MultiMP) + subject (70-char HFR cap, truncating) + the shared BBCode editor ; same #312 armed-confirmation and silent hash_check refetch as the reply editor, with a hydration guard so a refetch never clobbers an in-between recipients edit. The reply screen's chrome (header, IME-pinned submit bar, options sheet content, load/error states, banner mapping) moves to MessageEditorComponents (internal) consumed by both editors. MessagesScreen grows a « Nouveau » header button (shown once the inbox loaded) and a sentSignal hook that refreshes the list after a successful send. 10 new ViewModel tests. Refs #301 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(nav): PrivateMessageComposeRoute wired from the MP list Full-screen editor route (hides the navigation suite like the other composers). On SubmitSucceeded the host pops the composer and bumps privateMessageSentSignal — the created thread id is unknown (the bddpost success response of a new conversation is not topic-shaped), so the MP list re-fetches and shows the new conversation at the top. Refs #301 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(messages): address Codex review of #404 - sentSignal now lands on inbox page 1 (showFreshInbox) — a plain current-page refresh from page 2+ would never surface the created conversation ; soft refresh when already there. - New VM test : an unrecognised POST response emits NO navigation effect (the composer must not pop on an unproven outcome). - Composer-specific wording for the form error and the Unexpected banner (« vérifiez votre liste de messages privés »). - privateMessageSentSignal purged on auth transitions like the other private-message hints. - Reply screen : leftover imports from the chrome extraction removed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Merge par squash --admin (mono-maintainer, review Codex traitée dans la PR). closes-#386 réservé à la promotion dev vers main. Fix à confirmer au dogfooding device.
Merge par squash --admin (mono-maintainer, review Codex zéro finding dans la PR). closes-#393 réservé à la promotion dev vers main.
Merge par squash --admin (mono-maintainer, fichiers de notes Play uniquement).
…précédente en bas (#420) Squash de feat/quickwins-d1 : refs-#415 (palette 58 builtins + fixture smilies.php + test symétrie), refs-#416 (slot error SubcomposeAsyncImage → token tapé), refs-#418 (Supprimer déplacé dans PostMenuSheet, gates #292 conservées), refs-#412 (StartAtBottom via marqueur nav transitoire, durci post-review Codex 2f5faa1). CI verte, review Codex 0 bloquant. Merge --squash --admin : mono-maintainer, cf. convention repo. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… auto-refresh #378 (#421) Squash de feat/flags-pass : refs-#384 (type = bucket demandé, fixture preuve live flag_owntopic=3 dans participated), refs-#385 (reset scroll au flip du filtre, paire atomique post-review), refs-#417 (forceDarkAllowed=false), refs-#378 (auto-refresh on landing + pref opt-out + throttle 15s, durci post-review), aide refs-#331 (staleness). CI verte, review Codex 0 bloquant (2 IMPORTANT traités 1cdb5f9). Merge --squash --admin : mono-maintainer, cf. convention repo. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…275, #410) (#422) * fix(editor): keep the cursor visible under the IME in the three editors (#275, #410) The full-screen editors (post reply/edit, MP reply, MP compose) gave the draft field a bounded height (weight 1f) and let the TEXT scroll INSIDE it. Compose's keep-the-cursor-visible machinery lives in ANCESTOR scrollables - cursor bring-into-view requests (typing, tap-to-place, toolbar insertions) propagate to them and the scrollable re-anchors the focused area when its viewport shrinks under the IME - the internal text scroller has none of it. Net effect across keyboards (Gboard/SwiftKey/HeliBoard, #275): the IME compressed the field and the cursor line stayed hidden below the fold, both while typing (#275) and on refocus after the preview (#410). TopicFormScreen (outer-scroll layout) was already on the working pattern. BbcodeTextField gains a fillViewport mode: the field grows with its content (no internal scroll) inside its own scrollable column sized by the caller's bounded box, heightIn(min = viewport) preserving the v108 contract (the outlined area fills every free pixel, tap anywhere focuses). The three editors opt in; the contract forbids enabling it inside an outer scroll (nested unbounded scrollables). Robolectric tests pin the structure (field fills short, grows long, the wrapping column owns the scroll). The IME interaction itself cannot be exercised by Robolectric - device dogfooding on the next dev release. Refs #275, refs #410 (keywords broken on purpose: close at dev->main promotion). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(editor): address Codex review on the fillViewport contract (#275, #410) - KDoc: the field's internal scroller does take part in bring-into-view but does not re-anchor on IME shrink — drop the over-absolute claim - enforce the bounded-height contract with a require(maxHeight.isFinite) - refresh the three call-site comments that still described the old scroll-INSIDE-the-field layout Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
… refs-#280) (#423) * fix(parser): preserve deliberate blank lines and uniform line height A top-level <br> used to FLUSH the running paragraph, so every authored line became its own Paragraph block: the `<br><br>` HFR emits for an empty line collapsed into a dropped empty paragraph (refs 333) and the renderer's 8dp inter-block gap replaced the natural line height between every single line (refs 280). A top-level break is now an inline LineBreak inside the running paragraph (web parity: N consecutive breaks render N newlines), and flushParagraph trims breaks/blank fragments at the paragraph EDGES only — breaks adjacent to a block boundary (quote, image, end of post) duplicate the renderer's inter-block spacing, interior ones are the author's literal line structure. Regression tests anchored on the real fixture patterns: quote-header `</b><br /><br /><p>` (leading edge), end-of-quote `<br /><br /></p>` (trailing edge), single-br multi-line answer (topic_page_single). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(parser): address Codex review — stale br comment, exact AST assertion - parseInlineElement still said a top-level <br> "flushes the current paragraph" — the exact behaviour the fix removed; reworded - the synthetic regression test now asserts the exact inline AST (Text/LineBreak values), not just the node classes Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…5) (#425) The contextual menu (refs 362) shows avatar + pseudo but the identity was inert — tapping it now opens the profile sheet, parity with the post-card tap (refs 208). Whole hero row is the tap target (menu-row idiom), same profileId gate as the card, and the menu plays its hide animation before the profile sheet opens so the two never stack. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…383) (#424) * feat(topic): setting to hide the floating page-change FABs (refs 383) The bottom-of-topic previous/next mini-FABs (refs 283) duplicate the page swipe (refs 282) for readers who navigate by gesture — add an opt-out preference. The « Répondre » FAB keeps its own gates and stays. Same optimistic-flip + startup-race-guard machinery as the existing topic top-bar auto-hide toggle, mirrored into TopicUiState so a flip applies live without reopening the topic. Default true (historical behaviour). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(settings): address Codex review on the page-FABs toggle (refs 383) - DataStore round-trip test for the topic_page_fabs key (defaults true, persists false/true) — the fakes alone would miss a wrong key/default - hydration tests: a persisted false reaches state; a stale initial emission must not overwrite a local opt-out (TouchedLocally guard) - TopicPreferencesCard KDoc now documents both toggles Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
… page (refs-#379) (#426) * feat(topic): explicit end-of-topic marker on the last page (refs 379) The « page X/Y » counter (refs 284) lets the reader deduce they reached the last post; this says it: a sober centred label between two hairlines, rendered as the last list item of the topic's LAST page only. Reflects the loaded page, same contract as the counter. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(topic): drop the explicit key on the end-of-topic footer (refs 379) Codex review: a stable key makes Lazy track the sentinel across an insertion — a reader parked on the marker would keep it in view while a freshly fetched post lands above the viewport, unseen. Positional identity is correct for a stateless footer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat(topic,editor): multi-quote — quote several posts in one reply (refs 291) Client-side MVP, no new HFR contract: the #146 quote form fetch (message.php?numrep=N → prefilled [quotemsg]) is replayed once per selected post and the prefills are concatenated in SELECTION order; the submit rides the first form's hash_check (per-session, not per-post). - post menu: « Ajouter/Retirer de la citation multiple », same gate as « Citer » - floating cluster: « ❝N » FAB opens the editor with every selected quote; not governed by the #383 page-FABs preference (armed write affordance, not navigation) - basket hoisted to :app keyed (cat, post) — survives page changes, resets when selecting in another topic, cleared on editor launch - a failed extra fails the whole fetch (retryable) rather than silently dropping a quote the user selected Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(topic,editor): address Codex review on multi-quote (refs 291) - purge the multi-quote basket on auth transitions — a write intention armed under another session must not survive logout/login - a 200-OK form whose prefill comes back BLANK now fails the whole fetch instead of silently dropping a selected quote (+ test) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…refresh et ascenseur MP (refs-#351, tranche a) (#428) * feat(messages,ui): composants de lecture partagés topic-MP + pull-to-refresh et ascenseur MP (refs-#351) Tranche a de refs-#351 (ADR-013 : partager les composants, pas les écrans) : - LazyListScrollbar : TopicScrollbar déménagé verbatim vers :core:ui/list (composant générique LazyListState, géométrie #300 inchangée) - core.ui.pager.PageSwipe : fonctions pures du swipe #282 (seuils, drag-follow, edge-hint) + modifier pageSwipeEdgeHint partagés ; la machinerie route-driven (latch, slide-out) reste dans :feature:topic - MP thread : load() keep-content (page change / pull-to-refresh gardent la page affichée derrière isRefreshing, prérequis du swipe MP) ; échec keep-content = effect RefreshFailed (Toast) + page conservée ; PullToRefreshBox (parité #335) ; LazyListScrollbar en overlay (parité #300) ; scroll-to-top au changement de page rendue - tests : PageSwipeTest (35) + LazyListScrollbarTest (18) déplacés ; 4 nouveaux tests VM (keep-content gated, refresh in-place, échec keep-content, no-op) - docs : architecture.md (:core:ui list/ + pager/) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(messages): ne pas écraser la position restaurée au premier rendu Content (refs-#351) Finding IMPORTANT de la review Codex : le LaunchedEffect de scroll-to-top keyé sur la page rendue se déclenchait aussi au premier rendu d'une composition fraîche — une rotation/recréation avec contenu chargé restaurait la position de lecture (rememberLazyListState) puis la ramenait aussitôt en haut. Garde lastRenderedPage (null au premier rendu) extraite dans ScrollToTopOnPageChange (seuil detekt). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…#351, tranche b) (#429) * feat(messages): swipe horizontal de pages sur la conversation MP, in-place (refs-#351) Tranche b de refs-#351 : portage du swipe de pages (#282) à la vue MP, machinerie in-place dédiée (ADR-013) sur la géométrie partagée :core:ui : - threadPageSwipe : mêmes seuils/ressenti (drag-follow, overpull, mur aux bords, haptique arm/commit) ; commit -> selectPage() keep-content, la page reste lisible derrière l'indicateur pendant le round-trip ; pas de slide-out (la composition survit) ; gate sur isRefreshing lu au down, ré-armé à la fin du chargement (pas de latch par composition) - pointerInput(Unit) jamais re-keyé : page/total/gate/callback lus via lambdas adossées à rememberUpdatedState (rememberThreadSwipeModifier) - edge-glow partagé pageSwipeEdgeHint, accent désaturé identique au topic - le swipe s'applique à la LazyColumn ; l'ascenseur overlay reste fixe Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(messages): reset du drag-offset au changement de page + pager gated pendant le chargement (refs-#351) Findings IMPORTANT de la review Codex sur le swipe in-place : - dragOffset/Animatable survivent au changement de page (pas de destruction de composition, contrairement au topic route-driven) -> reset LaunchedEffect(renderedPage), la nouvelle page n'hérite plus d'un offset résiduel - boutons du pager désactivés pendant un chargement keep-content (même gate que le swipe : un re-tap ne faisait que superseder) - mineur : handlers remember{} sans clé + doc capture unique (pointerInput(Unit) garde sa première capture quoi qu'il arrive) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…uis la liste (refs-#378) (#431) * fix(flags): l'auto-refresh bypasse le throttle au retour d'un sujet ouvert depuis la liste (refs-#378) Retours dev v118 (Dintr-un lemn, bitubo) : l'auto-refresh est sauté quand on ressort d'un topic en moins de 15 s — pile le moment où l'état a changé (le sujet lu doit sortir d'une vue non-lus). Cause : le throttle s'arme à l'atterrissage et ne distingue pas un retour-de- lecture d'un aller-retour sans lecture. Fix : FlagsRoute marque l'ouverture d'un topic (onFlagOpened) ; le prochain maybeAutoRefresh bypasse le throttle et consomme le marqueur (un refresh manuel le consomme aussi — il capture le même état). Les allers-retours sans lecture (switch d'onglets) restent throttlés. 3 tests : bypass après ouverture, consommation par le refresh déclenché, consommation par un pull manuel. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(flags): snapshot de la génération topic-ouvert au call-time de maybeAutoRefresh (refs-#378) Finding IMPORTANT de la review Codex : une lecture armée PENDANT les suspensions pref/auth du refresh d'atterrissage était consommée par ce refresh (qui ne pouvait pas l'avoir capturée), perdant le bypass au vrai retour. Compteur de génération + snapshot au call-time (même idiome que le snapshot d'onglet PR #421) ; le pull manuel consomme les générations visibles à son propre call-time. Test de course ajouté (StandardTestDispatcher). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…i refs-#384) (#432) * fix(flags): la décoration favori redevient jaune dans « Mes sujets » (suivi refs-#384) Retour dev v118 (XaTriX, screens) : le « type = bucket » de refs-#384 avait raison pour le cache/routage mais la couleur de pastille dérive du type -> l'info « aussi favori » (flag_owntopic=3) a perdu son jaune dans l'onglet participated. - Flag.isFavorite (décoration, défaut false) <- flag_owntopic == 3, type-bucket inchangé (le « future étoile badge » anticipé par la KDoc du mapper) - FlagDot : le favori gagne sur la couleur du bucket (parité site) - Room : colonne flag_topics.isFavorite NOT NULL DEFAULT 0 (@ColumnInfo defaultValue alignée), migration 8->9 + schéma v9, MIGRATION_8_9 chaînée dans les 7 blocs production du MigrationTest - tests : fixture rest_cat13_participated_favorites (isFavorite vrai sur les 2 lignes owntopic=3, faux ailleurs) + migrate_8_to_9 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(flags,docs): specs alignées (type=bucket vs isFavorite) + FlagDot sur FlagPalette (refs-#384) Findings de la review Codex : - IMPORTANT : models.md (Flag sans isFavorite) et protocol-hfr.md (flag_owntopic décrit comme bucket) contredisaient le contrat réel — les deux specs canoniques documentent maintenant bucket-vs-décoration avec la preuve fixture, et le mapping direct conservé pour les TopicSummary hors drapeaux - MINEUR : FlagDot dupliquait les couleurs en littéraux divergents de FlagPalette (source des lignes topic du Forum) -> source unique, delta de teinte rouge/jaune subtil assumé Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…clavier (refs-#275, refs-#410) (#434) Retour dev v118 (XaTriX, screen) : sur « Nouveau message », clavier ouvert, destinataire+sujet+toolbar+barre Envoyer consomment l'écran et le champ message (weight 1 + fillViewport, design hérité de l'éditeur de réponse) est écrasé à ~0 sans scroll externe pour le ramener. Fix : ComposeEditorBody bascule sur la branche « éditeur à en-tête haut » du contrat BbcodeTextField, déjà utilisée par TopicFormScreen — body entier en verticalScroll + champ en mode défaut grow-with-content (bring-into-view et ré-ancrage IME via le scrollable ancêtre ; fillViewport interdit sous un scroll externe) + heightIn(min=160dp) pour une vraie zone de frappe à draft vide ; aperçu en bloc simple (plus de scroll imbriqué). Barre Envoyer épinglée inchangée ; l'éditeur de réponse MP garde fillViewport (pas d'en-tête, pas le bug). Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…eaux post-review (#435) * chore(privacy,docs): re-scrub fixture MP storage + cohérence doc drapeaux post-review Review complète de la branche dev (main...dev, 21 commits) — 2 findings ≥75 corrigés + 3 mineurs de cohérence : - fixtures mp_storage_search_hit.html (core:data + core:parser) : les value= des checkboxes d'effacement et les title="Sujet n°…" gardaient les vrais threadIds privés (renumérotés 9000001..9000003 comme les href), et le title « n'a pas été lu par » exposait la liste réelle des lecteurs d'une conversation (remplacée par des pseudos synthétiques). Journaux source.txt mis à jour. Aucun consommateur : le parser ne lit que href post= (tests inchangés, assertions 9000003 intactes). - docs/specs/architecture.md : signature RestFlagMappers.toFlags(envelope, defaultType, …) périmée → (envelope, type, …) + rappel bucket/isFavorite. - Flag.kt : KDoc des membres FlagType contredisait le KDoc de classe (bucket ≠ flag_owntopic, #384) → reformulé bucket participated/read/favorites. - HfrClient.deleteFlag : « same mapping as the REST flag_owntopic » trompeur depuis la vérification live → précisé sélecteur WRITE-only. - Réglages : entrées « à venir » caduques retirées (scroll par page livré always-on refs-#307 ; composer un MP livré refs-#301), strings purgées. Validation locale 2 parts : detektAll + test + :app:assembleProdDebug puis :app:lintProdDebug — vertes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(review): toOwntopic = sélecteur delflag-only, removeFlag, date du re-scrub Review Codex de la PR #435 : 1 IMPORTANT (la KDoc de toOwntopic() disait encore delflag/addflag + REST flag_owntopic — addflag ignore owntopic et le champ REST décrit le drapeau le plus fort, pas le bucket) + 2 mineurs (HfrClient.deleteFlag → removeFlag dans la KDoc de FlagType ; journal re-scrub daté 2026-06-12 → 2026-06-11). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ge (refs-#313) (#439) Validation Docker 2 parts + émulateur (badge « 3 » cohérent liste). Review Codex : 1 bloquant (piggyback non scellé à la session) corrigé — pseudo snapshotté au call-time + filtre à la collecte. Merge --admin mono-maintainer documenté. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… promu :core:ui (refs-#387) (#440) Validation Docker 2 parts + émulateur (insertion + recherche wiki). Review Codex : 2 importants (userId du form transmis à la recherche wiki ; aucun travail réseau picker fermé) corrigés. Follow-up migration :feature:editor = #441. Merge --admin mono-maintainer documenté. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…u canal dev (#444) Préparation cut bêta 0.10.0 (guard versionName vs bêta 0.9.0/v113) + builds dev distinguables (vérifié aapt2 : 0.10.0-dev.local). Merge --admin mono-maintainer documenté. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…gée d'onglets (#446) Diagnostic uiautomator (tabs 259 px, padding slot text 16 dp → 175 px utiles = largeur exacte du label). Surcharge content de Tab, 8 dp + maxLines=1 + Ellipsis, couleurs explicites. Vérifié émulateur (nominal + ellipse « +lus »). Merge --admin mono-maintainer documenté. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
) Audit adversarial par agent indépendant (Codex indisponible — 402 deactivated_workspace) : 4 bloquants ADR-013 + 5 importants ADR-014, tous corrigés avant acceptation. ADR-013 (lecture MP) — Accepté 2026-06-12 : - état d'implémentation : décision 1 LIVRÉE (PR #428/#429, noms réels PageSwipe/LazyListScrollbar), décisions 2-3 à implémenter (#430/#6) - prefetch borné N−1/N+1, définition « ouverte », suspension après marquage non-lu manuel ; purge étage 1 tranchée - conséquence Konsist : étendre la garde au domaine MP, pas exempter ADR-014 (MPStorage v0.1) — Accepté 2026-06-12 : - état d'implémentation : lecture LIVRÉE (PR #406), écriture/cache ids à venir - premier-hit documenté (parseFirstThreadId, à re-trancher avant écriture), paramètres réels de la requête de découverte documentés - trous de vérification complétés : storage réel jamais observé, risque faux NotFound → doublon à l'écriture Pages canoniques actées : - architecture.md : politique cache MP 3 étages + exception prefetch MP bornée (notes « Proposition en cours » remplacées) - protocol-hfr.md : exception prefetch actée + contrat nonlu.php vérifié live (#361) - models.md § MPStorage : référence ADR-014 - adr/README.md : retrait des mentions (Proposé) Demandé par @xatrix Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
#449) * fix(editor): le champ suit le curseur pendant la frappe (refs-#447 point 1) Le champ en scroll externe (#422/#434) ne demandait jamais au scrollable ancêtre de suivre le caret en frappe — Compose ne câble ce suivi que quand le TextField possède son propre scroll interne. Retour bêta-dev v123 de Dintr-un lemn (post #2787456). - BbcodeFieldImpl réécrit : BasicTextField (overload TextFieldValue, qui expose onTextLayout — M3 OutlinedTextField ne l'expose pas) + OutlinedTextFieldDefaults.DecorationBox pour la parité visuelle (label flottant, bordure focus, couleurs M3). - BringIntoViewRequester attaché au nœud texte INTERNE (le caret rect de TextLayoutResult.getCursorRect est dans son espace de coordonnées, pas celui de la boîte décorée), déclenché sur (focus, sélection, layout) — jamais sur value.text seul (layout périmé). - Parité M3 répliquée : semantics(mergeDescendants) + padding(top=8.dp) (réserve la moitié haute du label flottant — vérifié rogné sans), textStyle onSurface, caret primary, capitalisation Sentences. - Test Robolectric : sélection déplacée en fin de contenu long → le viewport scrolle (>0) pour révéler le caret. Vérifié sur émulateur : 30 lignes tapées, le viewport suit et le caret reste visible au-dessus de l'IME ; label flottant entier. Le point 2 de #447 (drag-to-scroll de sélection) reste ouvert — structurellement plus coûteux, traité séparément. Demandé par @xatrix Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(editor): durcir la preuve du suivi de caret (review Codex) 2 IMPORTANT + 1 MINOR de la review Codex sur #449 : - L'assertion « scrollAfter > scrollBefore » prouvait un scroll, pas la révélation du caret : un requester accroché au mauvais ancêtre (ou un rect lu dans l'espace de la boîte décorée) sous-scrollerait d'un offset constant et passait quand même. Caret en DERNIÈRE ligne => assert scrollAfter >= 95% du maxValue du range. - Le mode défaut (fillViewport=false dans un verticalScroll externe, layout TopicFormScreen) n'était pas couvert : 2e test avec Column scrollable externe taguée, même contrat. - Commentaires resserrés : le wrapper Box du requester est offset-free PAR CONTRAT (son origine coïncide avec celle du layout texte). 5/5 tests BbcodeTextFieldViewportTest verts + detektAll. Demandé par @xatrix Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ay (#450) - Entrée re-numérotée v124 → v126 (ledger 125+1 : les builds dev v124 et v125 sont passés depuis), plage dogfood v114 → v125. - Ajout du dernier round : suivi du curseur en frappe (#447 point 1) et ellipse de l'onglet « Mes sujets » (#446). - Notes Play fr/en : mention du fix curseur (474/403 octets, < 500). Demandé par @xatrix Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Contributor
Author
Dernier tour pré-merge (multi-agents + Codex)Verdict : SHIP — 0 bloquant sur 33 commits. 5 agents de review (bugs/intégration cross-PR, historique git, promesses des PRs passées, contrats des commentaires de code, conformité AGENTS.md) :
Review Codex (rapport complet) : 0 BLOCKER, 2 IMPORTANT sur le badge MP, tracés en issues de suivi :
Métadonnées release cohérentes : versionName 0.10.0, changelog v126, whatsnew < 500 octets. CI verte (6m45). |
This was referenced Jun 12, 2026
La promotion 0.9.0 (#400) a laissé sur main des commits de bump/changelog (11935ae, e8cc413) reportés textuellement sur dev par #401 : les deux branches divergeaient sur app/CHANGELOG.md et app/build.gradle.kts, et la promotion 0.10.0 (#451) ne pouvait pas créer son merge commit. Résolution : contenu dev conservé tel quel pour les deux fichiers — la version dev est strictement plus récente (v113 statut « open » + sha de promotion via #401, correction « 7 PR » d'e8cc413e incluse, entrée v126, versionName 0.10.0 + stamp -dev). L'arbre résultant est identique à dev. Avec main contenu dans dev, #451 et les promotions futures mergent proprement. Demandé par @xatrix Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
chore(sync): merge main into dev — réconcilier l'historique 0.9.0
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Promotion du canal dev vers main pour le cut bêta 0.10.0 (build anticipé v126). 33 commits depuis la 0.9.0/v113, dogfoodés en continu sur le canal dev (v114 → v125), chaque PR mergée avec CI verte + review (Codex ou multi-agents).
Contenu (depuis 0.9.0)
Messages privés : écriture complète (nouveau MP), picker de smileys dans les éditeurs MP, badge de non-lus sur l'onglet, swipe de pages in-place, pull-to-refresh, ascenseur, MPStorage lecture seule v0.1 (ADR-014).
Écriture : citation multiple + marquage visuel des posts ajoutés, l'éditeur suit le curseur pendant la frappe.
Recherche : filtre par auteur, repli du formulaire en bandeau compact.
Lecture : marqueur de fin de sujet, réglage des FAB de page, profil via le menu de post.
Fixes : IME/clavier (×3), parser (lignes vides, spoilers, smileys inconnus), drapeaux (×6), thème cold-start, onglet « Mes sujets » sans wrap.
Docs : ADR-013 + ADR-014 acceptées, pages canoniques actées (contrat nonlu.php).
Release : versionName 0.10.0, stamp
-dev.<build>sur le canal dev, changelog v126 + notes Play.Issues livrées
Closes #301, closes #387, closes #313, closes #291, closes #433, closes #379, closes #383, closes #395, closes #418, closes #415, closes #275, closes #410, closes #333, closes #280, closes #393, closes #416, closes #384, closes #378, closes #385, closes #412
Restent volontairement ouvertes : #6 (umbrella MPStorage), #351 (tranche c différée post-bêta), #436 (« Tout vider » non codé), #447 (point 2 drag-sélection), #430, #441, #442, #445 ; #331 et #417 (fixes livrés mais les PRs #421/#434 promettaient une confirmation device avant fermeture — on ferme dès confirmation en bêta) ; #452/#453 (findings Codex du dernier tour sur le badge MP, suivis post-bêta).
Procédure
Merge commit no-ff (pas de squash : on préserve l'historique des PRs dev), puis
gh workflow run release.yml --ref main -f channel=beta. Guard CI versionName : 0.10.0 vs 0.9.0 (dernière release « β ») — passe.🤖 Generated with Claude Code