Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
355 changes: 355 additions & 0 deletions docs/roadmap-v0.4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,355 @@
# v0.4.x Roadmap — Plan de bataille

> Statut : **proposed**, rédigé 2026-05-11 après research compétitive + audit codebase.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick (typo): Clarifier la formulation de « research compétitive » pour éviter que cela ne ressemble à une faute de frappe.

Comme le reste de la phrase est en français, envisagez d’utiliser « recherche compétitive » afin de garder une langue cohérente et d’éviter que cela ne semble être une insertion accidentelle d’anglais.

Suggested change
> Statut : **proposed**, rédigé 2026-05-11 après research compétitive + audit codebase.
> Statut : **proposed**, rédigé 2026-05-11 après recherche compétitive + audit codebase.
Original comment in English

nitpick (typo): Clarify phrasing of "research compétitive" to avoid looking like a typo.

Since the rest of the phrase is in French, consider using "recherche compétitive" to keep the language consistent and avoid this looking like an accidental English insertion.

Suggested change
> Statut : **proposed**, rédigé 2026-05-11 après research compétitive + audit codebase.
> Statut : **proposed**, rédigé 2026-05-11 après recherche compétitive + audit codebase.

> Auteur : @cc-gpc. À ratifier par @thierry.

---

## TL;DR

v0.3.x a posé les fondations (génération AI, multi-network composer, BYOK,
calendrier visuel, recovery hardening). v0.4.x ferme le gap fonctionnel sur
la concurrence (Buffer, Later, Hypefury) sans casser le moat local-first /
BYOK. Quatre features dans l'ordre — auto-publish, analytics fetch, optimal
posting time, content repurposing — plus un saupoudrage UX. 4 releases
mineures.

## Vision

Transformer Getpostcraft d'un **« composer puis publier manuellement »** en
un **assistant éditorial complet** qui :

1. **Génère** — déjà acquis (v0.3.x)
2. **Programme et publie automatiquement** — gap n°1, ferme v0.4.0
3. **Mesure ce qui a marché** — analytics post-publish, v0.4.1
4. **Suggère quand publier** — data-driven optimal time, v0.4.2
5. **Recycle le contenu cross-format** — one-click repurposing, v0.4.3

## Différenciation maintenue

Buffer / Later / Hypefury 2026 sont cloud + abonnement $15-99/mois. Tous ont
auto-publish, analytics, optimal posting time, repurposing.

Getpostcraft v0.4.x reste **local-first + BYOK** (~$0.15/mois pour un usage
typique sur Sonnet 4.6). La parité fonctionnelle ferme le gap « il manque
trop de features » tout en gardant le moat sur prix + privacy +
data-ownership.

**Aucune feature v0.4.x ne demande un backend cloud.** Tout reste pilotable
depuis la machine de l'utilisateur, en arrière-plan, avec ses propres clés
API.

---

## Top features priorisées par ROI

### #1 — Auto-publish scheduling (v0.4.0) — LE big move

**Pourquoi maintenant :** le calendrier visuel existant montre les posts
planifiés mais ne fait rien à l'heure dite. L'utilisateur doit revenir
cliquer « Publier maintenant ». C'est la friction n°1.

**Impact mesurable :**
- LinkedIn 2026 — « Golden hour » : les 60-90 premières minutes après
publication décident de la portée. Publier précisément à l'heure
optimale (mardi 19h00) vs « quand j'ai le temps » = 2-5× portée
attendue (source : recherche viral 2026 dans mémoire projet).
- Instagram 2026 — même logique sur engagement initial.

**Faisabilité technique** : moyenne
- Background task `tokio::spawn` au démarrage de l'app
- Poll DB toutes les 60 s : `SELECT * FROM post_history WHERE
scheduled_at <= datetime('now') AND status = 'draft'`
- Pour chaque post dû → appelle l'existant `publish_post` /
`publish_linkedin_post`
- Retry policy : 3 tentatives avec backoff exponentiel (5 min / 30 min / 2 h)
- Check `token_expires_at` (déjà capturé via migration 014) avant publish
pour éviter un 401 silencieux
- Migration 019 : ajouter `status='publishing'` + `failed_attempts INTEGER
DEFAULT 0` + `last_attempt_at TEXT` sur `post_history`
- Surface UI : badge « Programmé pour J 19h00 » + ligne « Échec tentative
1/3 à T+5min, prochain essai à T+30min » + notification système
(tauri-plugin-notification) sur succès/échec final

**Edge cases à couvrir :**
- App fermée à l'heure prévue → publish au prochain launch dans une
fenêtre ±15 min, ignore-si-trop-vieux (>24h sans publication =
considère perdu, demande à l'utilisateur)
- Multiple posts dus en même temps → publication séquentielle, pas
parallèle (évite rate-limit Meta/LinkedIn)
- Token expiré → status `failed` immédiat avec message « reconnecte ton
compte dans Paramètres → Comptes »
- User publish manuellement avant l'heure → l'auto-publish voit `status
= 'published'`, no-op

**Estimation découpée :**
- PR 1 : foundation scheduler module + migration 019 + types + tests
unitaires (background task non encore branché)
- PR 2 : intégration dans `init_pool`/`run()`, retry policy, token check,
notifications
- PR 3 : UI dashboard badge + history of attempts panel + Composer
intégration

**Différenciateur** : aucun (parité avec Buffer/Later). Mais sans ça,
toute autre feature est cosmétique — c'est la **table stake**.

### #2 — Post-publish analytics fetch (v0.4.1)

**Pourquoi maintenant :** l'utilisateur poste mais ne sait pas si ça
marche. Sans feedback, impossible d'itérer la stratégie. Tous les
concurrents le font.

**Faisabilité :** moyen-haut, dépend de l'accès aux API engagement.

**APIs :**
- **Instagram Graph API** : `GET /{media_id}/insights` →
`metric=reach,impressions,saved,likes,comments,profile_visits,shares`.
Disponible sur Business + Creator accounts. Limite : 90 jours
d'historique.
- **LinkedIn API** : `GET /socialActions/{urn}` →
`numLikes, numComments, numShares`. Pas de reach/impressions sur les
posts perso (uniquement Pages).

**Stockage :**
- Migration 020 : `CREATE TABLE post_analytics (post_id INTEGER, fetched_at
TEXT, metrics TEXT)` (metrics = JSON pour rester forward-compat).
- Pas une table 1:1 sur `post_history.id` parce qu'on veut historiser
l'évolution des métriques au fil du temps (engagement croît sur 48 h).

**Logique fetch :**
- Posts < 7 jours → fetch chaque heure (capture la croissance virale
initiale).
- Posts 7-30 jours → fetch chaque jour.
- Posts > 30 jours → fetch 1× par semaine.
- Background task qui pilote l'orchestre — mêmes contraintes que
l'auto-publish (sérialisation, rate-limit awareness).

**UI :**
- Sur le détail d'un post : 3 chiffres clés (likes / comments / saves)
+ petite courbe d'évolution si > 1 fetch
- Dashboard : nouvelle section « Top posts dernières 30 jours par
engagement » — actionable pour décider quoi repurposer

**Hashtags 2026 Instagram** : 10 hashtags, 3 larges + 5 niche + 2
communauté (memory `reference_viral_posts_research.md`). L'analytics
permet de mesurer **lesquels** marchent → feedback loop sur la stratégie
hashtag.

**Estimation découpée :**
- PR 1 : migration 020 + module `db::analytics` + Tauri command fetch IG
- PR 2 : Tauri command fetch LinkedIn (différent endpoint)
- PR 3 : UI post detail + dashboard top-posts

### #3 — Optimal posting time AI (v0.4.2)

**Bloqué par :** analytics doit avoir accumulé ≥ 30 jours de data sur le
compte de l'utilisateur. Donc ship après v0.4.1.

**Algorithme :**
- Groupe les engagements (likes+comments+saves pondérés) par heure-de-la-
journée × jour-de-la-semaine.
- Lisse avec moyenne mobile (atténue les outliers viraux).
- Retient le top 3 créneaux.
- Affichage Composer : « Ton meilleur créneau IG = mardi 19h00 (+38%
d'engagement vs moyenne) ».
- Affichage Calendar : highlight le créneau optimal sur la grille
hebdomadaire.

**Pas de cloud, pas d'API externe.** Calcul 100 % en SQLite + Rust à
partir des `post_analytics` locaux. Aucun risque GDPR.

**Estimation :** 1 PR mineure (modules + commands + UI).

### #4 — Content repurposing one-click (v0.4.3)

**Cas d'usage typique :** un post LinkedIn 1800 chars qui marche bien →
« Repurpose pour Instagram en carrousel 5 slides ». L'AI extrait la
structure, condense par slide, optimise les hooks. Sens inverse aussi
(carrousel IG → LinkedIn long-form prose).

**Sidecar nouveau prompt :** dédié, prend en input le post source + le
format cible + le réseau cible. Output = JSON formaté pour le format cible.

**UI :** bouton « Repurposer pour... » sur ContentPreview, menu dropdown
des formats accessibles (filtré selon le réseau source + cible).

**Aligné avec memory** : règles cross-platform (LinkedIn pas de liens
corps, Instagram CTA save/DM). L'AI applique les règles correspondant au
réseau cible.

**Estimation :** 1 PR.

### #5 — Responsive design 2026 (concern horizontal v0.4.x)

Pas une feature isolée mais un **standard de qualité** à appliquer à
toutes les UIs livrées en v0.4.x. Sur une app desktop Tauri, "responsive"
veut dire **adapter au redimensionnement de fenêtre** (de 600 px à
1920+ px), pas mobile au sens web.

Standards 2026 à appliquer systématiquement aux nouvelles surfaces UI
(dashboard scheduler, analytics views, repurpose modal, etc.) :

- **Container queries** au lieu de media queries pour les composants
(`@container (min-width: 480px)` plutôt que `@media`). Le composant
s'adapte à sa propre largeur, pas à la viewport — crucial quand le
composer brief-pane et preview-pane changent de ratio selon l'état du
groupe.
- **Fluid typography** avec `clamp(min, ideal-vw, max)` pour les
captions affichées et les chiffres analytics. Plus de breakpoint
cliff sur les chiffres "$0.0028" qui doivent rester lisibles entre
16 px et 24 px selon la place dispo.
- **Intrinsic layouts** : `grid-template-columns: repeat(auto-fit,
minmax(280px, 1fr))` pour la grille calendar, les tiles
GroupResultPanel, les cards analytics top-posts. Pas de calcul
manuel de breakpoint.
- **Dark mode contrast** : tester chaque nouveau composant en dark
(default Getpostcraft) avec un audit de contraste rapide. Objectif
WCAG AA pour le texte (4.5:1).
- **Image formats** : les data URLs base64 PNG actuelles sont OK pour
la transmission IPC mais l'AVIF pour les thumbnails dashboard
réduirait la taille de ~50 %. Backlog v0.5 si vraiment besoin.

**Checklist pré-merge** à appliquer à chaque PR UI v0.4.x :
1. Window à 600 × 800 (étroite, comme l'usage @thierry souvent en
parallèle d'autres apps) — layout reste utilisable.
2. Window à 1920 × 1080 — pas d'éléments qui s'étirent grotesquement
ni de typo qui flotte ridicule.
3. Container queries utilisées plutôt que media queries pour les
composants nouveaux.
4. `clamp()` sur les valeurs typographiques clés.

### #6 — UX polish backlog issue #62 (saupoudrage)

10 items collectés au smoke test v0.3.9. Reproduits ici avec triage
impact/difficulté :

| # | Item | Impact | Diff | Bundle |
|---|---|---|---|---|
| 1 | Tooltip explicite sur bouton ×3 | 🟢 | trivial | v0.4.0 |
| 2 | Loading spinner full-page pendant gen | 🟡 | facile | v0.4.0 |
| 3 | Modal "voir réponse complète" sur JSON parse fail | 🟢 | moyen | v0.4.0 |
| 4 | Auto-détecter URL dans brief → propose mode URL | 🟢 | facile | v0.4.1 |
| 5 | Cost banner breakdown par réseau | 🟡 | facile | v0.4.1 |
| 6 | Navigation clavier entre tabs GroupResultPanel | 🟡 | facile | v0.4.1 |
| 7 | Empty state "Connecter @network" sur réseau coché sans compte | 🟢 | moyen | v0.4.1 |
| 8 | Bouton Cancel pendant génération en cours | 🟢 | moyen-dur | v0.4.3 |
| 9 | Restaurer la sélection multi-network du dernier composer | 🟡 | facile | v0.4.3 |
| 10 | Indicator "sidecar prêt" pour le 1er prompt | 🟡 | facile | v0.4.3 |

Tri par impact : 1, 3, 4, 7, 8 = **🟢 haute valeur** (résolvent friction
réelle observée). 2, 5, 6, 9, 10 = **🟡 nice-to-have**.

---

## Test coverage strategy (parallel work)

État au 2026-05-11 : Rust **226/226**, Frontend 105/105, Python sidecar
60/60. Gaps audit complétés :

| Module | Pub fns | Stratégie | Bundle |
|---|---|---|---|
| `commands/calendar.rs` | 5 | Tests intégration command-level (mock state) | Au prochain touch v0.4.x |
| `commands/settings.rs` | 6 | Round-trip via fake keychain (`keyring` mock) | v0.4.0 (la feature touch settings_db pour scheduler config) |
| `adapters/instagram.rs` | 4 | Wiremock pattern (déjà existant pour `publisher.rs`) | v0.4.1 (analytics fetch reuse l'adapter) |
| `commands/logs.rs` | 2 | Tests trivial — read tempfile content | PR "test sweep" v0.4.0 |
| `commands/python_deps.rs` | 2 | Integration only (process spawn) — hors scope unit | Backlog v0.5 quand on a CI Python |

**Cible 240+ tests Rust à fin v0.4.0.**

---

## Sequencing des PRs

```
v0.4.0 ─── F1 scheduler foundation (migration 019 + module + types + tests)
├─ F2 scheduler integration (background task + retry + notif)
├─ F3 scheduler UI (dashboard badge + history + Composer)
├─ F4 test sweep (logs + settings + 1-2 calendar tests)
└─ F5 UX polish (items 1, 2, 3)

v0.4.1 ─── F6 analytics foundation (migration 020 + db::analytics)
├─ F7 analytics IG fetch (Meta Graph insights)
├─ F8 analytics LinkedIn fetch
├─ F9 analytics UI (post detail + dashboard top)
├─ F10 instagram adapter tests (wiremock)
└─ F11 UX polish (items 4, 5, 6, 7)

v0.4.2 ─── F12 optimal posting time (data-driven from analytics)

v0.4.3 ─── F13 content repurposing one-click
└─ F14 UX polish (items 8, 9, 10)
```

Estimation calendaire (pace soutenu d'1 PR/jour comme la stack v0.3.9-10) :
**~3 semaines réelles** sur v0.4.x complète, avec validation utilisateur
intercalée.

---

## Success criteria

### v0.4.0
- [ ] Schedule un post à T+5min via Calendar → publié auto à l'heure ±60s
- [ ] Schedule fail (token expired) → 3 retries avec backoff → notification
système avec "Reconnecte ton compte"
- [ ] Schedule miss (app fermée à l'heure prévue) → publication au launch
suivant si <15min de retard, sinon prompt utilisateur
- [ ] Test coverage : Rust **240+** (+14 vs 226)
- [ ] Smoke test desktop : publier 2 posts programmés à 5min d'intervalle
sans intervention manuelle

### v0.4.1
- [ ] Sur un post publié il y a >1h, voir likes/comments fetchés et
affichés dans le détail
- [ ] Dashboard "Top posts 30j par engagement" pondère likes/comments/saves
selon les règles 2026 (saves > comments > likes pour IG)
- [ ] Test coverage : Rust **260+** (+20 vs v0.4.0)

### v0.4.2
- [ ] Composer affiche un badge "Meilleur créneau IG mardi 19h00" basé
sur 30j+ d'analytics
- [ ] Si <30j de data → affiche "Pas encore assez de données — publie 5+
posts pour activer"

### v0.4.3
- [ ] Bouton "Repurposer pour..." sur ContentPreview ouvre menu →
sélection format → AI regénère → nouveau draft créé
- [ ] Préserve le lien source (`group_id` chaîne au repurposé)

---

## Hors scope v0.4.x — backlog v0.5+

- Multi-compte sur même réseau (ex: deux comptes LinkedIn)
- Stories / Reels publishing (nouvelle API IG nécessaire)
- Niveau C stat-tiles carousel (différé par décision Thierry, brief
required)
- Multi-language UI (FR-only confirmé)
- Cloud sync optionnel pour multi-machine (contradiction avec local-first
philosophy)
- Team / multi-user (out of philosophy)

---

## Sources externes (research mai 2026)

- [Buffer 2026 review](https://socialrails.com/blog/buffer-review)
- [Hypefury 2026 features](https://creatortrail.com/hypefury-review-2026/)
- [Best AI social media tools 2026](https://zapier.com/blog/best-ai-social-media-management/)
- [Tauri scheduling Tokio cron patterns](https://github.com/mvniekerk/tokio-cron-scheduler)
- [Tokio cron scheduler](https://lib.rs/crates/tokio-cron-scheduler)
- [Container queries 2026 guide (DEV)](https://dev.to/nickbenksim/the-ultimate-guide-to-css-container-queries-in-2026-1ndi)
- [Responsive Web Design 2026 (UXPin)](https://www.uxpin.com/studio/blog/best-practices-examples-of-excellent-responsive-design/)
- [Responsive Web Design 2026 — Beyond Media Queries (Open Door)](https://opendoordigital.dev/blog/responsive-web-design-2026)

## Sources internes (memory + repo)

- `claude-config/memory/reference_viral_posts_research.md` — règles algo
Instagram + LinkedIn 2026 (DM shares > saves > comments > likes ; golden
hour LinkedIn ; longueur caption optimal par réseau)
Comment on lines +306 to +307

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (typo): Corriger l’accord dans « longueur caption optimal par réseau ».

Vous pouvez remplacer par « longueur de caption optimale par réseau » afin que « optimale » s’accorde avec « longueur » et que le « de » manquant soit ajouté.

Suggested change
Instagram + LinkedIn 2026 (DM shares > saves > comments > likes ; golden
hour LinkedIn ; longueur caption optimal par réseau)
Instagram + LinkedIn 2026 (DM shares > saves > comments > likes ; golden
hour LinkedIn ; longueur de caption optimale par réseau)
Original comment in English

suggestion (typo): Correct agreement in "longueur caption optimal par réseau".

Consider changing this to “longueur de caption optimale par réseau” so that “optimale” matches “longueur” and the missing “de” is added.

Suggested change
Instagram + LinkedIn 2026 (DM shares > saves > comments > likes ; golden
hour LinkedIn ; longueur caption optimal par réseau)
Instagram + LinkedIn 2026 (DM shares > saves > comments > likes ; golden
hour LinkedIn ; longueur de caption optimale par réseau)

- `claude-config/memory/feedback_technical_ownership.md` — délégation
technique totale (informe la pace d'exécution et l'autonomie sur les
PRs)
- `docs/guides/recovery.md` — patterns de fragilité DB déjà adressés
(heal + STARTUP_BLOCKED)
- `CHANGELOG.md` — état des features livrées jusqu'à v0.3.10
Loading