Skip to content

Changelog — Documentation

Log reverse-chronologique des changements apportés au doc set PortfolioAI. Maintenu en fin de chaque session /doc-maintainer (cf. .claude/skills/doc-maintainer/SKILL.md) par le main thread, après application des patches.

Format inspiré de Keep a Changelog en version allégée : - une section par date (## YYYY-MM-DD) - bullets groupés par area (metier/, technique/, projet/, racine) - une ligne narrative par changement, mentionnant le fichier concerné

Le .claude/ (CLAUDE.md, skills, agents) y figure aussi quand il est touché — c'est de la doc-adjacent au sens où il définit les conventions et l'outillage de la session.

Le contenu d'un fichier ne reflète que son état actuel. Ce CHANGELOG est l'unique trace de comment on y est arrivés (ordre, motivation, version qui a sauté…). Quand un finding paraît bizarre dans une doc, regarde ici avant de la patcher — il y a peut-être une raison récente.


2026-05-18 (Phase 5a wrap — brand mark, release workflow, backup pipeline, audit doc)

Sessions enchaînées dans la journée post-1er-deploy : (1) refonte identité visuelle frontend (brand mark + favicon + landing), (2) câblage workflow GitHub Releases → Cloud Run avec rituel documenté, (3) câblage workflow backup Postgres weekly vers Cloudflare R2 + restore drill documenté, (4) passe doc-maintainer pour aligner le doc set sur ces 3 livrables structurels. Smoke v0.7.0-rc1 vert en CI end-to-end. Coût Phase 5a toujours $0/mo confirmé.

frontend/

  • public/favicon.svg + public/img/logo/logo.svg : nouveau brand mark « Reader » (un P stylisé contenant 3 lignes de paragraphe → métaphore directe du positionnement « the LLM is a writer, not a decider »). Tout vectoriel, deux couleurs paramétriques --logo-tile + --logo-mark définies dans styles.scss sur :root (dark default = tuile blanche + tracé sombre, pop sur toolbar Material primary violet) et [data-theme='light'] (inversées). Le SVG consomme les vars via fill="var(...)" + stroke="var(...)" avec fallback inline. Favicon indépendant adaptatif via prefers-color-scheme (OS-level, pas app-level — un favicon suit le tab du navigateur). Plus de PNG : tout vectoriel, fini les renders multi-tailles.
  • src/app/app.config.ts : nouveau provideAppInitializer qui registre 'portfolioai' dans MatIconRegistry.addSvgIcon('portfolioai', sanitizer.bypassSecurityTrustResourceUrl('img/logo/logo.svg')) au boot. Consommé partout via <mat-icon svgIcon="portfolioai"> — toolbar App et landing /login, zéro duplication, zéro SVG inline. Comme MatIconRegistry inline le SVG dans le DOM, les CSS variables du parent propagent → le toggle de thème via ThemeService se reflète instantanément sans rebuild.
  • src/app/app.{html,scss} : <mat-icon>trending_up</mat-icon> du toolbar remplacé par <mat-icon svgIcon="portfolioai" class="brand-mark">. Wordmark texte <span class="brand-name">PortfolioAI</span> retiré (logo-only design). .toolbar-brand épuré (gap retiré, font-weight, etc.). .brand-mark à 1.6 rem (le P n'occupe que ~60 % de son viewBox vs ~85 % pour un glyph mat-icon, rattrapage visuel).
  • src/app/features/login/login-page.{html,scss,spec.ts} : refonte de /login en landing minimaliste — hero (logo + h1 + tagline « L'analyse de marché qui se lit comme une histoire, pas comme un signal d'achat ») + 3 feature cards (query_stats Indicateurs calculés / auto_stories Narration IA / account_balance_wallet Portefeuille connecté) + CTA Google + disclaimer. Plus de card framing — fond plein viewport var(--color-bg) max-width 760 px, grid 3 cols qui stack sous 720 px. Nouvelles clés i18n FR+EN sous auth.login.tagline + auth.login.features.{indicators,narrative,portfolio}.{title,description}, ancien auth.login.subtitle retiré. Test login-page.spec.ts adapté au nouveau set de keys (vérif présence dans le rendu).
  • src/app/app.routes.ts : ajout du wildcard { path: '**', redirectTo: 'dashboard' } en fin de routes. Les autres redirections ('' → dashboard, /settings → /settings/configuration) sont déjà présentes. Tickets Revoir les redirections Angular Phase 5 fermé.
  • src/app/app.spec.ts : test should render the toolbar brand mark on a non-login route aligné sur la nouvelle structure logo-only (.brand-mark mat-icon présent, plus le texte .brand-name).
  • public/i18n/{fr,en}.json : auth.login.subtitle retiré, auth.login.tagline + bloc auth.login.features.{indicators,narrative,portfolio} ajouté en FR + EN.
  • src/styles.scss : nouvelles CSS variables --logo-tile / --logo-mark définies dans les deux blocs de thème.
  • src/index.html : <title>Frontend</title> boilerplate Angular CLI → <title>PortfolioAI</title>. Lien favicon image/svg+xml pointant sur favicon.svg. Pas de title: par route — Angular Router ne touche document.title que si déclaré, donc le tag HTML suffit comme défaut.

.github/workflows/

  • deploy.yml : nouveau workflow Cloud Run, ~95 lignes. Trigger on: release: published (pre-releases -rcN inclus), concurrency: deploy-production cancel-in-progress: false, environment: production (required reviewer self-approve), permissions: { contents: read, id-token: write }. Pipeline : actions/checkout@v4docker/setup-buildx-action@v3google-github-actions/auth@v2 via WIF → setup-gcloud@v2gcloud auth configure-docker northamerica-northeast1-docker.pkg.devdocker/build-push-action@v6 platforms: linux/amd64 (natif amd64 ubuntu-latest, pas de QEMU émulation, gain ~5-10×) + labels OCI org.opencontainers.image.{source,revision,version}gcloud run deploy portfolioai --service-account=portfolioai-runtime@... --update-secrets avec 4 secrets mountés depuis Secret Manager + --set-env-vars SPRING_PROFILES_ACTIVE=prod + flags resources → smoke curl /actuator/health post-deploy + résumé markdown dans $GITHUB_STEP_SUMMARY. Pas de APP_FRONTEND_URL explicite — le default / du base application.yml marche puisque backend + SPA sont même origine.
  • backup-postgres.yml : nouveau workflow backup Postgres, ~80 lignes. Trigger cron '0 4 * * 0' (dimanche 4 AM UTC, weekly) + workflow_dispatch manuel. Concurrency backup-postgres non-cancelable. WIF auth → install postgresql-client-16 depuis l'apt repo officiel Postgres (forward-compat vs PG 15 Supabase, +1 majeur de buffer) → fetch supabase-db-url Secret Manager → strip préfixe jdbc: pour libpq URI compat → pg_dump --no-owner --no-acl --format=plain | gzip > backup-<ISO-timestamp-UTC>.sql.gzaws s3 cp vers R2 (S3-compatible avec --endpoint-url + R2_* secrets) → prune au-delà des 30 plus récents (~7 mois d'historique au rythme weekly) → summary markdown. Décision cadence : daily envisagé initialement, weekly retenu (worst-case data loss 7j, beaucoup moins de bruit, suffisant pour un solo dev où Supabase free tier fait déjà du quotidien 7j en parallèle comme filet court — notre R2 est le filet long-terme indépendant).

devops/

  • devops/prod/README.md : check-list étape 5 « Câbler le workflow GitHub Actions deploy » passe de ⏳ ~1 h à ✅ Livré 2026-05-18 avec liens vers deploy.yml + release-process.md + backup-process.md.

docs/devops/

  • release-process.md : nouveau. Rituel deploy pas-à-pas (tag → Draft Release → Publish → approve production environment → workflow part → smoke browser), 3 variantes de tag CLI (gh release create direct, tag séparé puis Release, brouillon avant publish), cas particuliers (hotfix, rollback rapide via UI Cloud Run vs propre via rebuild, release supprimée, failed deploy), section pre-releases / RCs, tableau des inputs déjà câblés. Câblé dans mkdocs.yml.
  • backup-process.md : nouveau. Quick links (R2 bucket UI + API tokens + workflow Actions + CLI listing), rituel automatique, setup pas-à-pas (R2 bucket + 3 GH secrets R2_* + 3 GH vars repo-level GCP_* + grant secretmanager.secretAccessor à github-deploy@ per-secret), procédure restore drill trimestriel manuel, follow-ups hors v1 (alert staleness >36h, encryption client-side, cross-region replication, restore drill automatisé), section « Pièges rencontrés au 1er setup » (2 gotchas documentés : token Cloudflare R2 affiche 4 champs et 3 ne servent pas à S3 → mauvais paste possible ; vars ${{ vars.GCP_* }} vides au runtime si le job ne déclare pas environment: production → fix repo-level). Câblé dans mkdocs.yml.
  • liens-utiles.md : section Cloudflare réécrite (de « pas encore configuré » à 3 liens directs bucket / dashboard / API tokens avec Account ID 8f2780696b5e520f85b5fc80413c4c3f substitué) + bloc CLI aws s3 pointé sur R2 + note DNS / proxy à venir. Sous-section GitHub Actions enrichie de deploy.yml + backup-postgres.yml. Pointers internes vers release-process.md + backup-process.md ajoutés à la section Référence projet.
  • deploiement.md : (a) §5 Phase 5a tableau ligne Backup weekly (vs Backup nocturne) avec cron weekly + lien vers backup-process.md. (b) §6.1 setup initial — étape 7 réécrite pour clarifier que les 3 vars GCP doivent être à la fois environment-scoped (production, pour deploy.yml) ET repo-level (pour backup-postgres.yml dont le cron ne tient pas avec un required reviewer gate). (c) §6.2 squelette deploy.yml retiré, remplacé par pointer vers le fichier livré + lien release-process.md. Décisions ancrées préservées comme rationale historique. (d) §6.3 squelette backup retiré (avait des secret names faux et une cron daily), remplacé par pointer + rationale weekly mis à jour.

docs/technique/

  • ops.md : table Workflows GitHub Actions enrichie de deploy.yml (Cloud Run, trigger release-published, 3-5 min) + backup-postgres.yml (Postgres → R2 weekly, 1-2 min). Compteur passé de « Cinq » à « Sept ». Table Permissions enrichie des 3 workflows manquants (smoke-wif.yml + deploy.yml + backup-postgres.yml) tous avec + id-token: write au scope job pour WIF.

docs/projet/

  • backlog.md Phase 5 : tickets fermés — Workflow GitHub Releases (🔴 → livré) et Backup Postgres nocturne (🔴 → livré weekly). Tickets ajoutés en session — Industrialiser le versionning (🟡, drift git tags v0.6.0 vs Artifact Registry v0.7.0-dev1..dev5, à attaquer avant tout tagging post-v0.6.0), Analyse DNS custom domain (🟢, pré-requis du ticket Cloudflare existant), Persister les préférences utilisateur backend (🟡, sortir de localStorage : theme + langue + annotations). Phase 6 — nouveau ticket #10 hors-vague : News scraping + LLM digest (frustration UX directe : « les news de passer par les liens c'est insupportable »).
  • journal-livraisons.md Phase 5 : 3 nouvelles entrées ✅ — Identité visuelle frontend + landing + wildcard 404, Workflow deploy.yml Cloud Run + rituel Release-triggered, Backup Postgres weekly → Cloudflare R2.

Racine

  • README.md : table de doc enrichie de 4 entrées Devops (Liens utiles, Déploiement (analyse Phase 5), Release process, Backup & restore process) — alignées sur la nav MkDocs.
  • mkdocs.yml : 2 entrées nav ajoutées sous Devops (release-process.md, backup-process.md).

Côté Cloud / GitHub (manual ops cette session)

  • GCP : gcloud secrets add-iam-policy-binding supabase-db-url --member=serviceAccount:github-deploy@... --role=roles/secretmanager.secretAccessor (per-secret, principe du moindre privilège — github-deploy@ reste sans accès aux 3 autres secrets OAuth/admin réservés au runtime SA).
  • Cloudflare : compte créé + R2 activé free tier + bucket portfolioai-backups + API token scopé Object Read & Write sur ce seul bucket. Account ID 8f2780696b5e520f85b5fc80413c4c3f.
  • GitHub : 3 secrets R2_ACCOUNT_ID / R2_ACCESS_KEY_ID / R2_SECRET_ACCESS_KEY posés au repo level + 3 variables repo-level GCP_PROJECT / GCP_WIF_PROVIDER / GCP_SA_EMAIL (doublonnent celles déjà présentes dans l'environment production — c'est voulu, voir deploiement.md §6.1 patché). Décision : repo-level pour le cron backup qui ne peut pas tenir avec un required-reviewer gate, env-scoped pour deploy qui veut le gate.
  • GitHub Releases : gh release create v0.7.0-rc1 --target master --prerelease --generate-notes → workflow deploy.yml déclenché → required reviewer approve dans Actions UI → smoke vert end-to-end (login Google fonctionnel, dossier ticker mode mock OK). 1re Release publique sur le repo.

2026-05-18 (Phase 5a — premier deploy Cloud Run + Supabase end-to-end fonctionnel)

4e session de la journée. Walkthrough step-by-step du ticket Provisionner v1 Cloud Run + Supabase. 5 itérations d'image Docker (dev1 → dev5) avec 4 bugs réels rencontrés + fixes structurels documentés. Smoke test browser end-to-end passé sur dev4 : login Google OAuth → callback → session posée → SPA atterrit sur /settings/configuration (route ADMIN-only, role assigné parce que venet.julien@gmail.comAPP_ADMIN_EMAILS injecté depuis Secret Manager). Code SPA fallback (dev5) en place mais pas encore deployé — sera testé au début de la session suivante OU directement via le workflow GitHub Actions automatisé. URL prod : https://portfolioai-912181505110.northamerica-northeast1.run.app/. Coût $0/mo confirmé (sous tous les free tiers).

devops/

  • prod/Dockerfile : (1) Node 20 → Node 24 au Stage 1 frontend-builder (anticipe la dette technique 🟢 sur GitHub Actions Node 20 deprecation juin 2026). (2) mkdir -p ./backend/src/main/resources/static/ ajouté avant le cp -r ./frontend/dist/*/browser/* ./backend/src/main/resources/static/ du Stage 2 — Spring Boot ne crée pas static/ physiquement dans le repo (juste à la volée au runtime), donc le cp Docker plantait sur target is not a directory.
  • prod/README.md : ligne sur Node 20 mise à jour vers Node 24 dans la description du Dockerfile.

backend/

  • src/main/resources/application-prod.yml : suppression complète du bloc app: (overrides app.frontend-url + app.admin.emails redondants — le base application.yml les gère déjà avec relaxed binding ${APP_FRONTEND_URL:/} / ${APP_ADMIN_EMAILS:} qui consomment automatiquement les env vars Cloud Run injectées depuis Secret Manager) ET du bloc spring.security.oauth2.client.registration.google: (l'override prod avait un placeholder ${APP_FRONTEND_URL} sans default qui écrasait le default / du base et crashait SecurityConfig au boot avec Could not resolve placeholder 'APP_FRONTEND_URL'). Le base application.yml n'a intentionnellement pas ce bloc OAuth (ligne 36-55 commentaire qui explique pourquoi : ClientRegistration.Builder.build() crash sur Assert.hasText(client-id) si registration déclarée avec valeur vide), et le relaxed binding env vars Cloud Run (SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_CLIENT_ID/SECRET mountées depuis les 2 secrets Secret Manager) câble automatiquement la registration sans qu'aucun YAML ne soit nécessaire. Header de fichier réécrit pour documenter ce choix de design.
  • src/main/kotlin/com/portfolioai/auth/infrastructure/security/SecurityConfig.kt : changement de modèle authorization. Avant : enumération de permitAll puis anyRequest().authenticated() (conçu pour archi dev Phase 4 où la SPA Angular tourne sur :4201 séparé du backend, et Spring sert seulement /api/** + /oauth2/** + /login/**). Après : requestMatchers("/api/**").authenticated() explicite + anyRequest().permitAll(). Couvre l'archi prod où la SPA Angular est embarquée dans le jar (static/) et toutes les routes client-side (/, /login, /dashboard, /ticker/AAPL, /settings/configuration, etc.) doivent passer Security pour que SpaFallbackConfig puisse forward sur index.html. Aucune surface API non protégée — tout ce qui est sensible vit sous /api/** (authenticated) ou /api/{config,prompts,narrative/observability}/** (hasRole ADMIN). Commentaire d'invariant ajouté.
  • src/main/kotlin/com/portfolioai/shared/SpaFallbackConfig.kt : nouveau fichier (~50 lignes). @Configuration @Profile("prod") WebMvcConfigurer qui câble un resource handler /** avec un PathResourceResolver custom. Logique : (a) si le path commence par api/ / actuator/ / oauth2/ / login/oauth2/ / swagger-ui/ / v3/api-docs/, retourne null (laisse Spring routing naturel — controllers ou Spring Boot actuator gèrent ; un vrai 404 d'endpoint reste un 404, pas un faux 200 + index.html), (b) si le path correspond à un fichier dans static/ (e.g. /main.abc.js), le sert directement, (c) sinon (toutes les routes Angular client-side), forward vers index.html — Angular Router prend le relais browser-side pour rendre la bonne vue. Actif uniquement en prod parce qu'en dev (tilt up) la SPA tourne sur Angular CLI port 4201 avec son propre SPA fallback, Spring static/ est vide.

Côté Cloud (manual ops, déjà côté GCP avant cette session)

  • Cloud Run service portfolioai créé en northamerica-northeast1. Configuration : --service-account=portfolioai-runtime@trade-496613.iam.gserviceaccount.com, --memory 1Gi --cpu 1, --max-instances 3 --min-instances 0 (scale-to-zero), --timeout 300 (5 min pour requêtes LLM longues), --port 8080, --allow-unauthenticated (auth gérée par Spring Security in-app), --update-secrets mountent 4 secrets Secret Manager comme env vars (SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_{CLIENT_ID,CLIENT_SECRET} + APP_ADMIN_EMAILS + SPRING_DATASOURCE_URL). 4 revisions deployed dev1..dev4 (dev5 en attente). Service URL stable : https://portfolioai-912181505110.northamerica-northeast1.run.app/.
  • Artifact Registry : 5 images poussées (dev1 ARM64 inutilisable, dev2..dev5 amd64 fonctionnels). Path : northamerica-northeast1-docker.pkg.dev/trade-496613/backend/portfolioai:v0.7.0-devN.
  • Google Cloud Console OAuth Client : redirect URI ajoutée manuellement : https://portfolioai-912181505110.northamerica-northeast1.run.app/login/oauth2/code/google. À refaire pour le custom domain Cloudflare quand ce ticket sera livré.
  • Supabase portfolioai-prod : Flyway V1 appliquée au 1er boot Spring (création des 11 tables visible dans public.flyway_schema_history). Note opérationnelle : l'erreur dashboard Supabase relation "supabase_migrations.schema_migrations" does not exist observée pendant la session est bénigne — c'est la console Supabase qui essaie de lire SA table de migrations interne (utilisée par leur CLI supabase db push), qu'on n'utilise pas parce qu'on est sur Flyway. Ignorable.

projet/

  • backlog.md : (1) Ticket ⏳ Provisionner et déployer v1 sur Cloud Run + Supabase retiré de la section 🔴 Phase 5 (livré, voir journal). (2) Ticket ⏳ Workflow GitHub Releases promu de 🟡 Moyenne à 🔴 Critique + remonté en tête de la section 🔴 (avant Backup Postgres nocturne). Description réécrite avec les inputs concrets identifiés au deploy manuel : commande docker build --platform linux/amd64, path Artifact Registry, flags exacts gcloud run deploy validés ce soir, état "tout est prêt" côté GCP/GitHub/Supabase, et modèle release-triggered explicite (on: release: published, NOT on: push: master) suite à la décision user 2026-05-18 : un deploy = un acte conscient via création d'une Release GitHub avec tag, pas à chaque merge. Effort estimé révisé à ~1-2 h (vs ~½-1 j initial) parce que tous les inputs sont stabilisés.
  • journal-livraisons.md : entrée ✅ Provisionner et déployer v1 sur Cloud Run + Supabase ajoutée en tête de Phase 5 — documente les 5 itérations Docker (dev1 ARM64 → dev2 amd64 → dev3 placeholder bug → dev4 SecurityConfig 401 → dev5 SPA fallback pending), les 4 fichiers code modifiés, l'état final GCP + Supabase, et le suit naturel (ticket Workflow GitHub Releases).

2026-05-18 (Phase 5 — devops/prod/ scaffold + commit des Spring profile YAMLs)

3e session de la journée. Scope révisé deux fois en discussion vs le ticket backlog initial : (1) les symlinks racine pour Tiltfile + docker-compose.yml sont confusants (fichiers visibles 2× dans les recherches IDE) → rollback total, devops/local/ supprimé. (2) application-local.yml n'est plus secret-bearing depuis Phase 4 → un-gitignore + commit à backend/src/main/resources/. État final propre : devops/ ne contient que devops/prod/ (3 fichiers infra), les 3 YAMLs Spring vivent ensemble dans backend/src/main/resources/ (convention native, fresh-clone-friendly, dangerous settings isolés au profil local par construction). Aucun code applicatif touché.

devops/

  • prod/Dockerfile : nouveau fichier (~60 lignes). Multi-stage build pour Cloud Run linux/amd64 : Stage 1 Node 20 builds Angular, Stage 2 Eclipse Temurin 21 JDK builds le fat jar Spring Boot en embarquant l'output Angular dans static/, Stage 3 Eclipse Temurin 21 JRE minimal (~200 MB final) runtime avec utilisateur non-root spring et JVM tuning containerisée (-XX:MaxRAMPercentage=75 -XX:+UseG1GC). Build invocable depuis la racine via docker build -f devops/prod/Dockerfile .. Cache layers npm + gradle dependencies optimisés pour ré-builds incrémentaux.
  • prod/service.yaml : nouveau fichier (~60 lignes). Cloud Run service descriptor stub (Knative API serving.knative.dev/v1) qui documente le shape complet attendu : annotations scaling (maxScale=3, minScale=0 scale-to-zero, startup-cpu-boost=true), serviceAccount runtime portfolioai-runtime@, healthchecks startupProbe + livenessProbe sur /actuator/health, ressources memory=1Gi cpu=1, env vars (SPRING_PROFILES_ACTIVE=prod, APP_FRONTEND_URL) + secrets mountés via secretKeyRef sur les 4 secrets Secret Manager (google-oauth-client-id, google-oauth-client-secret, app-admin-emails, supabase-db-url). Tags TODO_TAG (image) et TODO_URL (custom domain) à remplir au moment du 1er deploy. Sera utilisé soit en référence pour gcloud run deploy imperatif soit pour gcloud run services replace --file service.yaml déclaratif plus tard.
  • prod/README.md : nouveau fichier. Check-list de l'état Phase 5 (ce qui est déjà câblé côté GCP + GitHub + Supabase) + les 5 étapes restantes du ticket Provisionner v1 (build local Docker → push Artifact Registry → premier gcloud run deploy manuel → smoke test end-to-end → workflow .github/workflows/deploy.yml on: release: published). Pointe vers ../deploiement.md pour le plan complet.

Spring profile YAMLs (déplacements + commits)

  • backend/src/main/resources/application-local.yml : passé de gitignored à committé. Aucun changement de contenu (mock providers + Ollama wiring + springdoc activé + JPA logging off + Flyway repair-on-migrate). Le ré-examen Phase 4 + Phase 5 a confirmé qu'aucun secret n'y vit ; les seuls settings dangereux-en-prod (flyway.repair-on-migrate=true, springdoc.api-docs.enabled=true) sont isolés au profil local par construction → safe à committer.
  • backend/src/main/resources/application-prod.yml : git mv depuis devops/prod/application-prod.yml (le stub posé une heure plus tôt). Header réécrit pour retirer la note « À déplacer » qui n'a plus de sens, remplacée par une explication de la convention Spring native (chargé automatiquement quand SPRING_PROFILES_ACTIVE=prod est posé par Cloud Run via service.yaml > env). Contenu inchangé : 6 sections (server/forward-headers + datasource HikariCP + jpa.ddl-auto=validate + Flyway baseline=0 + logging INFO + actuator probes + app + OAuth2 Google injecté depuis env vars Secret Manager).
  • Tiltfile + docker-compose.yml : rollback à la racine après un détour de ~10 min dans devops/local/. Les symlinks racine créés pour préserver tilt up étaient corrects techniquement mais "bizarre visuellement" (fichiers visibles 2× dans les recherches IDE) → décision de garder l'infra locale à la racine, devops/local/ supprimé entièrement.

projet/

  • backlog.md : ticket ⏳ Dossier devops/ retiré de la section 🔴 Phase 5 (livré). Phase 5 🔴 restants : Provisionner v1 Cloud Run + SupabaseBackup Postgres nocturne.
  • journal-livraisons.md : entrée ✅ Dossier devops/prod/ + commit des Spring profile YAMLs ajoutée en tête de Phase 5 (au-dessus de GitHub Secrets vault qui est aussi du jour). Documente la trajectoire scope (idée initiale symlinks → rollback) + le ré-cadrage application-local.yml (gitignored → committé) + l'état final.

technique/

  • developpement.md : (1) tree mis à jour pour refléter le nouvel état (devops/prod/ only, Tiltfile + docker-compose.yml à la racine sans annotations symlink). (2) Paragraphe « Le fichier application-local.yml (gitignored) contient... » réécrit en « (committé depuis 2026-05-18, sans secrets) ». (3) Paragraphe « Ne jamais committer application-local.yml ni .env » réécrit pour cibler .env uniquement, avec ajout d'une règle explicite « application-local.yml est désormais committé mais doit rester strictement secret-free ».
  • developper.md : section Configurer le LLM réécrite end-to-end. L'ancien flow disait « Crée le fichier application-local.yml (gitignoré, jamais commit) + colle ta clé Claude dedans » — caduc et dangereux maintenant que le fichier est committé. Nouveau flow propose 2 paths pour la clé Anthropic : (A.1) UI runtime /settings/configuration > LLM (recommandé, persisté en BDD app_config, aucun reboot) et (A.2) boot-time ANTHROPIC_API_KEY=... dans .env. Option B Ollama est désormais le défaut "rien à configurer". Encadré « ne jamais coller la clé Anthropic dans application-local.yml » ajouté. Les colonnes TwelveData et Finnhub du tableau providers reçoivent la même mise à jour (clé en .env ou UI, jamais YAML committé).
  • ops.md : (déjà mis à jour le matin pour mentionner smoke-wif.yml workflow). Aucun changement supplémentaire ici.

.claude/

  • CLAUDE.md : 4 patches. (1) Repository Structure simplifié — devops/ ne contient plus que prod/ ; les commentaires d'annotation symlink retirés de Tiltfile et docker-compose.yml à la racine. (2) Local Development — précision que application-local.yml est désormais committé sans secrets, avec renvoi vers Data & secrets pour le détail. (3) Backend conventions — entrée « Config in YAML » étendue pour mentionner les 3 fichiers committés sans secrets. (4) Data & secrets — paragraphe principal réécrit pour clarifier que application-local.yml + application-prod.yml sont committés (no secrets, only behavior overrides) avec pointer vers .env (local) et GCP Secret Manager (prod, cf. docs/devops/deploiement.md) pour les vrais credentials.

Racine

  • .gitignore : la ligne backend/src/main/resources/application-local.yml retirée. Bloc « Spring Boot » conservé avec un commentaire explicatif qui documente la policy actuelle (les 2 YAMLs profile sont committés sans secrets, secrets vivent dans .env ou GCP Secret Manager).
  • application-prod.yml header (dans son nouveau home backend/src/main/resources/) : note « À déplacer au moment du Provisionner v1 ticket » retirée, remplacée par une explication du chargement automatique Spring native quand SPRING_PROFILES_ACTIVE=prod est posé.

2026-05-18 (Phase 5 — GitHub Secrets vault livré, WIF validé end-to-end)

Walkthrough step-by-step pour câbler le pipeline secret-management côté CI/CD (GCP + GitHub) + valider le tout avec un smoke test minimal. Aucune décision d'architecture remise en question depuis le commit précédent — c'est de l'exécution propre du ticket 🔴 « GitHub Secrets + Environments vault ». Pattern de collaboration nouveau documenté : Claude crée les fichiers .github/workflows/*.yml directement avec Write, le user gère uniquement git add/commit/push (memory feedback_write_files_user_commits.md).

.github/

  • workflows/smoke-wif.yml : nouveau workflow (~30 lignes). Mini job manuel workflow_dispatch qui valide le pipeline Workload Identity Federation de bout en bout : (1) obtention OIDC token avec permissions: id-token: write, (2) google-github-actions/auth@v2 échange contre access token GCP via le pool github et le SA github-deploy@, (3) setup-gcloud@v2 configure le CLI, (4) gcloud run services list --region=northamerica-northeast1 exerce roles/run.admin, (5) gcloud artifacts repositories describe backend --location=northamerica-northeast1 exerce roles/artifactregistry.writer. Utilise environment: production pour exercer aussi la protection rule « required reviewers » côté GitHub. Run #1 a passé en 25 s avec 5 steps verts + 1 annotation Node 20 deprecation (filed en dette technique 🟢).

projet/

  • backlog.md : (1) Ticket ⏳ GitHub Secrets + Environments comme vault de CI/prod retiré de la section 🔴 Phase 5 (livré, voir journal). Phase 5 🔴 restants : Dossier devops/ skeleton → Provisionner v1 Cloud Run + SupabaseBackup Postgres nocturne. (2) Nouveau ticket dans Dette technique (🟢) : Migrer google-github-actions/auth@v2 + setup-gcloud@v2 vers Node 24 — capture l'avertissement Node.js 20 deprecation observé sur le run #1 du smoke test (Node 20 default-disabled le 2 juin 2026, supprimé 16 sept 2026). Inséré juste au-dessus du ticket CodeQL v3→v4 existant pour grouper les deprecations de workflows.
  • journal-livraisons.md : entrée ✅ GitHub Secrets + Environments vault ajoutée en tête de Phase 5 (au-dessus de Analyse hébergement). Documente les 11 sous-étapes livrées : billing account lié, 6 APIs activées, 2 service accounts deploy+runtime avec rôles granted per-secret pour le runtime SA, Workload Identity Pool + Provider GitHub OIDC avec attribute condition repository_owner == 'jv3n', binding sur principalSet attribute.repository/jv3n/trade, repo Artifact Registry backend créé à northamerica-northeast1, 4 secrets pushés via stdin (zéro trace shell history), choix mode connexion Supabase Session pooler 5432 (compromis Flyway-friendly + IPv4 obligatoire pour Cloud Run), Environment GitHub production avec required reviewer + branch policy master only, 3 Environment variables (pas secrets, parce qu'identifiants publics), Secret scanning + Push protection + Dependabot alerts activés, projet Supabase portfolioai-prod créé région ca-central-1 Toronto. Header Phase 5 mis à jour pour refléter que la DB Supabase est en ca-central-1 (pas US-East comme initialement supposé dans le deliverable v2).

.claude/projects/<repo>/memory/

  • feedback_write_files_user_commits.md : nouvelle memory feedback. Pattern de collaboration émergent pendant les walkthroughs step-by-step — Claude crée les fichiers (workflows, configs, Dockerfile, etc.) directement avec Write/Edit, le user gère uniquement les git operations. Couvre aussi : pas besoin de demander confirmation avant Write quand le contenu a été validé en discussion, le simple « ok crée le fichier » = feu vert.
  • MEMORY.md : ligne d'index ajoutée pour la nouvelle memory ci-dessus.

2026-05-18 (ouverture Phase 5 — analyse hébergement révisée, choix final Cloud Run + Supabase)

Session de cadrage Phase 5 sur la même journée, 3 itérations de la décision : (1) analyse initiale → reco Fly.io sur l'hypothèse « Ollama prod requis » (chip-question matin) ; (2) clarification contrainte #4 → VPS+IaC redevient éligible mais Fly maintenu ; (3) clarification LLM prod → « pas d'Ollama en prod, Mock+Claude uniquement », ce qui réouvre la fenêtre serverless → re-shortlist → choix final Cloud Run + Supabase ($0/mo durable). Le doc-set a été révisé pour refléter le choix final, avec le journey de décision tracé honnêtement dans le journal et l'architecture.md. Aucun code applicatif touché. Pas de tag associé (phase en cours, premier tag candidat v0.7.0 à la clôture Phase 5a). Effort cumulé session : ~4 h (3 itérations questions chip + 6 WebSearches pricing + 1 deep-dive Oracle A1 + écriture deliverable v1 + révision v2 + reorder backlog 2× + sync doc-set complet).

devops/

  • deploiement.md : réécriture v2 (rev du même jour). Provider primaire passe de Fly.io à Cloud Run + Supabase Postgres. Fly Phase 5a ($10/mo) et Oracle A1 Ampere ($0 + sysadmin léger) demotés en alternatives documentées avec triggers de bascule clairs (Phase 5c). Sections révisées : §1.5 (LLM prod = Mock+Claude seulement, Ollama exclu), §2 short-list (4 candidats : Cloud Run+Supabase, Fly, Oracle A1, Railway/Render), §3 tableau comparatif (colonne Cloud Run+Supabase ajoutée), §4 recommandation (4 arguments décisifs : $0 durable, Montréal natif compute, ops zéro, lock-in cosmétique), §5 plan phasé (5a $0 bootstrap → 5b $0 hardening Cloudflare+monitoring → 5c $0-25 si free tier serre), §6 pipeline GitOps réécrit pour gcloud run deploy + Workload Identity Federation (pas de SA JSON key) + workflow backup nocturne pg_dump → R2, §7 migration plan révisé avec discipline « zéro SDK Supabase » non-négociable, §8 risques (Supabase free tier shrinkage, Cloud Run egress 1 GB, auto-pause 7j), §9 tickets dépendants révisés, §10 décisions résumé. Sources ajoutées : Supabase pricing, Google Cloud Run, GitHub OIDC ↔ GCP Workload Identity Federation.

technique/

  • architecture.md : section Décisions techniques notables > Phase 5 — déploiement réécrite. Le bloc « Fly.io retenu » devient « Cloud Run + Supabase retenu » avec le journey de décision (3 itérations) documenté en lead, 3 arguments décisifs ($0 durable, Montréal native compute, ops zéro), plan phasé révisé, pipeline GitOps avec Workload Identity Federation explicite (pas de SA JSON key), lock-in cosmétique avec discipline non-négociable triple (zéro SDK Supabase, zéro Cloud Run-specific, backup nocturne), et rationales d'élimination révisées (Fly $120/an pour zéro gain composite sans Ollama ; Oracle A1 overkill ~2 GB/24 ; Hetzner latence injustifiée).

projet/

  • backlog.md : Phase 5 intro paragraphe réécrit (Fly.io → Cloud Run + Supabase) + 5 changements de tickets cohérents. (1) Provisionner v1 sur Fly.ioProvisionner v1 sur Cloud Run + Supabase — checklist détaillée mise à jour (GCP project + Workload Identity Federation + Supabase free + Artifact Registry + Secret Manager + smoke test login OAuth). (2) Backups Postgres automatisés réécrit en Backup Postgres nocturne pg_dump → Cloudflare R2 + promu 🔴 Critique (discipline d'exit dès le 1er deploy, pas plus tard). (3) Custom domain + TLS Let's Encrypt sur Fly fusionné dans Cloudflare devant Cloud Run — custom domain + TLS + cache + bypass egress quota Cloud Run free en un shot. (4) (Phase 5b) Ajouter Machine Ollama 4 GB retiré (caduc — Ollama exclu prod). (5) Nouveau ticket Monitoring uptime + Sentry ajouté (free tiers Healthcheck.io + Sentry hobby). Note de fin de section réécrite : pointe vers Phase 5c (Neon migration / Supabase Pro / Fly fallback) au lieu du « filed après l'analyse hébergement » qui n'a plus de sens post-analyse.
  • journal-livraisons.md : header Phase 5 et entrée ✅ Analyse hébergement réécrits pour refléter le choix final Cloud Run + Supabase. L'entrée capture les 3 itérations honnêtement (reco initiale Fly → clarification contrainte #4 → clarification Ollama prod → choix final révisé) plutôt que de présenter Cloud Run+Supabase comme la 1ère reco. Données comparatives, plan phasé révisé, pipeline GitOps WIF, discipline non-négociable triple, et tickets dépendants filed/révisés tous mis à jour.

Memory (.claude/projects/<repo>/memory/)

  • project_phase5_deploiement.md : title + body réécrits pour refléter le choix final (Cloud Run + Supabase) avec le rationale décisionnel mis à jour. Plan phasé et triggers de bascule (Fly $10/mo plan C, Oracle A1 fallback Ollama future, Neon free si Supabase serre) tous documentés.

devops/

  • deploiement.md : nouveau fichier (~10 sections). Synthèse complète de l'analyse — contexte + contraintes utilisateur arbitrées 2026-05-18, short-list 3 candidats PaaS (Fly.io, Railway, Render) après préférence assumée pour PaaS sur la charge ops récurrente ; VPS + IaC (Hetzner Cloud / OVH / DigitalOcean + Terraform) documenté en fallback éligible, tableau comparatif (latence Montréal, backend tier mini, Postgres v1/v2, Ollama, GitOps, lock-in, backups, TLS, scale-to-zero, estimation totale), recommandation Fly.io avec 3 arguments décisifs, plan phasé 5a (~$10/mo Claude-only) → 5b (~$35/mo +Ollama) → 5c (~$50+/mo Managed Postgres), pipeline GitHub Actions trigger on: release: published avec superfly/flyctl-actions, plan de migration sortie (~3-4 h via Dockerfile standard), risques assumés (backups manuels, Ollama always-on flat, cold-start backend), tickets dépendants à filer, et résumé tableau des 10 décisions clés. Sources citées : Fly.io pricing officiel, Railway pricing, Render pricing, Fly continuous deployment docs.

technique/

  • architecture.md : nouvelle sous-section Décisions techniques notables > Phase 5 — déploiement ajoutée juste après le bloc Phase 4 (avant Modèle pipeline d'analyse). Une décision documentée — Fly.io retenu comme hébergement v1 — qui condense les 3 arguments décisifs (latence yyz ~5 ms vs ~25-30 ms concurrents, architecture multi-Machine adaptée au pattern backend + sidecar Ollama, Fly Postgres unmanaged $7/mo honnête vs Managed $38/mo), le plan phasé 5a/5b/5c, la stratégie GitOps stricte (trigger sur release, GitHub Environment scoped), le lock-in mesuré (~30 lignes fly.toml), et les rationales d'élimination (Railway pas de Canada, Render $7/mo par service, VPS self-hosted violerait la contrainte utilisateur). Pointe vers docs/devops/deploiement.md pour le détail.

projet/

  • backlog.md : six changements cohérents en une session. (1) Phase 5 intro paragraphe réécrit pour fixer le verdict — « Provider retenu 2026-05-18 : Fly.io (région yyz Toronto), bootstrap ~$10/mo... » avec lien vers docs/devops/deploiement.md. L'ancienne formulation décrivait Phase 5 comme « gros morceau qui touche tous les axes (infra, observabilité prod, RGPD) » sans verdict — caduque post-analyse. (2) Ticket ⏳ Analyse hébergement retiré du backlog (livré, voir journal). (3) Ticket ⏳ Dossier devops/ skeleton minimal ajouté (issu de la demande utilisateur du même jour — séparer configs par env local + prod, pas de wiring runtime v1). (4) Reorder des 🔴 Critique : avant cette session, GitHub Secrets vault était en tête ; le user a tranché « Analyse hébergement d'abord » donc l'ordre devient Analyse hébergement → GitHub Secrets → devops/. Analyse hébergement est ensuite retirée (livrée) → l'ordre final 🔴 est GitHub Secrets → devops/ → Provisionner v1 Fly.io. (5) Quatre nouveaux tickets follow-up Phase 5 filed (issus de deploiement.md > §9 tickets dépendants) : Provisionner et déployer v1 sur Fly.io (Phase 5a bootstrap manuel) 🔴, Backups Postgres automatisés — cron pg_dump + Tigris S3 + alert 🟡, Custom domain + TLS Let's Encrypt sur Fly 🟡, (Phase 5b) Ajouter Machine Ollama 4 GB 🟡. (6) Wording GitHub Secrets ticket mis à jour : « Premier ticket Phase 5 (parallèle à l'analyse hébergement ci-dessous, mais indépendant — peut démarrer dès maintenant) » → « Démarrable en parallèle de l'analyse hébergement (indépendant du choix provider) » + retrait de « la position première dans la phase » (plus exacte post-reorder).
  • journal-livraisons.md : nouvelle section Phase 5 — Déploiement (en cours) ajoutée en tête (avant Phase 4), avec header qui fixe le verdict provider + lien vers deploiement.md + précision que les tickets ⏳ restent au backlog. Une entrée ✅ Analyse hébergement — comparaison PaaS + recommandation Fly.io livrée 2026-05-18 — décrit les contraintes utilisateur arbitrées via 3 questions chip, les données comparatives clés relevées (Fly.io shared-cpu-1x@1GB $5.7/mo + Postgres unmanaged $7.20/mo, Railway Hobby $5/mo flat, Render Starter $7/mo + Basic-256mb $6/mo), les 3 différentiateurs Fly.io (région yyz, multi-Machine, Postgres unmanaged honnête), le plan phasé 5a/5b/5c, le pipeline GitOps tranché (trigger on: release: published, FLY_API_TOKEN scoped), le migration plan sortie, les risques assumés, les 4 tickets dépendants filed dans le backlog, et l'effort réel (~1.5 h).

Racine

  • mkdocs.yml : ligne ajoutée à la section nav > Devops — « Déploiement (choix d'hébergement Phase 5) : devops/deploiement.md » entre commandes-pratiques.md et decision-ollama-deploiement.md. Sans cette entrée le nouveau fichier était orphelin du site déployé même si linké depuis backlog/journal.

2026-05-17 (clôture Phase 4 — audit doc-maintainer pré-tag v0.6.0)

Session de patches doc-set déclenchée juste après l'audit pré-tag v0.6.0 (3 subagents code-reviewer + 1 subagent doc-maintainer en parallèle, cf. projet/audits/2026-05-17-pre-v0.6.0-phase4.md). Le doc-maintainer a sorti 19 findings (7 HIGH + 7 MED + 5 LOW + 2 [?]), tous causés par les 6 commits Phase 4 (563df74f9588c2) qui ont livré OAuth2 Google OIDC + multi-tenant user_id FK + provider gating + Flyway V1→V10 squash sans que la doc soit mise à jour en parallèle. Un seul finding HIGH filtré (faux positif sur backlog.md « Phase 4 clôturée + ⏳ » — les ⏳ aux lignes 68-72 sont en Phase 5, pas Phase 4 ; l'agent avait mal compté). Tout le reste patché en un seul pass. Couvre aussi les patches Bloquants pré-tag de la matinée (B1 architecture.md schéma Flyway périmé + B2 AppUserPrincipal déplacé infrastructure/security/domain/) et les 4 paragraphes ajoutés/réécrits suite au code-review local (À discuter A/B/D : forward-headers-strategy ticket Phase 5, @ManyToOne User cross-bounded-context dans Décisions techniques notables, note /api/me non-permitAll dans SecurityConfig).

metier/

  • fonctionnalites.md : section Phase 4 — header passé de « v1 livrée 2026-05-17 » à « clôturée 2026-05-17 », et la liste « Reste à attaquer avant Phase 5 » réécrite. Avant, elle citait (1) la migration multi-tenant user_id FK et (2) la redaction des emails dans les logs comme à faire — les deux sont livrés depuis 03e8324 et e553fc8. Le nouveau paragraphe énumère le scope livré (multi-tenant, provider gating, logs redaction, Flyway squash) et pointe les tickets résiduels vers Phase 5 / Dette technique (hardening secret-management, forward-headers-strategy, GitHub Secrets vault, multi-provider OAuth).

technique/

  • architecture.md : six patches dans la même session. (1) Section Schéma de base de données (ligne 269) entièrement réécrite — l'ancien paragraphe « Neuf migrations Flyway V1→V9 » décrivait des fichiers supprimés par le squash livré dans f9f5f23. Le nouveau paragraphe décrit la migration unique V1__init.sql avec énumération des 11 tables dans l'ordre des FK + mention baseline-on-migrate: true + baseline-version: 0. (2) Section auth/ > Domain + persistence (ligne 208-210) — le bullet « Migration V9 crée uniquement app_user. Aucune FK user_id sur les autres tables en v1 » remplacé par un bullet qui décrit la FK user_id NOT NULL ON DELETE CASCADE sur portfolio et watchlist_entry (livrée dans 03e8324, incluse dans le V1 unifié), avec mention de la contrainte UNIQUE (user_id, symbol) sur watchlist. (3) Section Modules frontend > core/ (ligne 252) — description de auth.interceptor.ts corrigée : l'ancienne version disait « 5xx → redirect /error avec query params status + url », faux depuis 1e3a6d3 qui a retiré ce comportement. Le nouveau texte précise « les 5xx ne sont pas interceptés — les composants gèrent en local ». (4) Compteur provideRepositories (ligne 255) — « 14 bindings » → « 15 bindings » (Phase 4 a ajouté Auth). (5) Section Décisions techniques notables > Phase 1+ frontend (ligne 349) — « 8 buckets miroirs du backend » → « 9 buckets miroirs du backend » (auth/ ajouté) et « split sur 3 axes » → « split sur 5 sous-dossiers » avec mention de core/http/ (interceptors) et core/router/ (guards) Phase 4. (6) Section auth/ (ligne 220) — la mention de l'interface marker AppUserPrincipal enrichie d'une précision sur son emplacement en auth/domain/ (déplacement Bloquant B2 du jour pour respecter la frontière hexagonale application/ → domain/), et faute de typo « déliberément » → « délibérément » corrigée.
  • ddd.md : (1) tableau Bounded Contexts (ligne 15) — entrée watchlist corrigée : « single-table, pas de user_id » → « scopée user_id depuis Phase 4, UNIQUE (user_id, symbol) » (FK livrée dans 03e8324). Statut passé à « Phase 2 — multi-tenant Phase 4 ». (2) Nouvelle ligne auth ajoutée au tableau (Phase 4) — décrit le contexte OAuth2 + rôles + profile local-no-auth, comme source de vérité du user_id consommé par portfolio/watchlist. (3) Section Dépendances cross-contextes autorisées — 4 nouvelles lignes ajoutées au bloc ASCII pour documenter les flèches portfolio.domain → auth.domain, watchlist.domain → auth.domain, portfolio.application → auth.application, watchlist.application → auth.application. Note explicative qui pointe vers la décision dans architecture.md et fixe la limite de la tolérance (entities JPA seulement, pas les ports).
  • developpement.md : (1) arborescence ASCII core/ (lignes 157-164) — « split sur 3 axes » → « split sur 5 sous-dossiers » avec ajout des sous-dossiers http/ (interceptors Phase 4) et router/ (guards Phase 4) ; « 8 bounded contexts » → « 9 bounded contexts » avec auth/ ajouté à la liste des buckets ; mention auth.service.ts ajoutée au commentaire de app-state/ ; « wires les 14 ports » → « wires les 15 ports ». (2) arborescence ASCII backend/ (ligne 172) — nouveau module auth/ listé en premier (Phase 4 — OAuth2 + ADMIN/USER + local-no-auth) ; les modules portfolio/ et watchlist/ annotés « multi-tenant via user_id FK depuis Phase 4 » ; mention « V4 » retirée de config/ (numérotage périmé post-squash, le V1 unifié contient tout).
  • developper.md : section « Ce qui te manque encore après cet onboarding » — bullet conventions hexagonales front réécrit pour mentionner les 5 sous-dossiers de core/ (api/, local/, app-state/, http/ interceptors Phase 4, router/ guards Phase 4). L'ancienne formulation « 3 axes » prêtait à confusion post-Phase 4.

projet/

  • journal-livraisons.md : header Phase 4 (ligne 13) — « Phase ouverte 2026-05-17. Sortie du mode single-user no-auth via OAuth2 Google + Spring Security côté backend, AuthService + interceptor + /login côté frontend... la foundation backend est livrée en premier ; la migration user_id FK ... et le frontend /login + guards arrivent en tickets séparés » → « Phase clôturée 2026-05-17 » avec énumération de toute la livraison (foundation + frontend + Google OIDC + CSRF + multi-tenant FK + provider gating + logs redaction + Flyway squash + DevX toggle) et mention des deux tickets résiduels passés en Phase 5. La parenthèse précédente décrivait l'état mi-parcours et entrait en contradiction avec backlog.md:54 qui marque la phase clôturée.
  • audits/2026-05-17-pre-v0.6.0-phase4.md : nouveau fichier — audit pré-tag v0.6.0 synthétisant 3 subagents code-reviewer lancés en parallèle (sécurité multi-tenant + qualité backend + qualité frontend) sur la delta v0.5.1..HEAD. Verdict initial needs-fix avec 2 Bloquants (drift architecture.md schéma Flyway + violation hexagonale AuthServiceAppUserPrincipal en infrastructure) tous deux patchés en session, plus 11 À discuter + 8 Mineurs + 4 faux positifs filtrés.
  • audits/index.md : entrée 2026-05-17 ajoutée en tête de l'historique (reverse-chronological), pointe vers le nouveau fichier.
  • backlog.md (déjà patché avant l'audit, mais entrée pour traçabilité) : nouveau ticket Phase 5 🟡 « Hardening sécurité — restreindre server.forward-headers-strategy ou whitelist proxies internes » (issu d'À discuter A du code-review local pré-audit) inséré après le ticket « secret management OAuth en prod » pour grouper les sujets sécurité prod.

.claude/

  • CLAUDE.md : trois drifts factuels patchés. (1) Ligne 101 — description de auth.interceptor.ts corrigée : « 5xx → redirect /error avec query params status + url » remplacé par « les 5xx ne sont pas interceptés — les composants gèrent leurs erreurs en local (fail-soft, banners inline) ». Le commit 1e3a6d3 avait retiré ce comportement mais CLAUDE.md reflétait encore le plan initial. (2) Ligne 105 — description de la page error/ reformulée pour matcher : « surface globale pour les 5xx sur /api/** (l'interceptor route ici) » → « surface manuelle pour les états bloqués », avec mention que la page lit auth.lastError signal. (3) Ligne 78 — bullet « Migration V9 crée app_user... pas de FK user_id sur les autres tables en v1 (ticket dédié) » remplacé par une description de la migration unique V1__init.sql post-squash + mention de la FK user_id NOT NULL ON DELETE CASCADE livrée dans le même diff + UNIQUE (user_id, symbol) sur watchlist + baseline-version: 0. Sans ce fix, un dev qui ouvre CLAUDE.md cherchait un V9__app_user.sql qui n'existe plus.

Racine

  • mkdocs.yml : section nav > Projet > Audits — ajout des deux entrées manquantes pour les audits récents (2026-05-17-pre-v0.6.0-phase4 et 2026-05-16-pre-v0.5.1). Les fichiers existaient sur disque depuis leur création mais n'étaient accessibles que via audits/index.md — la nav latérale MkDocs ne les listait pas, donc orphelins du site déployé.

2026-05-16 (audit doc-maintainer post-refactor core/ 3-axes + précisions backend [?])

Patch session déclenchée par /doc-maintainer après le refactor structurel core/ frontend (livré le matin même : groupage en core/api/<bucket>/ + core/local/<bucket>/ + core/app-state/). L'audit a sorti 10 findings (4 HIGH + 5 MED + 1 LOW + 2 [?]), tous concentrés sur la propagation du refactor dans les docs hors CLAUDE.md (déjà aligné en session). Patch tout retenu par l'utilisateur ; les 2 [?] (split SymbolSearchService / SymbolValidator du 2026-05-15 et grouping @Value AppConfigService en 3 holders) validés sur lecture du code et intégrés à architecture.md. Finding #8 (incohérence interne du CLAUDE.md) filtré : la section narrative ET l'ASCII tree étaient déjà alignés au moment des patches — l'agent avait lu un état antérieur. Finding #10 (mix FR/EN dans developpement.md) filtré : l'arborescence est en réalité homogène FR après les patches HIGH du jour.

technique/

  • developpement.md : (1) arborescence ASCII core/ réécrite — l'ancien commentaire « 14 repositories » à plat + *.repository.ts racine + adapters/*.http.ts + adapters/*.local.ts remplacé par les 3 axes (api/<bucket>/ qui liste les 8 bounded contexts miroirs du backend + leur adapters/*.http.ts, local/<bucket>/ pour les ports navigateur, app-state/ pour theme + language sans port). Mention explicite des services bucket-locaux d'analysis/ (ollama-status, job-stream SSE, llm-timeout) qui ne sont pas des repositories — un dev qui parcourait l'arbo ne savait pas où ces 3 services vivaient. (2) Section « Thème et UI » — chemin frontend/src/app/core/theme.service.ts corrigé en frontend/src/app/core/app-state/theme.service.ts. Sans ce fix, un dev qui suivait la référence aboutissait sur un 404.
  • architecture.md : (1) schéma ASCII overview frontend — bloc core/ étendu de 2 à 4 lignes pour rendre visible les 3 axes (api/<bucket>/ + adapters/, local/<bucket>/ + .local.ts, app-state/ + theme/language signal). Le schéma overview est typiquement la 1ère chose que lit un nouvel arrivant. (2) Section Modules frontend > core/ — paragraphe restructuré en 3 sous-bullets explicites (api/ / local/ / app-state/) qui décrivent la forme port + adapter par axe et listent l'ensemble des 14 repositories répartis entre api/ et local/. L'ancienne version mélangeait dans la même phrase ports HTTP, ports localStorage, et services UI sans hiérarchie. Les 3 services bucket-locaux d'analysis/ (ollama-status.service polling, job-stream.service SSE Phase 2.5, llm-timeout.service signal) explicitement nommés. (3) Section « Décisions techniques notables — Phase 1+ frontend » — bullet « Ports & adapters léger » réécrit pour décrire les 3 axes (avant : core/<name>.repository.ts + core/adapters/<name>.http.ts à plat). (4) SymbolSearchService dans la section market/ > Services applicatifs — ancienne formulation « expose aussi validate(symbol) consommé par WatchlistService.add » remplacée par mention du split en deux beans (SymbolSearchService cache + SymbolValidator @Component séparé, 2026-05-15). Raison : un appel intra-bean this.search() bypasserait le proxy AOP Spring et brûlerait un credit Twelve Data par watchlist.add(). (5) AppConfigService dans la section « Configuration runtime éditable » — mention des trois @Component data classes (SecretsDefaults, DataProvidersDefaults, LlmDefaults, 2026-05-15) qui regroupent les défauts YAML, avec note que market.cache.ttl-minutes reste standalone parce qu'il ne s'agence pas naturellement avec un des trois groupes. Sans ce détail, la phrase « clés API vivaient en @Value » donnait l'impression d'un pattern uniforme alors qu'il a été refactoré en 3 holders.
  • developper.md : section « Ce qui te manque encore après cet onboarding » — bullet conventions hexagonales réécrit pour mentionner les 3 axes (core/api/<bucket>/ HTTP, core/local/<bucket>/ navigateur, core/app-state/ services UI signal). L'ancienne version pointait sur core/<name>.repository.ts et core/adapters/<name>.http.ts — un new-joiner cherchait au mauvais endroit.

projet/

  • journal-livraisons.md : nouvelle entrée standalone en tête (au-dessus de l'entrée Phase 3 « /settings/prompt-preview supprimée ») pour journaliser le refactor core/ 3-axes. Inclut le détail du naming retenu (api/ vs http/ ou remote/, local/ vs client-state/, app-state/ vs ui-state/), la cartographie des 8 buckets sous api/, la note sur le cross-bucket llm-timeout.serviceconfig/config.repository qui reste valide après le move, et le résultat tests (384/384 verts, lint clean). Sans cette entrée, le 1er audit qui suivrait verrait le refactor comme du drift inexpliqué (« où sont passés les fichiers à plat ? »).

Pass 2 — audit post-phase-renumbering + dette cleanup + README HTML

Deuxième pass déclenchée par /doc-maintainer après le chantier de l'après-midi : (a) renumérotage des phases — extraction nouvelle Phase 4 « Authentification » (OAuth2, ex-Phase 5), Phase 5 reste « Déploiement » (sans OAuth2), Phase 6 nouvelle = ancienne Phase 4 « Vision long terme / DAG » ; (b) nettoyage substantiel de la Dette technique (démotions 🟡 → 🟢 Stratégie de cache + Promise/Signal, freezes ⏳ → 🧊 News inline v2 + Traduction EN, rétrécissement scope testeur.md, closure formelle du ticket « Refonte .claude/ skills backend » déplacé en journal-livraisons.md > Dette technique) ; (c) conversion de tous les liens du README.md en HTML <a target="_blank" rel="noopener"> (4 badges CI + 3 badges qualité + 1 lien site complet + 13 entrées table doc). L'audit a sorti 10 findings (4 HIGH + 3 MED + 3 LOW + 2 [?]). Patch tout retenu par l'utilisateur ; les 2 [?] (TickerNarrativeExecutor + NarrativeObservabilityService) validés sur lecture du code — les deux fichiers existent en backend/src/main/kotlin/com/portfolioai/analysis/application/. Finding LOW #8 (providers.md Anthropic model claim claude-opus-4-6) filtré : confirmé comme défaut dans backend/src/main/resources/application.yml:48.

projet/

  • backlog.md : 4 corrections forward-looking sur des refs « Phase 4 » qui pointaient encore vers l'ancien contenu DAG (maintenant Phase 6). (1) Ligne 20, Phase 0 ❌ décommissionné : « Replacement Phase 4 = PortfolioAggregation » → « Replacement Phase 6 » (PortfolioAggregation vit en Phase 6 Vague 1 #2 après le renumérotage). (2) Ligne 44, note chapeau Phase 2.5 : « Phase 3 (observabilité narrative), Phase 4 (DAG unifié + Réintégration Phase 0), Phase 5 (déploiement) » réécrite en « Phase 4 (Authentification), Phase 5 (Déploiement), Phase 6 (DAG unifié + Réintégration Phase 0 + Vision long terme) » pour refléter le nouvel ordre. (3) Ticket Coutures Phase 3 item (2) : « PortfolioAggregation Phase 4 appellera ce service » → « Phase 6 appellera ». (4) Même ticket, trigger note finale : « au sortir de Phase 4 #1 (DAG) » → « Phase 6 #1 (DAG) ». Sans ces 4 fixes, un lecteur qui suit « Phase 4 » dans le texte tombe sur l'Authentification au lieu du DAG.

technique/

  • architecture.md : section « Modèle pipeline d'analyse » bloc Statut — référence « (« Page Jobs » Phase 3 + « Réintégration Phase 0 » Phase 6) » réécrite pour pointer vers les 4 tickets Phase 6 explicitement (« DAG unifié » Vague 1 #1, « Réintégration Phase 0 » Vague 1 #2, « Page Jobs » Vague 1 #3, « Cron pré-chauffe » Vague 2 #4). Page Jobs a été déplacée de Phase 3 → Phase 6 lors du renumérotage ; sans ce fix, un lecteur cherche le ticket sous Phase 3 (closée 2026-05-14).
  • ops.md : section « Roadmap CI / ops » — bullet « Merge queue » droppé. Le bullet pointait vers le backlog mais aucun ticket « Merge queue » n'y existe (vérifié via grep). Item fantôme qui mentait au lecteur. Cache Vitest reste (présent dans la Dette 🟢 Basse).

metier/

  • vision.md : section « Ce que cela implique pour les phases à venir » — 3 bullets réécrits pour rétablir l'ordre temporel honnête après le renumérotage. (1) Bullet Phase 3 : clarifié que la phase est clôturée 2026-05-14 (timeline + cohérence + biais) ET que la « Page Jobs » initialement projetée ici a été déplacée en Phase 6. (2) Bullet Phase 5 : reformulé pour acknowledge que Phase 5 deploy se fait avant le DAG (qui n'existe pas encore) ; les bénéfices DAG (cron, cache-hit pur) ne s'appliquent pas à Phase 5. (3) Bullet Phase 6 : étendu pour couvrir la « Réintégration Phase 0 » ET les bénéfices DAG en prod (chaque feuille retryable, durée bornée par le cache, coût LLM prévisible, cron daily pré-chauffe Vague 2 #4). Sans cette restructure, le bullet Phase 5 affirmait que « le DAG facilite aussi la prod » alors que la prod arrive en Phase 5 et le DAG en Phase 6 — drift temporel introduit par le renumérotage de l'après-midi.
  • fonctionnalites.md : section Phase 6 enrichie de 4 items manquants — DAG d'analyse unifié (fondation), Page Jobs (DAG) (UI au-dessus), Cron pré-chauffe quotidienne (consumer non-UI), Fine-tuning (R&D long terme). Ajout d'un paragraphe d'intro qui pose le DAG comme primitif fondateur des autres items et pointe vers architecture.md > Modèle pipeline d'analyse. Annotation « (indépendant du DAG) » sur Paper trading, Multi-broker, Fine-tuning pour clarifier qu'eux ne dépendent pas du DAG. Sans cet enrichissement, fonctionnalites.md > Phase 6 listait seulement 5 bullets sur les 9 tickets que backlog.md > Phase 6 contient.

Pass 3 — dry-run du nouveau /code-review sur le diff du jour

Après la livraison du subagent code-reviewer (clôture du ticket dette 🟡, cf. journal-livraisons.md ligne 192), le slash command /code-review a été testé en méta-review du diff de la journée. Le harness ne reload pas les .claude/agents/*.md en cours de session — code-reviewer n'est donc pas encore invocable par subagent_type, ce qui a forcé un dry-run via general-purpose avec le system prompt du code-reviewer.md passé en brief. Verdict du dry-run : mergeable, 0 bloquant, mais 6+ findings « À discuter » sur des refs forward-looking « Phase 4 » qui auraient dû être renumérotés en Phase 6 dans journal-livraisons.md (le doc-maintainer Pass 2 les avait classés « historiques immuables » mais le code-reviewer remet en cause cette classification par cohérence avec le backlog déjà patché). Plus un compteur erroné « 11 skills » dans l'entrée code-reviewer elle-même alors que la liste qui suit en énumère 10. Patches appliqués :

projet/

  • journal-livraisons.md : 6 refs « Phase 4 » corrigées en « Phase 6 » dans 5 entrées datées 2026-05-07 → 2026-05-14 — toutes pointaient vers le DAG unifié / PortfolioAggregation / page Jobs DAG / page Positions historiques, qui vivent en Phase 6 depuis le renumérotage de l'après-midi. Décision tranchée par le dry-run : les forward-looking refs dans le journal doivent rester cohérentes avec le backlog vivant (sinon asymétrie qui crée du drift à chaque lecture future). Les références purement historiques (« filed comme Phase 3 #4 », « clôturée 2026-05-14 ») restent intactes — elles décrivent un état passé, pas une intention future. Compteur « 11 skills » → « 10 skills » dans l'entrée 192 (énumération qui suit dénombre 10 entrées).

.claude/

  • agents/code-reviewer.md : 3 améliorations drift-driven sortie du dry-run. (1) Périmètres adjacents — nouvelle section qui explicite ce que le code-reviewer ne couvre pas : drift rédactionnel pur du doc-set (périmètre doc-maintainer), refs forward-looking dans les fichiers historiques (journal-livraisons.md + audits/*), audit de messages de commit non encore créés. Sans cette section, le code-reviewer avait dû arbitrer seul ces cas et avait failli les classer en Bloquant. (2) Grep tool nudge — dans la section « Restriction Bash », ajout d'un nudge explicite : « pour un scan cross-files multi-fichiers, un seul appel Grep avec glob couvre la même surface qu'un grep -rn Bash et reste autorisé — préfère-le au réflexe Bash ». Le dry-run avait confessé avoir utilisé grep -rn Bash plusieurs fois faute d'incitation à passer par le tool dédié. (3) Exception gros diffs — nouveau paragraphe qui décrit le fallback quand git diff HEAD <file> excède la limite du sandbox (Read direct du fichier actuel + inférence, ou découpe par hunk via git diff | head -300). Sans ce paragraphe, l'agent serait bloqué sur un refacto > 500 lignes. (4) Ambiguïté @Lazy self : reformulation de la ligne de drift « bypass AOP » qui contradisait elle-même sa propre recommandation (« passer par @Lazy self (mais @Lazy self est déprécié) ») — réécrit en « La correction est un bean séparé ; le pattern @Lazy self est explicitement déprécié dans le projet (cf. spring-boot/SKILL.md ticket B3) ». Une seule recommandation, claire.

Pass 4 — patches pré-tag v0.5.1 (audit 2026-05-16-pre-v0.5.1)

Audit de release lancé en fin de journée pour cadrer le tag v0.5.1 (delta de 19 commits / 213 fichiers / +3 765 / −2 382 depuis v0.5.0). 3 subagents general-purpose en parallèle sur les tranches backend / frontend / .claude/ + doc-set. Sortie consolidée dans docs/projet/audits/2026-05-16-pre-v0.5.1.md (premier audit pré-tag du repo — les précédents étaient post-phase). 2 Critiques + 9 Important + ~14 Mineurs + 11 faux positifs filtrés. Choix utilisateur : « patch les 2 Critiques + 3 Important doc-set », tag rapide propre, 6 Important restants filed en backlog dette 🟢.

Racine

  • README.md : ajout d'une row Sources de données (URL MkDocs https://jv3n.github.io/trade/projet/sources/) entre « Journal des livraisons » et « Conventions de commit ». La page sources.md existait dans mkdocs.yml > nav depuis l'origine du projet mais n'avait jamais été listée dans la table doc du README — orpheline du point d'entrée public. Audit subagent l'a remonté en Important par cohérence avec la règle doc-maintainer.md:52 (« nav additions might warrant a README row too »).

technique/

  • architecture.md : 2 occurrences dette technique 🟡 ouverte/ouvert sur la stratégie de cache hétérogène réécrites en dette technique 🟢 Basse — démoté 2026-05-16, cosmétique (lignes ~198 et ~254). Le ticket a été démoté ce matin dans le backlog.md > Dette technique mais le statut n'avait pas été propagé. Un lecteur de architecture.md aurait cru l'effort nécessaire à court terme alors que le backlog l'a explicitement repoussé.

.claude/

  • skills/spring-boot/SKILL.md:268 : ref « Phase 4 introduces more on aggregation/ » → « Phase 6 ». Le skill avait été écrit avant le renumérotage des phases du jour ; aggregation/ vit en Phase 6 Vague 1 #2 maintenant. Le skill est un fichier « vivant » selon la convention code-reviewer.md:98 (à patcher si périmé, par opposition aux audits historiques).

Code patché en parallèle (non-doc, pour traçabilité)

  • Backend Critique : shared/GlobalExceptionHandler.kt:39 — body 503 "Données de marché momentanément indisponibles""Données momentanément indisponibles". Régression bénigne mais visible utilisateur après le rename B2 MarketUnavailableException → UpstreamUnavailableException : l'exception couvre maintenant 5 providers (market + news + analyst + earnings + LLM) mais le 503 mentait « marché » sur tous. Le journal-livraisons B2 disait pourtant « message inchangé » — c'est le journal qui s'est trompé sur le verbe, pas le code qui l'a raté.
  • Frontend Critique : features/settings/configuration/configuration.ts:288 + :323 — ajout de .subscribe() aux deux callsites de this.timeoutService.refresh(). Régression runtime silencieuse introduite par 22fa6f5 (bascule Promise → Observable) : refresh() cold sans subscribe = no-op, le label « estimation max » sous le slider llm.timeout-seconds restait figé sur la valeur de boot jusqu'au prochain reload. Le test masquait le bug — il vérifiait toHaveBeenCalledTimes(1) sans pin la subscription. Test durci dans configuration.spec.ts avec un Subject<void> sentinel qui vérifie refreshSubject.observed === true post-action, et mock timeoutServiceMock.refresh migré de vi.fn().mockResolvedValue(undefined) (Promise) à vi.fn(() => of(undefined)) (Observable) pour aligner test et runtime.

projet/

  • backlog.md Dette technique 🟢 : nouveau ticket « Coutures post-audit pré-v0.5.1 — 6 Important non patchés (2026-05-16) » qui file les 6 findings Important restants (3 backend : Clock seam, asymétrie 404 Ollama pullModel, @Suppress watchlist ; 3 frontend : effect() injection context contraint, pas de spec snapshot.repository.ts dédié, mocks Ollama Promise→Observable). Description self-contained pour ne pas dépendre de l'audit, mais pointer vers audits/2026-05-16-pre-v0.5.1.md pour le contexte complet. Ouvert pour attaque opportunistique en lots de 30 min, ou en 1 session de ~45 min. Inséré en tête de la section 🟢 par convention « items récents en haut ».
  • audits/2026-05-16-pre-v0.5.1.md (nouveau fichier) : audit consolidé des 3 subagents — scope, méthode, résumé exécutif, 3 sections (Backend / Frontend / .claude/ + doc-set) chacune avec Critique / Important / Mineur / Faux positifs filtrés / Verdict, plus un Verdict global + action plan + décisions retenues du dry-run code-reviewer (les contrats « journal + audits historiques », « skills vivants », « frontière code-reviewer ↔ doc-maintainer »).
  • audits/index.md : nouvelle row en tête de l'historique pointant vers 2026-05-16-pre-v0.5.1.md avec un résumé 1-ligne du périmètre.

2026-05-14 (audit doc-maintainer post-clôture Phase 3 + audit code globale 2026-05-14-fin-phase-3)

Patch session déclenchée par /doc-maintainer après la clôture Phase 3 (4 livraisons en 5 jours : foundation prompt management 2026-05-10, page observabilité 2026-05-13, score de cohérence + détection de biais 2026-05-14). L'audit a sorti 9 findings (4 HIGH + 3 MED + 2 LOW), tous concentrés sur un même cluster : les livraisons #2 (cohérence) et #3 (biais) du 2026-05-14 propagées dans le code mais pas dans les docs de référence (fonctionnalites.md Phase 3 ⏳ alors que livré, architecture.md section analysis/ qui ne mentionne ni CoherenceScorer ni NarrativeBiasService, developpement.md compteur 12 alors que vrai = 14, mkdocs.yml nav qui n'inclut ni l'audit 2026-05-10 ni 2026-05-14). 7 findings patchés (4 HIGH + 1 MED + 1 LOW + bonus arborescence ASCII features/ qui manquait observability/) ; 2 MED filtrés comme faux positifs après vérification (mkdocs sources.md déjà présent ligne 25, README table observabilité = décision éditoriale puisque la feature est documentée dans architecture.md + fonctionnalites.md qui sont déjà linkés et qu'aucun observability.md standalone n'existe). En parallèle, l'audit code globale 2026-05-14-fin-phase-3.md a été posé dans docs/projet/audits/ (3 subagents general-purpose en parallèle, 1 bloquant patché en session — lookupOrFallback qui cachait la FALLBACK_TEMPLATE, 1 important filed dette tech 🟡 — priceAtOrAfter borne basse, 1 umbrella 🟢 pour les 8 résidus mineurs).

metier/

  • fonctionnalites.md : section Phase 3 — chapeau réécrit au passé (« Phase 3 clôturée 2026-05-14 — tag candidat v0.5.0 ») ; les 2 entrées ⏳ « Score de cohérence narrative » et « Détection de biais récurrents » flippées en ✅ Livré avec une description chacune (chip cohérence + tooltip 5 lignes, page /observability/bias avec 4 sections), nouvelle section 🧊 « Reporté Phase 4 » qui héberge le ticket Page Jobs DAG déplacé en Phase 4 (justification : bloqué sur le DAG unifié, son intérêt repose sur les primitives multi-kind / parent-child / cache-aware qui n'existent pas en Phase 3).

technique/

  • architecture.md : (1) section analysis/ — 2 nouveaux paragraphes inline pour les livraisons #2 et #3 du 2026-05-14, qui décrivent le CoherenceScorer (heuristique pondérée sentiment 0.55 / keypoints 0.30 / length 0.15 discountée par price move, pas de LLM-as-judge) et le NarrativeBiasService (3 round-trips SQL + calibration one-fetch-per-symbol + topic coverage regex [a-z][a-z0-9]* + ~80 stopwords + count-by-snapshot). Sans cet ajout, un dev qui lisait architecture.md après Phase 3 close pensait qu'il manquait la moitié de la surface livrée. (2) Inventaire endpoints REST narrative — extension de la ligne /api/narrative/observability pour inclure GET /bias, avec note sur la précédence routing (déclaré avant /{symbol} sinon Spring lierait bias comme path-variable). (3) Compteur frontend repositories bumpé 13 → 14 + ajout explicite de NarrativeBias à la liste avec sa description (agrégats corpus narratif en 4 sections), phrase « regroupe les 12 bindings » alignée à 14. (4) Section features/observability/ étendue : énumère désormais les 3 routes (/observability index avec chip biais, /observability/:symbol timeline + chip cohérence, /observability/bias 4 sections cards) et mentionne l'entrée navbar « Observabilité » ajoutée après Dashboard.
  • developpement.md : (1) arborescence ASCII core/ — commentaire « 12 repositories » bumpé à 14, liste explicite des ports étendue de NarrativeFeedback jusqu'à NarrativeBias (les 2 ports Phase 3 #1 et #3 manquaient), commentaire providers.ts aligné « wires les 14 ports → adapters ». (2) Arborescence ASCII features/ — ajout d'une entrée observability/ qui manquait alors que la feature est livrée depuis Phase 3 #1 (2026-05-13), avec courte description « index symbols, timeline narratif vs prix par ticker (#1) + chip cohérence (#2), bias dashboard (#3) ». (3) Dégraissage doublons vs developper.md (drift signalé par l'utilisateur post-audit) : retrait de la section « Prérequis » (Docker / Java 21 / Node 24 / Tilt) qui dupliquait developper.md — ce fichier est désormais explicitement une référence quotidienne qui suppose les prérequis déjà installés, et une note d'intro pointe vers developper.md pour le first-time onboarding. Retrait du bloc YAML application-local.yml minimal (anthropic / llm / ollama) qui dupliquait le détail narratif de developper.md > Configurer le LLMdevelopper.md reste single-source du first-time setup, developpement.md ne garde que la note « Performance Ollama sur Mac » (qui est une référence opérationnelle, pas du setup) et l'« Alternative runtime via /settings/configuration » (édition sans reboot, daily ops).
  • developper.md : retrait du bloc complet .env Conflit de port (procédure + table 4 variables + note gitignored) qui dupliquait developpement.md — remplacé par un redirect compact qui pointe vers la section Conflit de port de developpement.md (single-source canonical). Le reste du fichier (60-sec, prereqs, tilt up, configurer LLM, premier test, switcher providers, quand ça merde, pour aller plus loin) reste intact — c'est l'onboarding narratif. Convention de propriété (developper = first-time, developpement = daily reference) ancrée dans les intros respectives.

projet/

  • audits/2026-05-14-fin-phase-3.md : nouveau fichier — rapport d'audit code globale fin Phase 3 (avant tag candidat v0.5.0). Format mirror du 2026-05-10-fin-phase-2.5.md : scope + méthode (3 subagents general-purpose en parallèle sur des slices cohérentes) + état du commit, résumé exécutif, sections par sévérité (Critique / Important / Modérée / Mineur), Forces remarquables, Résidus audits précédents, Recommandation tag. Concrètement : 1 bloquant FALLBACK_TEMPLATE caching patché en session avec test de régression, 1 important priceAtOrAfter borne basse filed dette tech 🟡, 8 mineurs filed dans une umbrella 🟢, 1 faux positif filtré (claim agent sur priceMove BigDecimal scale qui n'existe pas).
  • audits/index.md : nouvelle entrée en tête de l'historique pour pointer vers 2026-05-14-fin-phase-3.md.
  • backlog.md : (1) section Dette technique — 2 nouveaux tickets ajoutés en sortie de l'audit code 2026-05-14 : 🟡 « Bar-lookup priceAtOrAfter — guard la borne basse » (vrai bug à terme quand le corpus aura 1+ an d'histoire), 🟢 umbrella « Coutures post-livraison Phase 3 — résidus review 2026-05-14 » qui regroupe 9 sous-items (race thumbs PromptScore théorique, calibration fan-out séquentiel, bias flag rounding edge, V8 backfill claim, LIMIT interpolé vs bound, stopwords drift, duplications buildFilter+nextDayIso et mapper jacksonObjectMapper et normalizeInstant). (2) Phase 3 marquée close 2026-05-14, tag candidat v0.5.0. (3) Phase 4 réordonnée en 4 vagues séquencées : DAG foundation (DAG unifié → Réintégration Phase 0 → Page Jobs déplacée depuis Phase 3) → Extensions DAG (Cron pré-chauffe) → Surfaces dashboard (Croisement portfolio × insights, Watchlist alertes) → R&D long terme (Paper trading, Multi-broker, Fine-tuning). Justification : livrer le DAG sans consumer concret = code sans vie, livrer le consumer sans Page Jobs = aucun moyen d'observer.

Racine

  • mkdocs.yml : section Projet > Audits — ajout des entrées 2026-05-14 — Revue globale fin Phase 3 et 2026-05-10 — Revue globale fin Phase 2.5 qui manquaient au nav. Sans ce sync, les liens depuis audits/index.md produisaient un 404 sur le site MkDocs hébergé (MkDocs en mode nav: explicite ne génère pas de page pour un fichier non listé).

.claude/

  • CLAUDE.md : section observability/ du tableau Frontend modules — paragraphe étendu pour inclure les livraisons Phase 3 #2 (chip cohérence vs précédent OK/WARN/HIGH avec tooltip natif 5 lignes) et #3 (sous-dossier bias/ qui rend /observability/bias avec 4 sections cards + chip vers le bias depuis la page index), mention de l'entrée navbar « Observabilité » ajoutée après Dashboard. Sans cet ajout, la section décrivait seulement Phase 3 #1 alors que la feature couvre désormais les 3 sous-livraisons.

2026-05-11 (audit doc-maintainer post-livraison Phase 3 prompt-management)

Patch session déclenchée par /doc-maintainer après la livraison des 6 sous-PRs Phase 3 « Prompt management + scoring » (V8 Flyway + prompt_template + prompt_score + endpoints /api/prompts + page /settings/prompts + thumbs /api/narrative/snapshots/{id}/thumbs + page stats /settings/prompts/{id}/stats). Le doc set avait déjà été mis à jour côté roadmap dans la matinée (backlog.md Phase 3 #1 retiré, journal-livraisons.md entrée détaillée Phase 3, architecture.md table BDD + paragraphe prompt-management, fonctionnalites.md Phase 3 ✅/⏳) mais les inventaires (compteurs repositories frontend, liste des routes settings, table Bounded Contexts) n'avaient pas suivi. L'audit a sorti 13 findings (4 HIGH + 4 MED + 5 LOW) — les 4 HIGH tous liés au même drift d'inventaire propagé dans trois fichiers de référence (architecture.md, developpement.md, CLAUDE.md). 10 findings patchés ; 3 marqués comme fausses alertes après vérification (thumbs endpoint déjà présent dans la liste structurée d'architecture.md, ancre phase-3--observabilité-narrative cohérente avec les autres ancres de phase déjà éprouvées, guillemets de la ligne 128 fonctionnalites.md qui sont des chevrons français « » corrects et non des guillemets accentués).

metier/

  • fonctionnalites.md : section Phase 0 Décommissionné — phrase qui affirmait que « le sidenav settings garde configuration/ et prompt-preview/ » était périmée depuis Phase 3 ; étendue pour inclure prompts/ et prompts/:id/stats (gestion + scoring des prompts narratifs).

technique/

  • architecture.md : compteur frontend repositories bumpé de 10 → 12 (ajout de Prompt PR3 + NarrativeFeedback PR5) et phrase « regroupe les 10 bindings » alignée à 12. Section features/settings/ étendue pour décrire les deux nouvelles routes /settings/prompts (liste/activate/éditeur+diff) et /settings/prompts/:id/stats (sparkline latence p50 + tableau quotidien) — sans ces ajouts, un dev qui lisait l'architecture après Phase 3 pensait que les settings ne contenaient que configuration/ et prompt-preview/.
  • developpement.md : arborescence ASCII du dossier core/ — commentaire « 10 repositories » et liste explicite des ports mis à jour pour inclure Prompt + NarrativeFeedback ; commentaire providers.ts aligné « wires les 12 ports → adapters ». Dossier settings/ détaillé : ne disait plus que « Configuration runtime + prompt-preview » alors qu'il héberge maintenant 4 routes. Mention de la card ticker enrichie avec les thumbs.
  • ddd.md : ligne analysis du tableau des Bounded Contexts — responsabilité étendue pour mentionner « gestion des prompts narratifs en BDD avec scoring continu (latence / retry / parse-validator failed / thumbs user, Phase 3) », statut bumpé à « Phase 1 — étendu Phase 3 ». Nouvelle sous-section « Prompt management & scoring » ajoutée sous infrastructure/llm/ qui explique pourquoi les prompts vivent dans analysis/ (détail d'implémentation du narratif, table prompt_score jointe à ticker_narrative_snapshot) plutôt qu'en bounded context séparé, et qui liste les 4 artefacts neufs (TickerNarrativePromptService, PromptScoreRecorder, PromptScoreStatsQuery, PromptController + NarrativeThumbsController).
  • ops.md : trois liens absolus github.com/jv3n/trade/blob/master/... (Detekt SARIF, ESLint config, dependabot.yml) remplacés par des chemins relatifs ../../.... Raison : ces liens cassent si le repo est renommé ou rendu privé, et de toute façon naviguer vers le fichier voisin ne nécessite pas un round-trip GitHub. Standardise avec le reste du doc-set qui utilise des liens relatifs.

projet/

  • backlog.md : ticket dette technique « Gestion d'erreur transverse » — retrait d'une mention inline « livraison prompt-management 2026-05-10 » qui mélangeait l'historique des livraisons avec la description de ce qu'il reste à faire (convention « dette = à faire, pas historique »). La trace de la livraison reste dans journal-livraisons.md > Phase 3, là où c'est sa place.

Racine

  • README.md : 14 liens de la table « Documentation » repointés des chemins relatifs docs/<path>.md vers les URLs MkDocs hostées https://jv3n.github.io/trade/<path>/. Rationale : le README est l'entrée publique du repo ; le site MkDocs est la surface canonique de lecture (rendu thème Material, recherche, nav), les .md sont les sources. Pointer vers le .md brut amenait le lecteur GitHub sur le source plutôt que sur la doc rendue. 3 orphelins MkDocs nav repérés mais non ajoutés (choix éditorial laissé à l'utilisateur) : projet/sources.md, projet/audits/index.md, et les fichiers d'audits archivés.

.claude/

  • agents/doc-maintainer.md : ajout de README.md au tableau « Doc set under your responsibility » avec la note explicite « doc-links table vers MkDocs hosted, pas vers les .md relatifs ». Section Cross-check étendue de deux lignes : (1) « README.md — CI badges target real workflows » qui croise les badges contre .github/workflows/*.yml, (2) « README.md — doc-links table uses MkDocs hosted URLs » qui croise la table contre mkdocs.yml > nav (chaque row → URL https://jv3n.github.io/trade/<path-without-.md>/ ; rows MkDocs orphelines du README à flagger ; rows README sans cible MkDocs à flagger). Ajout de deux examples of drift correspondants dans la sous-section « Examples of drift you must catch ». Raison : la livraison Phase 3 a montré que les inventaires (compteurs, listes, routes settings) dérivent vite — le README et son aiguillage public méritent le même filet que les autres docs.
  • skills/doc-maintainer/SKILL.md : default prompt template synchronisé avec le scope élargi de l'agent — README.md ajouté en tête de la liste explicite, et instruction de cross-check « badges CI vers .github/workflows/*.yml + table de liens vers URLs MkDocs hostées, drift dans les deux sens avec mkdocs.yml > nav » ajoutée dans la capacité 1. Sans ce sync, le skill émettait un prompt qui n'instruisait pas l'agent de regarder le README — celui-ci aurait pu le silencieusement ignorer.
  • skills/folders-structure-frontend/SKILL.md : tree ASCII rewrité de zéro après audit. Avant : 4 fictional repos (portfolio.repository.ts, analysis.repository.ts, settings.repository.ts, snapshot.repository.ts — dont 2 inventés analysis.repository.ts et settings.repository.ts qui n'ont jamais existé) + 6 features dont 2 décommissionnées Phase 2.5 (history/, recommendations/) et ticker/ (la feature majeure Phase 1) absente. Après : 12 repositories réels listés explicitement avec leur statut Phase (Phase 1 base, Phase 2 watchlist/news/analyst/earnings, Phase 2.5 OllamaStatus, Phase 3 Prompt + NarrativeFeedback), les 2 stateful services (Theme, Language), les 2 cross-feature helpers (JobStream SSE Phase 2.5, LlmTimeout), providers.ts mentionné comme wiring central, et les 5 features actuelles (dashboard, ticker, import, suivi, settings) avec courte description par feature. Le settings/ détaillé : 4 routes courantes (configuration, prompt-preview, prompts, prompts/:id/stats).
  • skills/angular-component/SKILL.md + instructions/frontend/best-practices.md : retrait du mandat ChangeDetectionStrategy.OnPush qui contredisait directement CLAUDE.md (« Default change detection strategy is fine, no need to add OnPush everywhere »). Rationale ajouté en place : en mode zoneless (provideZonelessChangeDetection() dans app.config.ts, en place depuis le début), il n'y a plus de Zone tracking à supprimer ; les signaux pilotent la change detection manuellement, OnPush devient un no-op fonctionnel qui ajoute du bookkeeping. Conséquence d'ajustement : code snippet du skill perd l'import ChangeDetectionStrategy et la propriété changeDetection: sur le @Component({}), le frontmatter description: retire « OnPush change detection » de la liste de triggers.
  • skills/angular-testing/SKILL.md : description retire « OnPush change detection » des cas d'usage listés. Section « Testing OnPush Components » conservée mais préfacée d'une note explicite qui pin la convention zoneless du projet et précise que la section reste comme référence pour le cas rare où un composant opt-in OnPush localement.
  • CLAUDE.md : trois zones synchronisées avec la livraison Phase 3. (1) Paragraphe analysis/ du tableau « Backend modules » étendu d'un bloc « Prompt management (Phase 3, livré 2026-05-10) » qui décrit le TickerNarrativePromptService (lookup BDD + cache 1 min + fallback hardcodé), le PromptScoreRecorder (row à chaque run, succès ou échec), et les endpoints /api/prompts + PATCH /api/narrative/snapshots/{id}/thumbs. (2) Section Frontend modules > core/ — compteur « Currently 10 repositories » corrigé en 12 avec ajout explicite de Prompt et NarrativeFeedback. (3) Section Features > settings/ — sidenav décrit avec ses 4 routes actuelles au lieu de 2 (configuration/, prompt-preview/, prompts/ Phase 3, prompts/:id/stats Phase 3).

2026-05-10 (audit doc-maintainer post-tag v0.4.0, clôture Phase 2.5)

Patch session déclenchée par /doc-maintainer juste après la pose du tag v0.4.0 (Phase 2.5 close). L'audit a sorti 14 findings (3 HIGH + 4 MED + 7 LOW) — cluster centré sur les drifts introduits par les 14 commits de la Phase 2.5 (instrumentType end-to-end / V7, refacto WatchlistService.add hors @Transactional, audit follow-up, sweep Security/Quality detekt). Les 3 HIGH + 4 MED + 5 LOW patchés. Deux LOW non actionnés : (a) architecture.md ligne 284 — référence textuelle vers vision.md > Le pipeline d'analyse, le slug Markdown contient une apostrophe et casserait si transformé en lien cliquable, mais aujourd'hui c'est une référence textuelle ; (b) ddd.md lignes 86-99 — l'audit signalait un mix tirets cadratin / simples sur les blocs infrastructure/analyst/ + infrastructure/earnings/ mais relecture ciblée n'a pas pinpointé d'incohérence concrète vs le voisin infrastructure/market/ ; reportée au prochain audit si elle se confirme.

metier/

  • fonctionnalites.md : section Phase 2 v2 « Comparaison vs benchmark » — ajout d'une mention inline que TwelveDataSectorClassifier a été décommissionné le 2026-05-06 et remplacé par FinnhubSectorClassifier (/profile Twelve Data paid-tier only). Le chapeau de fin de phase 2 réécrit pour pointer la Phase 2.5 close (tag v0.4.0, 2026-05-10) plutôt que lister à la main les sujets restants — la liste précédente était figée à un moment intermédiaire et tous les items mentionnés sont depuis livrés.

technique/

  • architecture.md : section « Schéma de base de données » — bumpée de « Six migrations Flyway » à « Sept migrations » avec ajout de V7 (watchlist_instrument_type.sql, Phase 2.5) qui ajoute la colonne instrument_type VARCHAR(20) nullable sur watchlist_entry pour persister le type capturé au POST add. Section config/ > Switch provider à chaud — la phrase « Cache key préfixée par adapter ⇒ pas de collision » remplacée par un paragraphe honnête qui distingue le MARKET_CHART_CACHE (préfixé) des quatre autres caches (news-by-symbol, analyst-recommendations, earnings, sector-by-symbol, sans préfixe → staleness ~15 min post-switch). Le précédent claim datait de Phase 2 fin et avait survécu à un audit Phase 2 finding #21 sans correction. La parenthèse « (Note : RoutingSectorClassifier ... détail caché derrière le routing) » réécrite en texte direct cohérent avec le reste du bullet. Section watchlist/ : rationale ajouté pour expliquer pourquoi WatchlistService.add est délibérément non-@Transactional depuis le commit a608967 (deux appels réseau hors Hikari, helper privé persistNew avec re-check TOCTOU + UNIQUE constraint comme filet).
  • developper.md : section « Un job narratif reste bloqué PENDING » — phrase « Cleanup automatique au boot prévu en dette technique » remplacée par un constat factuel — OrphanedJobCleanupListener est en place depuis Phase 1 et flippe les PENDING orphelins en ERROR au boot, donc un redémarrage suffit à débloquer l'UI sans intervention manuelle. La doc prétendait que c'était à faire, ça l'induisait en erreur.
  • ddd.md : section domain/ — exemple d'enums métier remplace RecommendationStatus (Phase 0 décommissionné en V6, fichier supprimé) par InstrumentType + EarningsTime (Phase 2, vivants).
  • ops.md : description de la décision WildcardImport désactivée — le commit cité « via Spotless+ktlint » corrigé en « custom step Spotless » avec un pointeur vers architecture.md > Décisions techniques notables pour le rationale du rollback ktlint (incohérent avec la décision documentée à cet endroit-là, qui précise « volontairement pas de ktlint »).
  • providers.md : table Anthropic — modèle de fallback noté claude-sonnet-4-5 corrigé en claude-sonnet-4-6 (alignement avec la nomenclature courante de la génération Sonnet, mirror de la version Opus configurée par défaut).

projet/

  • backlog.md : chapeau Phase 2.5 — étendu pour inclure « persistence instrumentType BDD V7 (drop du lazy-lookup burst Twelve Data) » et la session « retour audit fin Phase 2.5 » (4 findings clôturés en un commit avant le tag), tag v0.4.0 mentionné dans le titre. Le ticket dette 🟢 Basse « Sweep doc drifts mineurs post-Phase 2.5 » narrowé : le finding developper.md:195 cleanup orphelins ferme via cette même session, reste seulement le sous-finding ConfigTestClient.kt:225 (mention « Tilt button » obsolète dans un message user-visible) — code change, sera pris dans un commit ultérieur.

2026-05-09 (audit doc-maintainer post-livraison SSE)

Patch session déclenchée par /doc-maintainer après la livraison du ticket SSE en 4 commits sur la branche events (PR1 backend SSE + PR2 frontend bascule + PR2.5 reattach pending + PR3 UX phase visible). L'audit a sorti 14 findings (5 HIGH + 5 MED + 4 LOW) — cluster cohérent autour du périmètre SSE qui n'avait pas été propagé dans architecture.md ni developper.md. Tous les findings ont été appliqués sauf le LOW « ddd.md infrastructure/llm narratif vs bullets » jugé borderline (consistant avec les sections voisines infrastructure/market/ qui suivent le même pattern paragraphe + bullets).

technique/

  • architecture.md : section analysis/ ré-écrite — l'item « Job tracking pour le polling front » devient « pour le suivi du job côté front (transport SSE depuis Phase 2.5) », ajout d'un nouvel item dédié aux 4 artefacts SSE (JobPhase enum 9 valeurs, JobEvent data class, JobEventPublisher pub/sub avec replay 60 s post-terminal, endpoint GET /jobs/{id}/stream), ajout d'un item liste des 6 endpoints REST narratifs incluant le nouveau GET /jobs/pending (PR2.5 reattach). Section config/ : retrait des références frontend POLL_ABORT_SECONDS / NARRATIVE_POLL_ABORT_SECONDS (constantes supprimées en PR2) ; la phrase « lu par le poller market.http.ts (le seul poller restant) » devient « consommé par les surfaces UI qui affichent l'estimation max — depuis la migration SSE, plus d'abort de poller ». Section Modules frontend > core/ : 9 → 10 repositories, ajout de OllamaStatus dans la liste, mention de JobStreamService comme pièce non-repository (pas de DTO BDD à exposer). Section « Modèle pipeline d'analyse > Implications côté frontend » : « Stream live via SSE ou polling 2-3 s » devient « via SSE — l'infra de transport est déjà en place pour les jobs ticker, la page Jobs n'aura qu'à consommer les mêmes événements à l'échelle d'un DAG ».
  • developper.md : section « Générer un narratif LLM » réécrite — le frontend n'« poll plus toutes les 5s », il « ouvre un EventSource sur /jobs/{id}/stream » et reçoit les transitions LOADING_CONTEXT → … → DONE / ERROR en push avec compteur de secondes par phase (le bandeau de progression livré en PR3). La phrase « switcher LLM provider en éditant application-local.yml — Tilt redémarre au save » est étendue pour mentionner aussi /settings/configuration > LLM (rotation runtime sans reboot, plus pratique au quotidien).
  • developpement.md : arborescence core/ mise à jour — 9 → 10 repositories avec OllamaStatus dans la liste, ajout de job-stream.service.ts (Phase 2.5 SSE) à la liste des fichiers du dossier.
  • ddd.md : table « Conventions de nommage » ligne « DTO entrée » — exemple UpdateSourceEnabledRequest (DTO Phase 0 décommissionné en V6) remplacé par UnloadModelRequest + UpdateConfigRequest (DTO vivants).

projet/

  • backlog.md : légende « Statuts » nettoyée — ✅ Fait retiré (les ✅ vivent dans journal-livraisons.md depuis le split, plus aucun dans le backlog). Chapeau Phase 2.5 étendu pour refléter la densité réelle de la phase (Swagger UI, .env ports, panneau État Ollama, eject VRAM, clé Anthropic SECRET, drag-drop portfolios, décision Ollama statu quo, SSE en 4 PR) — la liste précédente s'arrêtait au 2026-05-07 et omettait toutes les livraisons des 2026-05-08 / 09.

.claude/

  • CLAUDE.md : section analysis/ — la phrase « doesn't leave the front polling forever » devient « doesn't leave the frontend SSE waiting indefinitely » (transport stale), ajout d'une mention du JobEventPublisher + endpoint /jobs/{id}/stream qui surfacent la progression per-phase. Section Frontend modules > core/ : 9 → 10 repositories avec OllamaStatus, mention de JobStreamService (Phase 2.5 SSE), reformulation du rôle de LlmTimeoutService (« used today only for the estimated max label on the LLM card — the legacy poll-abort path is gone since the SSE migration »).

2026-05-08 (suite 2 — ports stack locale configurables via .env)

Patch onboarding suite à une friction observée : un testeur a cloné le repo et tilt up a échoué sur « port already allocated » parce que son Postgres système occupait 5432. Décision : sortir tous les ports hôte de la stack (Postgres / Ollama / backend / frontend) dans un fichier .env à la racine, gitignored, avec .env.example comme template versionné. La doc d'onboarding et le guide dev pointent désormais explicitement la procédure.

technique/

  • developper.md : nouvelle sous-section « Conflit de port avec un autre service local » sous « Premier lancement » — procédure cp .env.example .env + édition + tilt up, mention des 4 vars (POSTGRES_HOST_PORT / OLLAMA_HOST_PORT / BACKEND_HOST_PORT / FRONTEND_HOST_PORT) et de leur fallback automatique. Le bon endroit pour cette friction parce que c'est l'onboarding qui bute, pas le dev contributeur.
  • developpement.md : nouvelle sous-section « Conflit de port » sous « Démarrage » avec tableau récapitulatif des 4 variables + leurs défauts. Plus détaillée que la version developper.md (mention de la mécanique d'injection via serve_cmd côté Tilt) parce que ce fichier est lu par les contributeurs.

Racine

  • .env.example : nouveau fichier — template documenté des 4 ports configurables. Listé en haut du repo pour découvrabilité, gitignored via le pattern .env* + !.env.example déjà en place.
  • CHANGELOG.md : cette entrée elle-même.

2026-05-08 (suite — patch audit /doc-maintainer 12 findings)

Patch doc-set en sortie de session quick-wins. L'audit /doc-maintainer lancé après l'ajout du folder docs/devops/ au mkdocs.yml a remonté 13 findings (4 HIGH, 5 MED, 4 LOW), tous patchés (12 effectifs — finding #8 « note Cleanup automatique au boot prévu en dette technique dans developpement.md » s'est avéré être un false positive de l'agent : la note n'existe pas dans le fichier, qui ne fait que 146 lignes alors que l'agent référençait « ligne 183 »). Drift concentré sur deux clusters : (1) la livraison Anthropic SECRET runtime de la session précédente n'avait pas été propagée dans developpement.md / architecture.md / fonctionnalites.md / providers.md (compteurs « 7 clés » / « 11 clés » périmés, pas de mention de la card UI ni de POST /api/config/test/anthropic) ; (2) le décommissionnement Phase 0 (V6 du 2026-05-07) avait laissé des résidus dans commandes-pratiques.md (commandes SQL sur tables droppées) et ddd.md (décrit le LLM comme @ConditionalOnProperty alors que RoutingLlmClient @Primary existe depuis Phase 2.5 v1).

metier/

  • fonctionnalites.md : Settings & config runtime — « sept clés » → « douze clés », structuration en deux sub-sections Providers / LLM, mention explicite des extensions Phase 2.5 (toggle llm.provider, modèles autocomplete, slider timeout, clé anthropic.api.key SECRET).

technique/

  • architecture.md : config/ — « onze clés » → « douze clés », ajout de anthropic.api.key à la liste des SECRETs masqués. ConfigController — endpoint /test/{provider} étendu à anthropic. ConfigTestClient — mention du testAnthropicKey(candidate). Lecture per-call dans les adapters — ClaudeClient ajouté au trio (Twelve Data + Finnhub + Claude), avec note sur le pattern header per-request vs defaultHeader() builder. LLM provider+model runtime — mention que la clé Anthropic suit désormais le même pattern. LLM timeout runtime — retrait de AnalysisJobStore.DEDUP_WINDOW_SECONDS (Phase 0 droppée), seul TickerNarrativeJobStore.pendingFor reste, analysis.http.ts portfolio poller retiré (un seul poller market.http.ts narrative depuis V6).
  • developpement.md : (1) section /settings/configuration — « sept clés » → « douze clés », structuration en 4 catégories (secrets / toggles / strings / sliders) ; (2) arborescence core/(11 repositories)(9 repositories), listing aligné (retrait Analysis et Settings supprimés V6), ajout de providers.ts (provideRepositories()) et mention SSR-safe pour theme.service.ts + language.service.ts.
  • ddd.md : infrastructure/llm/ — réécriture complète de la sélection LLM. Avant : « @ConditionalOnProperty(llm.provider) au boot, pattern hérité Phase 1 conservé tant que LLM pas piloté par AppConfigService ». Après : RoutingLlmClient (@Primary) délègue per-call via appConfig.getString(LLM_PROVIDER), les @ConditionalOnProperty ont été retirés, mention de la lecture per-call de la clé Anthropic (Phase 2.5 v2, 2026-05-08).
  • providers.md : tableau Anthropic — colonne Modèle utilisé clarifiée comme « default YAML » (vs runtime), colonne Config étendue avec « les deux éditables au runtime depuis /settings/configuration > LLM (clé masquée + bouton Tester) », ajout d'une nouvelle ligne Probe documentant POST /api/config/test/anthropic et son round-trip Claude. Code YAML d'exemple — comment pull via Tilt button llm:pull-qwenllm:ensure-model (idempotent).
  • developper.md : tableau « Configurer le LLM » — bouton Tilt llm:pull-qwenllm:ensure-model aligné sur le vrai nom dans le Tiltfile (le developpement.md était déjà aligné, ce fichier portait encore l'ancien nom).

devops/

  • commandes-pratiques.md : (1) titre h1 « Commandes pratiques (devops local) » → « Commandes pratiques » (alignement avec la nav MkDocs, le folder devops/ rend la parenthèse redondante) ; (2) section Postgres — requêtes SQL sur analysis_job (table droppée V6) supprimées, ne reste que ticker_narrative_job ; (3) section « Vider la table RSS legacy (Phase 0 gelée) » — supprimée intégralement (table feed_article droppée, AnalysisExecutor supprimé, ingestion.rss.enabled n'existe plus) ; (4) AnalysisRunner.runTickerNarrativeRunner.run dans la note restart Ollama ; (5) AnalysisExecutor.execute retiré du thread-dump exemple (seul TickerNarrativeExecutor.execute reste depuis V6) ; (6) bouton Tilt UI exemple « llm:pull-qwen » → générique « boutons custom de pull modèle » (le nom exact change peu et llm:ensure-model est documenté ailleurs).

projet/

  • commit-conventions.md : table « Scopes courants » — scopes ingestion et recommendations supprimés (modules droppés V6, plus du gelé). Description du scope settings rafraîchie (« back-office, prompt-preview, configuration runtime » au lieu de « test-sources » qui n'existe plus). Exemple inline feat(ingestion): add RSS fetcher...feat(market): add TwelveDataClient... pour ne plus exhiber un scope mort dans la doc.

Racine

  • README.md : table Documentation — ajout des deux entrées docs/devops/ (commandes-pratiques.md et decision-ollama-deploiement.md). La nav MkDocs avait déjà ces deux fichiers depuis l'édition d'aujourd'hui, mais la page d'accueil du repo restait sans pointeur — cluster Devops désormais découvrable depuis le README.
  • CHANGELOG.md : cette entrée elle-même.

2026-05-08 — suppression etat-actuel.md + alignement doc-maintainer sur le doc-set Phase 2.5 + ADR déploiement Ollama

Trois patches groupés en fin de session quick-wins. (1) docs/projet/etat-actuel.md supprimé : doublon de plus en plus stale du couple journal-livraisons.md + backlog.md ; le user n'y trouvait plus de valeur unique en lecture. (2) Audit manuel de l'agent et du skill doc-maintainer confronté à l'état réel du doc-set : 3 docs Phase 2.5 manquaient à la table de responsabilité (drift HIGH — un audit /doc-maintainer lancé aujourd'hui n'aurait pas vu ces fichiers), et plusieurs exemples factuels du system prompt étaient périmés (drift HIGH — risque de fabrication de findings invalides). (3) Nouveau brouillon ADR docs/devops/decision-ollama-deploiement.md qui formalise les 3 options de déploiement Ollama (Mac dev vs serveur cible) suite à l'incident saturation CPU 9 cores du 2026-05-07. Pas une décision finale — recommandation argumentée pour l'option 3 (statu quo), 3 sous-questions ouvertes en attente d'arbitrage.

projet/

  • etat-actuel.md : fichier supprimé. Remplaçant : journal-livraisons.md pour le narratif livré, backlog.md pour ce qui reste, git log pour l'état exact d'une session passée.
  • backlog.md : entrée Phase 2.5 « Décision design : stratégie déploiement Ollama » passe de (description verbeuse de 3 options) à 🚧 (pointeur vers le nouveau brouillon ADR + reste à faire = arbitrer + implémenter selon option). Réordonnée en tête de la section Phase 2.5 ⏳ per convention CLAUDE.md (🚧 avant ⏳ trié par priorité).

devops/

  • decision-ollama-deploiement.md : nouveau fichier — ADR léger format brouillon. Capture l'incident 2026-05-07 (Ollama saturé 60-180 s en CPU pur sur Mac Docker Desktop, root cause Apple ne laisse pas Metal s'exposer dans la VM Linux), expose les 3 options arbitrables (Ollama natif macOS hors Compose / Compose+override Tilt local / statu quo Claude-first), recommande l'option 3 par défaut tant que Ollama reste un backup occasionnel (< 20 % des sessions), bascule conditionnelle vers option 1 (natif) au-delà. Documente les 3 sous-questions ouvertes (% usage, cible Phase 5, distribution repo) qui doivent être tranchées avant l'implémentation. Tableau des fichiers à éditer selon l'option choisie. Status 🟡 Brouillon jusqu'à arbitrage.

technique/

  • developper.md : section « Pour aller plus loin » — ligne etat-actuel.md retirée (le journal des livraisons couvre déjà la photo de session récente).

Racine

  • README.md : tableau Documentation — ligne « État actuel » remplacée par « Journal des livraisons » (le pointeur manquait), description de la ligne Backlog reformulée pour pointer le scope « ouvert seulement » et le split avec le journal.
  • mkdocs.yml : nav Projet: — entrée État actuel retirée.
  • CHANGELOG.md : cette entrée elle-même.

.claude/

  • agents/doc-maintainer.md : (1) tableau « Doc set under your responsibility » — 3 entrées ajoutées : docs/devops/commandes-pratiques.md (cheatsheet devops local — psql, Tilt, Ollama, jobs LLM bloqués), docs/projet/journal-livraisons.md (reverse-chronological log of shipped features by phase), et description de backlog.md narrowée (« Open work only — ⏳/🚧/🧊/❌ + Dette technique. Shipped features live in journal-livraisons.md. »). (2) Tableau « Cross-check (factual drift) » — exemples avec compteurs hardcoded (8 repositories : Portfolio, Analysis, ..., Phase 1 ✅ terminé, Phase 2 ⏳ en cours, listes de noms) remplacés par des descriptions paramétrées (« Frontend repositories count + listing », « Phase status /🚧/ ») + nouvelle note explicite « Don't trust hardcoded counts in this prompt — re-derive from disk on each run ». (3) Liste « Examples of drift you must catch » — anciennes lignes périmées ("7 repositories" when there are 8, "3 migrations Flyway" when V4 exists) remplacées par des templates abstraits, nouvelle entrée pour pin le drift typique du split backlog ↔ journal-livraisons (ticket qui devrait être déplacé en dans le journal). (4) Section « Output format » — ancien exemple punch-list qui citait Portfolio, Analysis, Settings (deux modules décommissionnés en V6) reformulé en placeholders <count>, <actual>, <noms>. Note : analysis_job reste référencé dans commandes-pratiques.md (table droppée V6) — drift réel que l'agent pourra catcher au prochain run, hors scope de ce patch.
  • skills/doc-maintainer/SKILL.md : (1) prompt template par défaut — scope étendu de docs/projet/* (sauf data-input/...) à inclure aussi docs/devops/* + mention explicite du CHANGELOG.md. (2) Section « After patches are applied — update the CHANGELOG » — liste des areas étendue de metier/, technique/, projet/, .claude/, Racine à inclure devops/.

2026-05-07 (suite 5 — split backlog.md ↔ nouveau journal-livraisons.md + ticket clé Claude SECRET)

Patch doc-set qui exécute le ticket ⏳ Scinder backlog.md filed en 2026-05-07 : docs/projet/backlog.md mélangeait ⏳ À faire (planning) et ✅ Livré (historique) sur 233 lignes — chaque session de planning scrollait à travers des trimestres de livré pour trouver le restant. Le split sépare les deux usages : le backlog ne garde que ce qui est ouvert, le journal devient l'archive narrative reverse-chronological par phase.

projet/

  • journal-livraisons.md : nouveau fichier — toutes les lignes ✅ Livré / ✅ Conservé migrées depuis backlog.md avec leurs notes d'implémentation détaillées intactes. Format reverse-chronological par phase (Phase 2.5 → 2 → 1 → 0 → Dette technique), à l'image de docs/CHANGELOG.md côté doc-set. Sections frontmatter par phase qui rappellent le tag de clôture (v0.1.0, v0.2.0, v0.3.0).
  • backlog.md : rétréci de 233 → 125 lignes. Ne garde que ⏳ À faire, 🚧 En cours, 🧊 Gelé, ❌ Décommissionné et la section Dette technique (uniquement les ). Chaque phase clôturée (0, 1, 2, 2.5) ouvre par un pointeur explicite vers la section correspondante du journal. Le ❌ Décommissionné Phase 0 reste dans le backlog parce qu'il documente l'état courant du code (équivalent 🧊 en spirit) — il est aussi présent dans le journal pour la chronologie. Nouvelle entrée ⏳ /settings/configuration > LLM : exposer la clé API Anthropic (Claude) comme SECRET runtime ajoutée dans Phase 2.5 (parité avec market.twelvedata.api-key et market.finnhub.api-key déjà éditables au runtime ; ClaudeClient + ConfigTestClient lisent la clé via @Value figé aujourd'hui, pattern à aligner sur per-call appConfig.getString(ANTHROPIC_API_KEY)).

technique/

  • developper.md : section « Pour aller plus loin » scindée — ligne backlog.md reformulée pour clarifier qu'elle ne liste que /🚧, nouvelle ligne dédiée au journal des livraisons.

Racine

  • mkdocs.yml : nav Projet: étend avec une nouvelle entrée Journal des livraisons: projet/journal-livraisons.md placée juste après Backlog:.
  • CHANGELOG.md : cette entrée elle-même.

.claude/

  • CLAUDE.md : (1) section ### Backlog réécrite — explique le split en deux fichiers, le workflow « après livraison : entrée nouvelle dans journal-livraisons.md, ligne retirée de backlog.md », et retire l'item « ✅ Livré » de la convention d'ordering puisque le backlog ne contient plus de ✅. (2) Tableau « Documentation » : ligne docs/projet/backlog.md reformulée (« holds only ⏳/🚧/❌/🧊 + Dette technique »), nouvelle ligne docs/projet/journal-livraisons.md insérée juste après. (3) Repository structure tree : commentaire de la ligne projet/ étendu pour mentionner les deux fichiers.

2026-05-07 (suite 4 — décommissionnement Phase 0 : drop des modules ingestion + analysis legacy + tables)

Patch doc-set appliqué dans la foulée des trois PR séquentielles qui décommissionnent la Phase 0 (RSS ingestion + analyse portefeuille legacy). Drift à corriger sur tout le corpus parce que le code, les tables, les pages frontend et les endpoints REST mentionnés dans la doc viennent de disparaître. La Phase 0 reste une référence historique (le projet a démarré là), mais elle n'est plus en l'état dans le code — le replacement viendra en Phase 4 via un PortfolioAggregation au-dessus des snapshots ticker existants, pas un retour de l'ancien moteur.

metier/

  • fonctionnalites.md : header Phase 0 passe de « terminé » à « terminé, partiellement décommissionnée ». Section « Gelé (en mode module désactivé) » remplacée par « Décommissionné (Phase 2.5) » avec contexte explicite : module ingestion/ supprimé, pipeline AnalysisExecutor supprimé, pages /recommendations + /history + /settings/sources supprimées, tables droppées (V6). Bloc « Pourquoi décommissionné maintenant » qui pointe sur l'incident timeout 400 s du 2026-05-07 comme déclencheur. Bullet « Settings (back-office) » + « Architecture frontend » nettoyés des références aux pages supprimées et au compte historique de 4 repositories Phase 0. Référence Phase 4 « Réintégration Phase 0 gelée » → « décommissionnée ».

technique/

  • architecture.md : vue d'ensemble ASCII allégée — les blocs « gelé Phase 0 » (RSS / macro / crypto, ingestion/, analysis/ legacy, recommendations/, history/) retirés. Section dédiée ### analysis/ (legacy) — gelé Phase 0 (~5 lignes) supprimée intégralement. Section ### ingestion/ — gelé Phase 0 supprimée intégralement. Frontend modules : recommendations/, history/ — gelé Phase 0 retiré, settings/ reformulé pour refléter les seules entrées restantes (configuration/ + prompt-preview/). Schéma BDD : passe de 5 à 6 migrations Flyway avec V6__drop_phase0.sql documentée ; la table résumée perd 3 lignes (recommendation, analysis_job, feed_*), reste 5 sections actives. Section « Conservé depuis Phase 0 » → « Patterns transverses backend » (titre neutre puisque le code Phase 0 a été supprimé). Bloc entier « Gelé Phase 0 (référence) » supprimé (4 paragraphes : RecommendationValidator, ArticleRelevanceScorer, parsing RSS, fenêtres timeout legacy). Vision pipeline DAG : référence à analysis_job legacy Phase 0 retirée — la migration cible part maintenant d'un seul ticker_narrative_job. Compteur repositories frontend : 11 → 9 (Analysis et Settings repositories supprimés).
  • ddd.md : contexte ingestion retiré du tableau des bounded contexts (était 🧊 Legacy gelé Phase 0). Note sur le contexte analysis reformulée au passé : son périmètre a été réorienté en Phase 1, et le code Phase 0 a été supprimé en Phase 2.5. Section « dépendances héritées Phase 0 » (3 flèches analysis (legacy) → portfolio.infrastructure.persistence etc.) supprimée.
  • developpement.md : arborescence projet retire recommendations/, history/, ingestion/ et la mention « legacy reco portfolio gelé » sur analysis/. La ligne settings/ mise à jour pour refléter le sub-set restant.

projet/

  • backlog.md : ticket « ⏳ Décommissionner Phase 0 » déplacé en ✅ Livré Phase 2.5 avec une entrée détaillée par PR (PR1 backend / PR2 frontend / PR3 docs). Ticket SSE mis à jour : retire la référence au polling Phase 0 (un seul poller restant, le narrative ticker). Tickets « Pipeline d'analyse — modèle DAG unifié » + « Page Jobs » + « Réintégration Phase 0 » mis à jour pour refléter qu'il n'y a plus qu'une table async (ticker_narrative_job) après V6, plus de référence à analysis_job legacy comme étant en BDD.
  • sources.md : section « 🧊 Phase 0 — sources gelées » (RSS Le Monde / CNBC / MarketWatch + macro FRED / BCE / Banque Mondiale + crypto CoinGecko / CoinMarketCap / Binance) supprimée. Remplacée par un court paragraphe « Phase 0 — sources décommissionnées » qui pointe sur le replacement Phase 4 et git log pour l'historique seedé.

.claude/

  • CLAUDE.md : (1) note d'intro Phase 0 : « est frozen et en cours de décommissionnement » → « was decommissioned in Phase 2.5 ». (2) Repository structure tree : ligne │ ├── ingestion/ # 🧊 legacy Phase 0 — RSS scheduler supprimée ; ligne analysis/ # Phase 1 ticker narrative (legacy reco pipeline frozen) reformulée. (3) Backend modules table : ligne ingestion/ — 🧊 legacy Phase 0 supprimée ; blurb analysis/ allégé (suppression de la phrase « Legacy portfolio-wide pipeline (AnalysisExecutor, RecommendationValidator, etc.) is frozen in place ») et étendu pour mentionner OrphanedJobCleanupListener. (4) Frontend modules : compteur 11 repositories9 repositories (Analysis et Settings retirés), mention LlmTimeoutService ajoutée. Settings sub-list reformulée (retire recommendations/, history/, sources/, test-sources/).

Racine

  • CHANGELOG.md : cette entrée elle-même.

2026-05-07 (patch /doc-maintainer post feat(settings) + fix(market) + fix(ticker))

Sortie d'audit /doc-maintainer traitée intégralement (2 HIGH, 3 MED, 3 LOW). Drift concentrée sur trois changements de la session : (a) feat(settings) — la page /settings/configuration expose désormais 4 toggles provider (au lieu de 2) et un hint conditionnel sur la card Twelve Data ; (b) fix(market)TwelveDataMappers.toTickerQuote accepte un fallback metaType lu depuis /time_series.meta.type parce que Twelve Data /quote ne renvoie pas le type sur free tier (cas observé NVDA → null) ; (c) fix(ticker) — la gating instrumentType côté front est passée de degrade-open à degrade-closed et son scope s'est étendu du toggle Sector seul à la section Fondamentaux entière + skip des fetches loadAnalyst/loadEarnings quand non-STOCK.

metier/

  • fonctionnalites.md : Phase 2 « Settings & config runtime » — « cinq clés UI / sept backend, leurs cards UI restent à brancher » réécrit en « sept clés sans reboot, quatre toggles provider mock ↔ live ». Le finding #1 audit Phase 2 (drift contrat doc ↔ code analyst+earnings) est désormais clos. Hint Sector / Finnhub mentionné en passant (correctif finding #4).

technique/

  • architecture.md : (1) Bullet MarketChartClient — la description du gating front d'instrumentType passe d'une mention « gate la Sector benchmark » à l'énumération des trois affordances gardées (toggle Sector + section Fondamentaux entière + fetches loadAnalyst/loadEarnings non lancés à l'init mais depuis le success de load()). (2) Même bullet — « dégrade ouvert » reformulé en « dégrade fermé » avec rationale (le toggle leakait sur les ETFs pendant le null window initial du snapshot). (3) Même bullet — la phrase « Twelve Data le populate depuis /quote.type » étendue pour mentionner le fallback seriesResponse.meta?.type (observé NVDA free tier).

projet/

  • etat-actuel.md : (1) Bullet « Settings & config runtime » — « cinq clés »« sept clés », ajout de analyst.provider et earnings.provider dans la liste des toggles, ajout de RoutingAnalystClient + RoutingEarningsClient à la liste des dispatchers @Primary, mention du hint conditionnel Sector / Finnhub. (2) Bullet « Comparaison vs benchmark v2 » — le drift préexistant TwelveDataSectorClassifier corrigé : la doc mentionne maintenant le swap 2026-05-06 vers FinnhubSectorClassifier (REST /stock/profile2, free tier 60 calls/min) et l'asymétrie de routing.

.claude/

  • CLAUDE.md > Frontend modules > ticker/ : la mention « benchmark overlay (SPY/QQQ/IWM/Sector/Custom) » étendue pour préciser que le toggle Sector et toute la section Fondamentaux sont conditionnels à quote.instrumentType === 'STOCK' — règle utile à un nouveau dev qui modifie ticker.ts ou ses tests sans avoir besoin de retrouver le commentaire inline.

2026-05-07 (suite 3 — vision pipeline d'analyse + DAG de jobs cristallisée dans le doc set)

Session de design provoquée par le timeout 400 s de l'analyse portefeuille — l'investigation a remonté à la vraie question : à quoi devrait ressembler le moteur d'analyse à terme ? Le user a verbalisé une vision claire (« VOO déjà analysé aujourd'hui = cache hit, NVDA non = job individuel, puis agrégation au-dessus ») qui clarifie le cœur de métier. Cette suite cristallise cette vision dans les trois surfaces doc-set qui la portent : vision.md (framing produit), architecture.md (modèle technique), backlog.md (deux tickets fondateurs Phase 4 + un ticket prérequis bloquant). Pas de code modifié — c'est purement un alignement design avant d'attaquer l'implémentation.

metier/

  • vision.md : nouvelle section « Le pipeline d'analyse — composer au-dessus du dossier ticker » ajoutée entre « Observabilité honnête » et « Disclaimer ». Pose le DAG (parent PortfolioAggregation + N feuilles TickerAnalysis(symbol, day) cache-aware), trois propriétés essentielles (économie LLM par cache granulaire, visibilité par décomposition, composabilité du primitif feuille trigger par 3 origines), implications pour les phases à venir (Phase 3 « Page Jobs » devient la vue pipeline du DAG, Phase 4 « Réintégration Phase 0 » devient le premier consumer du parent, Phase 5 deploy bénéficie du coût borné), et le contrat de transparence avec l'utilisateur (« quand tu cliques Analyser le portefeuille tu vois exactement ce qui se passe »).

technique/

  • architecture.md : nouvelle section « Modèle pipeline d'analyse (vision Phase 3 + Phase 4) » ajoutée en queue de « Décisions techniques notables ». Détaille (a) le concept central DAG + cache, (b) le schéma cible de la table job unifiée (colonnes id, kind, parent_id, status, origin, cache_key, target_id, payload, result_summary, error, timestamps), (c) la machine à états avec ASCII art (PENDING → cache lookup → DONE_CACHED instant ou RUNNING → DONE / ERROR / CANCELLED), (d) la mécanique cache-aware leaf en pseudocode Kotlin, (e) les trois origines de trigger unifiées (dashboard / cron / api), (f) les implications côté frontend (vue arborescente, retry granulaire), (g) le rationnel pour ne PAS partir sur Temporal/Airflow en v1 (single-user, low concurrency, JVM unique).

projet/

  • backlog.md > Phase 3 ⏳ « Page Jobs : visualisation des async » : entrée existante massivement étoffée — passe d'un v1 read-only flat list à une vue arborescente DAG avec drill-down parent → enfants, indicateur cache-hit visuel par feuille, stream live par polling. Documente les extensions naturelles (retry granulaire, cancel cascade, logs structurés, métriques agrégées, alertes) et la séquence de livraison recommandée (DAG model d'abord, puis cette page).
  • backlog.md > Phase 4 : (1) nouvelle entrée fondatrice « Pipeline d'analyse — modèle DAG unifié (job, parent/child, cache-aware leaves) » placée en tête — détaille le schéma de la table job, la machine à états, la dedup déterministe par cache_key, l'orchestrateur Spring (ThreadPoolTaskExecutor borné), les tests à pin, et le rationnel anti-Temporal v1. Marquée comme prérequis bloquant pour les deux tickets dépendants. (2) Entrée existante « Réintégration Phase 0 (legacy) » réécrite — passe d'un one-liner vague à une description structurée : devient le premier consumer concret du DAG, parent PortfolioAggregation qui digère les N narratifs déjà persistés au lieu de re-prompter sur les indicateurs bruts, nouvelle table portfolio_analysis_snapshot avec traçabilité explicite des feuilles agrégées, UI dashboard avec widget « Pipeline en cours » live. (3) Nouvelle entrée « Cron quotidien — pré-chauffe du cache des positions OPEN » filed en queue Phase 4 — exploite le 3ᵉ trigger origin du DAG pour pré-chauffer hors heures de bureau, coût borné (~$18/an Claude, single-user).

.claude/

  • CLAUDE.md > Project : paragraphe « Architecture cible — pipeline d'analyse composable » ajouté juste après le framing « per-ticker market intelligence app » — résume le DAG, le caching, et pointe vers les deux fichiers source (vision.md, architecture.md). La note Phase 0 frozen mentionne maintenant le décommissionnement en cours (Phase 2.5) et le replacement Phase 4 (PortfolioAggregation), pour qu'un nouveau dev / une nouvelle session ne reparte pas sur le malentendu « Phase 0 RSS reviendra un jour ».

2026-05-07 (suite 2 — bump OllamaClient read timeout 180 s → 400 s)

Hotfix sur l'analyse portefeuille (Phase 0 frozen mais toujours invocable) : un user a hit Read timed out sur POST http://localhost:11434/api/chat parce que OllamaClient.readTimeout = 180 s saturait avant le retour Ollama dans un cold-start. Bumpé à 400 s pour s'aligner sur POLL_ABORT_SECONDS (frontend) et DEDUP_WINDOW_SECONDS (backend) — l'invariant historique « 2 × backend ≤ frontend » est cassé par ce changement (avec backend = frontend = 400 s, un retry validateur ne fit plus dans le budget) ; trade-off accepté parce que les échecs validateur sont des parse errors near-instant, pas des timeouts, et Phase 0 est gelée. Côté doc-set, alignement de tous les commentaires qui décrivaient l'invariant 2 × ou la valeur 180 s.

technique/

  • architecture.md > Choix techniques principaux : section « Fenêtres de timeout alignées (legacy, 400 s) » réécrite — l'invariant POLL_ABORT_SECONDS ≥ DEDUP_WINDOW_SECONDS ≥ 2 × OllamaClient.readTimeout + marge devient POLL_ABORT_SECONDS = DEDUP_WINDOW_SECONDS = OllamaClient.readTimeout = 400 s, avec note sur le trade-off retry validateur.

projet/

  • backlog.md > Phase 2.5 ⏳ « Config runtime v2 LLM » : entrée étendue avec une section v1.5 « fenêtres de timeout éditables » — ajout d'une clé runtime llm.timeout-seconds (INT, range 60–900, par défaut 400) consommée par les trois bornes (OllamaClient, POLL_ABORT_SECONDS front, DEDUP_WINDOW_SECONDS back). Motivation : un user qui change de modèle Ollama (e.g. mistral 7B → llama3.2:8b) doit aujourd'hui éditer code + reboot pour ajuster ; un slider settings le rendrait runtime-friendly. Cost ~3-4 h, à grouper avec le v1 (provider + model) puisque les deux changements touchent la même page.

.claude/

  • CLAUDE.md > Conventions > LLM provider : la phrase « legacy Phase 0 timeouts (frontend abort + dedup window) are aligned at 400 s » étendue pour inclure OllamaClient HTTP read dans le set des bornes alignées, avec mention de la date du bump.

2026-05-06 (suite 8 — patch /doc-maintainer + nouvelle convention de tri du backlog)

Sortie d'audit /doc-maintainer traitée intégralement (2 HIGH, 3 MED, 4 LOW, 7 findings). Drift concentrée sur la nav MkDocs (rapport audit fin Phase 2 absent), sur la liste des dispatchers @Primary (4 → 6 listés) et sur le claim « 5 toggles éditables » qui était devenu faux depuis l'ajout d'analyst+earnings côté backend. En passant : titre etat-actuel.md rafraîchi pour la fin Phase 2, mention Phase 5 ajoutée à fonctionnalites.md, schéma ASCII architecture.md aligné sur l'ordre de présentation des ports.

Nouvelle convention introduite : CLAUDE.md > Backlog gagne une « Ordering convention » — quand on touche backlog.md, réordonner la section éditée par priorité descendante (🔴 → 🟡 → 🟢) en haut, ✅ Livré au bas. Appliquée comme exemple sur la section Dette technique : 17 entrées ⏳ (5 🟡 puis 12 🟢) suivies de 6 ✅ (newest-first dans l'ordre).

metier/

  • fonctionnalites.md : (1) Phase 2 « Settings & config runtime » — « édite en direct cinq clés » reformulé en « cinq clés UI / sept backend, analyst.provider et earnings.provider non encore exposés UI, cf. ticket Critique du backlog ». Drift contrat doc ↔ code fermé. (2) Nouvelle section Phase 5 — Déploiement ajoutée en queue avec note explicative (phase orthogonale infra/sécurité, détails dans backlog.md).

technique/

  • architecture.md : (1) Section « Switch provider à chaud » — passe de 4 dispatchers @Primary à 6 listés explicitement (RoutingMarketChartClient, RoutingSymbolSearchClient, RoutingSectorClassifier dans market/, plus RoutingNewsClient, RoutingAnalystClient, RoutingEarningsClient). Note ajoutée sur le routage Sector → Finnhub. (2) Phrase d'intro module market/« chacun avec un adapter TwelveData* et un adapter Mock* » assouplie pour refléter que SectorClassifier dévie côté live vers FinnhubSectorClassifier. (3) Schéma ASCII vue d'ensemble — ordre des ports chart + sector + symb.searchchart + symb.search + sector pour matcher l'ordre de présentation détaillée plus bas dans le même fichier.

projet/

  • backlog.md : (1) Phase 2 ✅ Livré « Comparaison vs benchmark v2 » — entrée historique enrichie d'une note « remplacé 2026-05-06 par FinnhubSectorClassifier qui hit /stock/profile2 free tier, cf. CHANGELOG suite 4 » pour ne plus mentir aux lecteurs récents. (2) Section Dette technique réordonnée en application de la nouvelle convention : ⏳ 🟡 Moyenne (5 entrées : Coutures analyst, Stratégie de cache, Agent code-reviewer, Onboarding doc, Centraliser gestion d'erreur) → ⏳ 🟢 Basse (12 entrées) → ✅ Livré (6 entrées). Note d'intro ajoutée pointant vers la convention CLAUDE.md.
  • etat-actuel.md : titre + paragraphe complètement refresh — "Phase 2 chart analyse v1+v2+v3 livré + Phase 2.5 amorcée (2026-05-06)""Phase 2 clôturée (2026-05-06)". Le paragraphe liste les nouveaux livrables Phase 2 post-snapshot (analyst, earnings, sector swap, instrumentType, sidenav outils chart, news inline accordion), mentionne l'audit 2026-05-06 + 7 findings ingérés, et marque l'ouverture de la Phase 5 Déploiement au backlog.

Racine

  • mkdocs.yml : nav Audits — entrée 2026-05-06 — Revue globale fin Phase 2 ajoutée en tête (avant le 2026-05-02). Le rapport est désormais servi sur le site déployé.

.claude/

  • CLAUDE.md > Backlog : nouvelle sous-section « Ordering convention » documentant le tri à appliquer chaque fois qu'on touche backlog.md (⏳ par priorité descendante 🔴 → 🟡 → 🟢, puis 🚧, puis ✅ en bas). Précise que le tri est par-section, pas global ; et que les entrées non-touchées dans la même session restent à leur place.

2026-05-06 (suite 7 — audit fin Phase 2 + ingestion backlog)

Lancement de la 2ᵉ revue de code globale (1ʳᵉ datait du 2026-05-02 fin Phase 1). Subagent general-purpose qui a examiné le scope Phase 2 (modules analyst/ / earnings/ / news/, swap sector Twelve Data → Finnhub, instrumentType end-to-end, sidenav outils chart, accordion sections, news inline, chart analyse v1+v2+v3) et remonté 24 findings : 1 Critique + 6 Important + 6 Modérée + 7 Mineure + 4 Documentation. Rapport archivé dans docs/projet/audits/2026-05-06-fin-phase-2.md. Le user a décidé d'ingérer les 7 findings Critique + Important dans le backlog.

projet/

  • audits/2026-05-06-fin-phase-2.md (nouveau) : rapport complet au format 2026-05-02-revue-globale.md (résumé exécutif + findings par sévérité avec ref fichier:ligne + reco de priorisation).
  • audits/index.md : entrée 2026-05-06 ajoutée en tête de l'historique.
  • backlog.md : (1) finding #1 (Critique) — l'entrée existante « /settings/configuration : exposer analyst.provider et earnings.provider côté UI » bumpée de 🟡 → 🔴 avec reference audit en intro ; (2) finding #4 (Important) — nouvelle entrée Phase 2.5 ⏳ « Sector benchmark : signaler la dépendance Finnhub key même en mode market.provider=twelvedata » 🟡 ; (3) finding #5 (Important) — nouvelle entrée Phase 2.5 ⏳ « CacheTtlListener : passer en @TransactionalEventListener(AFTER_COMMIT) » 🟡 ; (4) finding #2 (Important) — l'entrée existante « Coutures post-livraison analyst » bumpée de 🟢 → 🟡 avec note expliquant que le sous-finding #1 (FinnhubAnalystClient MockWebServer test) est promu Important suite à audit ; (5) finding #3, #6, #7 (Important) — 3 nouvelles entrées Dette technique (cache strategy à trancher, watchlist fail-open à tester + DTO flag, front 404/503 paths à pin sur analyst+earnings).

Findings Modérée + Mineure + Documentation non ingérés automatiquement — restent dans le rapport d'audit comme observations. Per CLAUDE.md (Documentation > audits/), c'est le user qui décide ce qui devient action ; les findings de basse sévérité non promus restent référencés dans l'audit pour la prochaine revue.


2026-05-06 (suite 6 — News inline v1 minimaliste, Phase 2 clôturée)

Dernier item Phase 2 livré : le panneau News du Dossier ticker passe en accordéon — chaque headline cliquable expand un body inline qui affiche le summary Finnhub + un lien explicite vers la source. Le user reste sur le dossier par défaut, sort uniquement quand il veut le contenu intégral. Choix v1 minimaliste explicite : pas de scraping (ToS Reuters/Bloomberg, paywalls, JS-heavy sites trop fragiles pour un personal project) ; pas de LLM-as-summarizer (hallucination + ~10 calls Claude par dossier ouvert). On affiche ce que Finnhub fournit déjà dans le payload summary, point. Si à l'usage les ~150-200 chars se révèlent trop courts, v2 LLM-as-summarizer filed en Phase 2.5 dette pour bascule sur Claude Haiku avec cache 24 h+. Avec cette livraison, Phase 2 est clôturée — tous les items « ticker » de profondeur sont livrés (multi-timeframe + axes + crosshair, watchlist v1+v2, sidebar collapsable, news, settings & config runtime, benchmark v1+v2, chart analyse v1+v2+v3, recommandations analystes, earnings, news inline). Les sujets restants vivent en Phase 2.5 (stabilisation, outillage, dette) et Phase 3 (observabilité narrative).

metier/

  • fonctionnalites.md : Phase 2 — promotion News inline (v1 minimaliste) dans ### ✅ Livré avec narratif court (accordéon, summary Finnhub, lien externe explicite, multi-open). Note de clôture de phase ajoutée pointant vers Phase 2.5 et Phase 3 pour ce qui reste.

projet/

  • backlog.md : (1) Phase 2 « ⏳ À faire — items ticker restants » — la note d'intro passe à « Phase 2 clôturée 2026-05-06 ». L'ancien tableau « ⏳ À faire » est vidé (l'item news inline migré). (2) Section ✅ Livré — nouvelle entrée détaillée « News inline : accordéon de contenu sans navigation externe (v1 minimaliste) » avec design rationale (pourquoi pas scraping / pourquoi pas LLM v1) + détails techniques (signaux, helpers, HTML, SCSS, i18n, tests). (3) Section Dette technique — nouvelle entrée « News inline v2 — LLM-as-summarizer pour étendre le résumé Finnhub » 🟢 Basse, à attaquer si v1 se révèle trop court à l'usage et idéalement groupé avec Phase 3 (observabilité narrative + prompt management).

2026-05-06 (suite 5 — instrumentType end-to-end pour gater la Sector benchmark)

Suite logique du swap Twelve Data → Finnhub côté sector : pour les ETFs (VOO, SPY…) la feature « Sector benchmark » n'a pas de sens (un ETF est une exposition à un sector ou au marché). Plutôt que de laisser l'utilisateur cliquer et découvrir via un 404 inline, on surface le type d'instrument end-to-end pour cacher le toggle Sector quand le ticker n'est pas une action individuelle. Champ ajouté côté domain (InstrumentType { STOCK, ETF, INDEX, OTHER }), populé depuis Twelve Data /quote.type côté live et tagué dans la table seedée du mock pour les ~17 ETFs courants (SPY/QQQ/IWM/VOO/VTI/DIA + 11 SPDR sectors). Front : benchmarkChoicesForCurrentTicker computed filtre 'sector' quand instrumentType !== 'STOCK'. Quand le provider ne surface pas le type (null), on dégrade ouvert (toggle visible) — préfère afficher une option légitime qu'over-hider sur des stocks dont la métadonnée a été perdue.

technique/

  • architecture.md : Section market/ Quote — mention du nouveau champ TickerQuote.instrumentType et de l'enum InstrumentType. Twelve Data mapper note la consommation du champ type du /quote jusqu'ici ignoré.
  • developpement.md (si nécessaire) — non touché, le détail vit dans architecture.

.claude/

  • CLAUDE.md : description ticker enrichie pour mentionner le filtre conditionnel des benchmark options selon le type d'instrument.

2026-05-06 (suite 4 — sector classifier swap Twelve Data → Finnhub)

Constat live cassant : Twelve Data /profile est paid-tier only sur les comptes free, ce qui rendait la feature « Sector benchmark » (overlay Phase 2 v2) inutilisable — chaque clic sur le toggle Sector renvoyait auth-failed → 503 → bandeau d'erreur côté front. Plutôt que de patcher autour (fallback automatique sur la table mock — solution proposée puis abandonnée), on remplace l'adapter par FinnhubSectorClassifier qui hit /stock/profile2 (free tier 60 calls/min, déjà câblé pour news / analyst / earnings). Le RoutingSectorClassifier route le mode live vers Finnhub plutôt que Twelve Data — détail caché derrière le routing, pas de nouveau runtime key (toggle market.provider reste binaire mock/live). TwelveDataSectorClassifier + TwelveDataProfileModels supprimés du repo (récupérables via git history si besoin un jour).

technique/

  • architecture.md : Section market/ Sector — TwelveDataSectorClassifier remplacé par FinnhubSectorClassifier (mention explicite « Remplace TwelveDataSectorClassifier depuis 2026-05-06 — Twelve Data /profile est paid-tier only »). Description de RoutingSectorClassifier enrichie pour pin la décision de routage live → Finnhub (le toggle market.provider reste, le détail Finnhub est interne).
  • ddd.md : Couche infrastructure/market/ Sector classification — adapter Twelve Data → Finnhub, mention des sub-industries Finnhub (Banks → Financials, Pharmaceuticals → Healthcare, Retail → Consumer Discretionary…) absorbées par les synonymes étendus.
  • providers.md : Tableau Twelve Data — /profile retiré de la colonne Endpoints (avec note explicative), TwelveDataSectorClassifier retiré de la colonne Adapter, sector-by-symbol retiré de Cache (passe côté Finnhub). Tableau Finnhub — /stock/profile2 ajouté avec note de remplacement, FinnhubSectorClassifier ajouté à la colonne Adapter, sector-by-symbol ajouté à Cache.

projet/

  • sources.md : Tableau Twelve Data — ligne /profile supprimée. Tableau Finnhub — ligne /stock/profile2 ajoutée avec note Remplace /profile Twelve Data qui est paid-tier only.
  • backlog.md : Item dette technique ⏳ Sector benchmark : fallback sur la table mock (filed plus tôt aujourd'hui en suite 3) supprimé — le swap Finnhub résout la cause sous-jacente (paid-tier hard-block) plutôt que de contourner.

2026-05-06 (suite 3 — sidenav outils chart livrée)

Refonte de la chart-toolbar du Dossier ticker : les contrôles (timeframe / benchmark + autocomplete custom / overlays / outils annotation+measure+reset zoom) sont déportés dans une sidenav gauche persistante façon Amazon, foldable via chevron, état persisté global en localStorage. La sidenav embarque aussi une section « Annotations posées » avec liste explicite + bouton supprimer par item, qui devient le chemin discoverable pour supprimer une annotation (le handle × inline reste fonctionnel mais n'est plus le seul moyen). Cette livraison clôt en passant le ticket Phase 2 « Chart analyse v3 — fiabiliser la suppression d'annotations ». Drift doc mise en cohérence dans le même tour : architecture.md, CLAUDE.md, backlog.md (deux tickets promus en ✅, intro Phase 2 et Phase 2.5 mises à jour).

technique/

  • architecture.md : Section Modules frontend — description du module ticker/ enrichie pour refléter le passage en layout 2-col avec la sidenav outils chart à gauche (Amazon-style, foldable, sticky, état localStorage ticker-sidenav-open) qui héberge timeframe / benchmark / overlays / outils / liste annotations posées. Ajout de la mention « section Fondamentaux (analyst + earnings) » qui manquait dans la liste des sections de la colonne droite.

projet/

  • backlog.md : (1) Phase 2 « ⏳ À faire » — retrait du ticket ⏳ Chart analyse v3 — fiabiliser la suppression d'annotations (livré en passant via la sidenav), ajout d'une note explicative pointant vers Phase 2.5 ✅ Livré pour l'historique. Compteur Phase 2 « 2 items restants » → « l'item ci-dessous ». (2) Phase 2.5 « Stabilisation et outils » — création d'une section ### ✅ Livré avec la première entrée Sidenav outils chart (Dossier ticker) détaillée (sections, persistance, sticky, responsive, embarquement de la fiabilisation annotation, tests). Retrait du ticket ⏳ Chart Dossier ticker : déporter filtres + actions (devenu cette livraison). Intro de Phase 2.5 « refonte chart-toolbar » retirée de la liste des items déplacés (n'est plus à faire).

.claude/

  • CLAUDE.md : Description du module ticker/ dans ## Frontend modules enrichie pour refléter le layout 2-col + le contenu de la sidenav (timeframe / benchmark / overlays / tools / annotations-posées list). Le bloc « Fondamentaux section » + les indicateurs et le narratif sont désormais explicitement situés en colonne droite.

2026-05-06 (suite 2 — earnings module livré)

Livraison v1 large du module earnings sur le Dossier ticker, 2ᵉ sous-bloc « Résultats » de la section « Fondamentaux » sous le sous-bloc analyste. Pattern hexagonal calqué sur analyst/ : nouveau module backend earnings/ (port EarningsClient + FinnhubEarningsClient + MockEarningsClient + RoutingEarningsClient @Primary + EarningsService cache-bearing + EarningsController), nouvelle clé runtime earnings.provider, nouveau cache Caffeine earnings, deux nouveaux endpoints Finnhub (/stock/earnings requis pour les 4 derniers Q + /calendar/earnings optionnel fail-soft pour la prochaine date), nouveau repository frontend EarningsRepository (11ᵉ port) + adapter HTTP, sous-bloc UI avec next-date + countdown + BMO/AMC tag + tableau 4 lignes EPS estimate/actual/surprise %. Drift doc mise en cohérence dans le même tour : architecture.md, ddd.md, developpement.md, developper.md, providers.md, sources.md, CLAUDE.md, commit-conventions.md, fonctionnalites.md, backlog.md (entrée déplacée vers ✅ Livré, compteur Phase 2 « 2 items » → « 1 item »).

metier/

  • fonctionnalites.md : Phase 2 — promotion Earnings dates et derniers résultats dans ### ✅ Livré avec résumé technique du livrable (port + adapters + fail-soft /calendar/earnings, mock symboles réservés, helper computeSurprisePercent, signaux scopés à la panel, helpers earningsCountdownDays + earningsSurpriseSign). « ⏳ Fundamentals avancés » conservé mais reformulé sur ce qui reste réellement à faire (guidance forward-looking, breakdown revenue par segment).

technique/

  • architecture.md : (1) Schéma ASCII vue d'ensemble — ajout du module earnings/ à la liste backend. (2) Section Caches Caffeine — passe de 5 caches à 6 avec earnings ajouté. (3) Nouvelle section ### earnings/ — nouveau, Phase 2 modelée sur la section analyst/ voisine — port + adapters + routing + service + endpoint + erreurs, narratif (pas bullets secs) pour rester cohérent avec la voix du fichier. (4) Section config/six cléssept clés avec earnings.provider ajouté à l'énumération. (5) Décision technique « Switch provider à chaud » — mention de RoutingEarningsClient à côté des trois routings existants. (6) Section Modules frontend10 repositories11 avec Earnings ajouté à la liste nominale.
  • ddd.md : (1) Tableau Bounded Contexts — nouvelle ligne earnings | Earnings trimestriels par ticker (4 derniers Q EPS estimate/actual/surprise % + prochaine date d'annonce, Finnhub + mock), cache court | ✅ Phase 2. (2) Tableau structure {context}/ — ligne earnings/ ajoutée sous analyst/ avec triplet adapters + routing. (3) Nouvelle section ### infrastructure/earnings/ (earnings uniquement, Phase 2) en prose (pattern voisin de news/ et analyst/) qui décrit le triplet, le partage du RestClient Finnhub via @Qualifier, le placement du cache au niveau service et l'isolation des mappers purs.
  • developpement.md : (1) Arbre projet backend — module earnings/ ajouté entre analyst/ et config/. (2) Compteur frontend 10 repositories11 avec Earnings ajouté à l'énumération nominale. (3) Bloc « Alternative runtime » — six cléssept clés avec mention du toggle earnings.provider.
  • developper.md : (1) Section « Switcher les providers » — quatre providers configurablescinq, ajout d'une nouvelle sous-section ### Earnings — earnings.provider avec tableau mock/finnhub aligné sur le pattern news/analyst, mention explicite que le toggle est séparé de news.provider et analyst.provider. (2) Liste des chemins de modification — mention de earnings.provider dans le toggle de la page /settings/configuration.
  • providers.md : Tableau Finnhub — titre news par ticker + recommandations analystesnews par ticker + recommandations analystes + earnings. Colonne Endpoints utilisés étendue à /stock/earnings + /calendar/earnings (avec note fail-soft sur le calendrier paid tier). Adapter liste les trois clients (FinnhubClient + FinnhubAnalystClient + FinnhubEarningsClient). Mock alternative documente MockEarningsClient avec ses trois symboles réservés. Cache liste les trois caches (news-by-symbol + analyst-recommendations + earnings). Bloc YAML « Récapitulatif config locale » enrichi du toggle earnings.provider.

projet/

  • backlog.md : (1) Phase 2 « ⏳ À faire — items ticker restants » — ligne ⏳ Earnings dates et derniers résultats déplacée vers le bloc « ✅ Livré » avec notes d'implémentation détaillées (modules, ports, adapters, fail-soft /calendar/earnings, mock symboles réservés, cache, clé runtime, endpoint, helpers front, tests 26 backend + 7 frontend). Compteur intro 2 items ci-dessousl'item ci-dessous (plus que news inline). (2) Phase 2.5 « refonte chart-toolbar » — référence 2 items ticker restants (earnings, news inline)l'item ticker restant (news inline).
  • sources.md : Section Finnhub — titre News par ticker et recommandations analystesNews par ticker, recommandations analystes et earnings, intro élargie pour expliquer le partage de la clé market.finnhub.api-key entre news.provider, analyst.provider et earnings.provider. Tableau étendu aux deux nouveaux endpoints (/stock/earnings + /calendar/earnings avec note fail-soft). Note « Switch runtime » ajoute earnings.provider à la liste des toggles disponibles depuis /settings/configuration.
  • commit-conventions.md : Tableau « Scopes courants » — nouvelle ligne earnings sous analyst (cohérence de forme avec les autres modules backend market, watchlist, news, analyst, etc.).

.claude/

  • CLAUDE.md : (1) Repository structure — module backend earnings/ ajouté à l'arbre sous analyst/. (2) Backend modules — paragraphe dédié earnings/ ajouté sous analyst/ (port + adapters + fail-soft + mock symboles réservés + routing + cache + endpoint + mention du domain helper computeSurprisePercent), modèle de la voix de la section voisine. (3) Section config — mention de RoutingEarningsClient à côté des trois routings existants, analyst.provideranalyst.provider / earnings.provider. (4) Frontend modules — 10 repositories11 avec Earnings ajouté à la liste, description du module ticker/ enrichie de l'earnings sub-block (next-date countdown avec BMO/AMC tag, tableau 4 trimestres EPS estimate vs actual + surprise %).

2026-05-06 (suite — patch /doc-maintainer)

Sortie d'audit /doc-maintainer traitée intégralement (5 HIGH, 6 MED, 3 LOW). Drift concentrée sur le livrable de la session courante : PR #31 « feat: recommendation analyst » — nouveau module backend analyst/ (port AnalystRecommendationClient + FinnhubAnalystClient + MockAnalystClient + RoutingAnalystClient @Primary + AnalystRecommendationService cache-bearing + AnalystController), nouvelle clé runtime analyst.provider (mock ↔ finnhub), nouveau cache Caffeine analyst-recommendations, deux nouveaux endpoints Finnhub (/stock/recommendation + /stock/price-target), nouveau repository frontend AnalystRepository + adapter HTTP (10ᵉ port), nouvelle section « Fondamentaux > Recommandations analystes » sur le Dossier ticker. Le module était entièrement absent des docs avant ce passage.

metier/

  • fonctionnalites.md : Phase 2 — promotion Recommandations analystes de ### ⏳ À venir vers ### ✅ Livré avec résumé technique du livrable (port AnalystRecommendationClient, deux adapters via analyst.provider, fail-soft sur /price-target, cache analyst-recommendations, helper deriveConsensus 60/50 %, helper analystBucketPct, computed analystTrend, signaux scopés à la panel). La ligne ⏳ Recommandations analystes correspondante retirée du À venir.

technique/

  • architecture.md : (1) Schéma ASCII vue d'ensemble — titre Vue d'ensemble (Phase 1)Vue d'ensemble (le schéma couvre déjà la Phase 2 entière), ajout du module analyst/ à la liste backend et de Finnhub aux sources de données. (2) Section Caches Caffeine — passe de 4 caches à 5 avec analyst-recommendations ajouté, formulation Tous partagent le TTL validée (le code partage bien market.cache.ttl-minutes). (3) Nouvelle section ### analyst/ — nouveau, Phase 2 modelée sur la section news/ voisine — port + adapters + routing + service + endpoint + erreurs, narratif (pas bullets secs) pour rester cohérent avec la voix du fichier. (4) Section config/cinq cléssix clés avec analyst.provider ajouté à l'énumération. (5) Décision technique « Switch provider à chaud » — mention de RoutingAnalystClient à côté des deux routings existants. (6) Section Modules frontend9 repositories10 avec Analyst ajouté à la liste nominale.
  • ddd.md : (1) Tableau Bounded Contexts — nouvelle ligne analyst | Recommandations d'analystes par ticker (consensus monthly + price target 12 mois, Finnhub + mock), cache court | ✅ Phase 2. (2) Tableau structure {context}/ — ligne analyst/ ajoutée sous news/ avec triplet adapters + routing. (3) Nouvelle section ### infrastructure/analyst/ (analyst uniquement, Phase 2) en prose (pattern voisin de news/ et market/) qui décrit le triplet, le partage du RestClient Finnhub via @Qualifier, le placement du cache au niveau service et l'isolation des mappers purs.
  • developpement.md : (1) Arbre projet backend — module analyst/ ajouté entre news/ et config/. (2) Compteur frontend 9 repositories10 avec Analyst ajouté à l'énumération nominale + ajout des ports Annotation, Analyst à la ligne descriptive. (3) Bloc « Alternative runtime » — cinq cléssix clés avec mention du toggle analyst.provider.
  • developper.md : (1) Section « Switcher les providers » — trois providers configurablesquatre, ajout d'une nouvelle sous-section ### Recommandations analystes — analyst.provider avec tableau mock/finnhub aligné sur le pattern news/market, mention explicite que le toggle est séparé de news.provider pour permettre les combinaisons live news + mock recos pendant l'itération. (2) Liste des chemins de modification — mention de analyst.provider dans le toggle de la page /settings/configuration.
  • providers.md : Tableau Finnhub — titre news par tickernews par ticker + recommandations analystes. Colonne Endpoints utilisés étendue à /stock/recommendation + /stock/price-target (avec note fail-soft sur le price-target paid tier). Adapter liste les deux clients (FinnhubClient + FinnhubAnalystClient). Mock alternative documente MockAnalystClient avec ses trois symboles réservés. Cache liste les deux caches (news-by-symbol + analyst-recommendations). Bloc YAML « Récapitulatif config locale » enrichi du toggle analyst.provider.

projet/

  • backlog.md : (1) Phase 2 « ⏳ À faire — items ticker restants » — ligne ⏳ Recommandations analystes déplacée vers le bloc « ✅ Livré » (juste avant l'entrée v2 benchmark) avec notes d'implémentation détaillées (modules, ports, adapters, fail-soft /price-target, mock symboles réservés, cache, clé runtime, endpoint, helpers front, tests 31 backend + 8 frontend, cinq coutures post-livraison filed en dette). Compteur intro 3 items ci-dessous2 items. (2) Phase 2.5 « refonte chart-toolbar » — référence 3 items ticker restants (recos analystes, earnings, news inline)2 items ticker restants (earnings, news inline). (3) Item « Earnings dates » légèrement reformulé pour cross-référencer la section Fondamentaux désormais existante.
  • sources.md : Section Finnhub — titre News par tickerNews par ticker et recommandations analystes, intro élargie pour expliquer le partage de la clé market.finnhub.api-key entre news.provider et analyst.provider. Tableau étendu aux deux nouveaux endpoints (/stock/recommendation + /stock/price-target avec note fail-soft). Bullets « Avantages » + « Limites à connaître » mis à jour pour mentionner les recos en free tier et le quirk du price-target. Note « Switch runtime » ajoute analyst.provider à la liste des toggles disponibles depuis /settings/configuration.
  • commit-conventions.md : Tableau « Scopes courants » — nouvelle ligne analyst sous news (cohérence de forme avec les autres modules backend market, watchlist, news, etc.).

.claude/

  • CLAUDE.md : (1) Repository structure — module backend analyst/ ajouté à l'arbre sous news/. (2) Backend modules — paragraphe dédié analyst/ ajouté sous news/ (port + adapters + fail-soft + mock symboles réservés + routing + cache + endpoint), modèle de la voix de la section voisine. (3) Section config — mention de RoutingAnalystClient à côté des deux routings existants, news.providernews.provider / analyst.provider. (4) Frontend modules — 9 repositories10 avec Analyst ajouté à la liste, description du module ticker/ enrichie de la section Fondamentaux avec sous-bloc analyste (consensus chip, segmented breakdown bar, price target, trend arrow).

2026-05-06 (patch /doc-maintainer)

Sortie d'audit /doc-maintainer traitée intégralement (3 HIGH, 5 MED, 2 LOW). Drift concentrée sur le livrable de la session courante : chart : analyse + sélection v1+v2+v3 (zoom drag-select + brush mini-chart + multi-select overlays MA / Bollinger / 52w + annotations user persistées localStorage + measure tools), avec en particulier l'arrivée d'un 9ᵉ port frontend AnnotationRepository (premier port avec un adapter non-HTTP — LocalStorageAnnotationRepository côté client). En passant : module backend config/ (Phase 2) qui n'avait jamais été listé dans CLAUDE.md, ddd.md sans point d'entrée dans le README, et developper.md qui pointait sur npx ng test (Karma) alors que le projet tourne sur Vitest.

metier/

  • fonctionnalites.md : Phase 1 — phrase "Overlays MA50 / MA200 et zoom drag-select restent en backlog Phase 2" supprimée (désormais livrés). Description du graphique reformulée en deux temps (Phase 1 = courbe simple 1Y, Phase 2 = liste complète des outils interactifs livrés). Phase 2 — promotion Chart : analyse + sélection (v1+v2+v3) en ### ✅ Livré avec résumé technique du livrable (zoom + brush + overlays MA/BB/52w + annotations localStorage + measure tools), retrait de la ligne ⏳ À venir correspondante.

technique/

  • architecture.md : Section "Modules frontend" — passage de "8 repositories" à 9 avec AnnotationRepository ajouté à la liste nominale, mention du nouveau pattern d'adapter adapters/*.local.ts (client-only) en complément du *.http.ts historique. Description du module ticker/ enrichie pour refléter la livraison de la session : ajout de la couche chart analyse interactive (zoom drag-select, brush mini-chart, overlays multi-select MA/Bollinger/52w, annotations localStorage, measure tools) à côté du benchmark overlay déjà documenté. Picker benchmark étendu de SPY/QQQ/IWM à SPY/QQQ/IWM/Sector/Custom pour cohérence avec le livrable v2 du jour précédent.
  • developpement.md : Arbre du projet — passage de "8 repositories" à 9 + ajout Annotation à la liste des ports + ajout de la ligne adapters/*.local.ts # localStorage impls (annotation v3) sous la ligne HTTP existante. Section "Lint et formatage" — import …\.\* (artefact d'escape Markdown) reformulé en package.* lisible en source.
  • developper.md : Section troubleshooting "Les tests Vitest ne reconnaissent pas describe" — recommandation npx ng test / npx ng test --watch=false (Karma builder Angular) corrigée en npm run test / npx vitest run src/path/to/file.spec.ts. Le projet utilise Vitest et ng test rate la config — un nouveau dev qui suivait littéralement aurait été coincé.

projet/

  • etat-actuel.md : Refresh du titre — "Phase 2 benchmark v2 (sector + custom) livré + Phase 2.5 amorcée (2026-05-05)""Phase 2 chart analyse v1+v2+v3 livré + Phase 2.5 amorcée (2026-05-06)". Ajout du livrable chart analyse + sélection v1+v2+v3 dans la section "Phase 2 démarrée" (résumé technique : zoom drag-select avec slice symétrique, brush mini-chart 52 px navigator, overlays multi-select calculés sur la série complète puis sliced à l'affichage, port AnnotationRepository 9ᵉ + adapter localStorage avec defer() pour quota-safety, measure tools recovered par timestamp). Section "Frontend" — picker benchmark étendu en Off/SPY/QQQ/IWM/Sector/Custom, ajout du bloc "chart analyse interactive" à la description du Dossier ticker. Section "Phase 2 — restant à attaquer" — ligne "Chart : analyse interactive" supprimée (livrée), passage de 4 à 3 items restants dans le compteur d'intro. Section "Dette technique" — compteur provideRepositories() mis à jour de 8 lignes répétitives à 9.

.claude/

  • CLAUDE.md : Repository structure — ajout module backend config/ (Phase 2 runtime-editable settings) dans l'arbre + section Backend modules avec un paragraphe dédié décrivant AppConfigService / ConfigController / RoutingMarketChartClient + RoutingNewsClient (@Primary) / CacheTtlListener. Frontend modules — passage de 8 à 9 repositories avec Annotation ajouté, mention du pattern adapters/*.local.ts (client-only) à côté du *.http.ts. Description du module ticker/ enrichie de la couche chart analyse interactive (zoom + brush + overlays + annotations + measure). Pattern adapters/*.http.ts assoupli à adapters/*.http.ts (default) + adapters/*.local.ts (client-only) pour ne plus mentir sur l'adapter localStorage.

Racine

  • README.md : Tableau Documentation — ajout d'une ligne DDD — bounded contexts pointant vers docs/technique/ddd.md qui n'avait pas de point d'entrée depuis le README (référencé par architecture.md et le doc-set MkDocs uniquement, manquant de la table d'orientation principale).

2026-05-05 (suite — patch /doc-maintainer)

Sortie d'audit /doc-maintainer traitée intégralement (3 HIGH, 5 MED, 3 LOW). Drift concentrée sur deux changements structurants livrés dans la session courante : (1) comparaison vs benchmark v2 (nouveau port SectorClassifier + 3 adapters + SpdrSectorEtfs + SectorClassifierService + endpoint /sector-benchmark, totalement absent des docs techniques), et (2) outillage no-wildcard imports (Spotless custom step + .editorconfig racine + désactivation de la rule Detekt redondante). L'audit a aussi remonté que la Phase 2 watchlist v2 avait déjà laissé du résidu non documenté côté SymbolSearchClient — patché en passant.

metier/

  • fonctionnalites.md : Promotion Comparaison vs benchmark (v2 — Sector + Custom) en ### ✅ Livré avec résumé technique du livrable (port SectorClassifier, 2 adapters via market.provider, mapping SpdrSectorEtfs 11 sectors GICS, endpoint /sector-benchmark, UI 5-buttons + autocomplete sidecar). Suppression de la note "v2 reportée" qui traînait en queue de l'item v1.

technique/

  • architecture.md : Réécriture complète de la section market/ — passe de "1 port + indicateurs + 2 endpoints" à 3 ports (chart / symbol-search / sector) + 2 services applicatifs (SymbolSearchService, SectorClassifierService) + SpdrSectorEtfs domain helper + 4 endpoints + 4 caches Caffeine. Schéma ASCII Phase 1 mis à jour (ligne market/) pour refléter le scope élargi sur deux lignes. Nouvelle décision technique notable "Pas de wildcard imports en Kotlin" (Phase 2.5 outillage) qui documente le choix custom Spotless + .editorconfig plutôt que ktlint, avec mention explicite du faux départ (152 fichiers consolidés en * parce que ktlint applique la sémantique IntelliJ de ij_kotlin_packages_to_use_import_on_demand = "force"). Tone — entrée "Tracking du modèle LLM par snapshot" reformulée : retrait du backtick-dans-gras (**`LlmClient.modelId()` tracé sur chaque snapshot**), titre en gras pur cohérent avec les autres décisions, mention de LlmClient.modelId() déplacée dans le corps du paragraphe.
  • ops.md : Section "Detekt — analyse statique Kotlin" — retrait du bullet "WildcardImport autorise jakarta.persistence.* etc." (faux depuis le commit Spotless+ktlint) et remplacement par un paragraphe narratif qui explique pourquoi la rule est désactivée et où vit l'enforcement (custom step Spotless qui casse le build au lieu de juste rapporter, allowlist de 14 packages dans build.gradle.kts).
  • ddd.md : Section infrastructure/market/ — passage d'une liste de 3 lignes (TwelveData/Mock/Routing chart) à un découpage par famille de port (Chart Phase 1 / Symbol Search Phase 2 v2 / Sector Classification Phase 2 v2). Chaque famille suit le même triplet TwelveData* + Mock* + Routing*. Mention de SpdrSectorEtfs comme internal object colocalisé. Tableau structure {context}/ aligné sur la nouvelle réalité.
  • providers.md : Tableau Twelve Data — colonne Endpoints utilisés étendue à /symbol_search (autocomplete v2) + /profile (sector v2). Adapter liste les 3 clients (TwelveDataClient + TwelveDataSymbolSearchClient + TwelveDataSectorClassifier). Cache liste les 3 caches Caffeine (market-chart + symbol-search + sector-by-symbol).
  • developpement.md : Section "Lint et formatage" — ajout de la mention du custom step Spotless no-wildcard-imports et du .editorconfig racine comme couche de prévention IntelliJ.

projet/

  • sources.md : Tableau Twelve Data — ajout de deux endpoints jusqu'ici non documentés (/symbol_search Phase 2 v2, /profile Phase 2 v2 → SPDR via SpdrSectorEtfs). Cache nommé explicitement par endpoint dans la 3ᵉ colonne (market-chart / symbol-search / sector-by-symbol).
  • etat-actuel.md : Refresh du titre — "Phase 2 watchlist v2 + lifecycle + benchmark v1 livrés (2026-05-05)""Phase 2 benchmark v2 (sector + custom) livré + Phase 2.5 amorcée (2026-05-05)". Ajout de deux nouveaux livrables manquants à la section "Phase 2 démarrée" : (1) comparaison vs benchmark v2 Sector + Custom (port + 2 adapters + Routing @Primary + SpdrSectorEtfs + endpoint + UI 5-buttons + autocomplete), (2) outillage no-wildcard imports (.editorconfig + Spotless custom step + désactivation Detekt + faux départ ktlint documenté). Refonte complète de la section "Phase 2 — restant à attaquer" en 4 items strictement ticker (chart analyse, recos analystes, earnings, news inline) avec note de clôture de phase. Nouvelle section "Phase 2.5 — Stabilisation et outils" qui liste les 3 items déplacés (config runtime v2 LLM, drag-drop portfolios, sidebar modulaire). Section "Dette ouverte saillante" enrichie avec les 4 entrées du jour (shrink allowlist no-wildcard, sweep ::ng-deep, coutures benchmark v2, agent code-reviewer pré-commit).

2026-05-05 (patch /doc-maintainer)

Sortie d'audit /doc-maintainer traitée intégralement (2 HIGH, 3 MED, 2 LOW). Drift causée par le livrable du jour (comparaison vs benchmark v1) qui n'avait pas encore été reflété, et résidu de renommage Tilt sur providers.md.

metier/

  • fonctionnalites.md : Phase 2 — "Comparaison vs benchmark" déplacée de ### ⏳ À venir vers ### ✅ Livré avec résumé technique du livrable (overlay opt-in SPY/QQQ/IWM, Y-axis bi-mode price/%, 2ᵉ polyline dashed, tooltip enrichi, MatTooltipModule, front-only zéro changement back, v2 reportée pour sector ETF + custom).

technique/

  • architecture.md : Liste des features frontend/features/ enrichie pour le module ticker/ — mention de l'overlay benchmark opt-in (Y-axis bi-mode, polyline dashed, MatTooltipModule) à côté du graphe multi-timeframe + axes + crosshair existants.
  • providers.md : Tableau Ollama — bouton Tilt llm:pull-qwenllm:ensure-model (alignement avec developpement.md et developper.md patchés au CHANGELOG 2026-05-04 suite 4 ; providers.md était la dernière doc à porter l'ancien nom). Mention "(idempotent)" ajoutée pour cohérence avec la justification du renommage.

projet/

  • etat-actuel.md : Refresh complet — en-tête "Phase 2 multi-timeframe + watchlist + news livrés (2026-05-04)" → "Phase 2 watchlist v2 + lifecycle + benchmark v1 livrés (2026-05-05)". Ajout de trois nouveaux livrables manquants à la section "Phase 2 démarrée" : (1) lifecycle de position OPEN/CLOSED (V5, fix CSV upsert), (2) watchlist v2 (autocomplete mat-autocomplete + validation backend /symbol_search), (3) comparaison vs benchmark v1 (overlay opt-in front-only). Section "Phase 2 — restant à attaquer" actualisée : retrait de "comparaison vs benchmark" (livré) et "watchlist v2" (livré), ajout de "comparaison vs benchmark v2" et "config runtime v2 LLM". Sous-section "Frontend" enrichie pour mentionner overlay benchmark sur le Dossier ticker, autocomplete watchlist sur le Dashboard, et section news. Liste des endpoints actualisée avec /symbols/search, /news, /api/config. Glyphe 4ᵉ (exposant unicode) → 4e ligne 23 — cohérence avec fonctionnalites.md post-patch CHANGELOG 2026-05-04 suite 4.

2026-05-04 (suite 4 — patch /doc-maintainer)

Sortie d'audit /doc-maintainer traitée. Drift concentrée sur les changements faits dans la session courante (réorganisation Tilt, Dependabot daily America/Toronto, ktfmt 0.62, retrait du cache Angular CI). Findings 8 (architecture.md:205) et 11 (architecture.md:207+) écartés après vérification — faux positifs : claim factuellement correct pour le premier, section déjà au format narratif pour le second.

metier/

  • fonctionnalites.md : Exposant unicode 4ᵉ4e (cohérence de glyphe avec le reste du doc set qui n'emploie pas d'exposants).

technique/

  • developpement.md : Tableau "Commandes Tilt utiles" mis à jour pour refléter la réorganisation Tiltfile — db:reset (ressource standalone) → bouton Purge attaché au panel postgres via cmd_button, et llm:pull-qwenllm:ensure-model (renommage pour rendre l'idempotence explicite).
  • developper.md : Section "Configurer le LLM" — référence llm:pull-qwenllm:ensure-model, mention de l'idempotence ajoutée.
  • ops.md : Trois drift corrigés. (1) gradle/actions/setup-gradle@v4@v6 (4 occurrences) suite au bump Dependabot déjà mergé sur master. (2) Section "Frontend" simplifiée — un seul cache npm au lieu de deux, mention explicite du retrait du step Cache Angular build (jamais alimenté car .angular/cache non créé en mode prod, Path Validation Error au post-step). (3) Dependabot — "Scan hebdo lundi 06:00 Europe/Paris" → "Scan quotidien 06:00 America/Toronto" suite au passage weeklydaily et au switch de timezone vers le fuseau de l'utilisateur.

projet/

  • etat-actuel.md : Décisions techniques notables — gradle/actions/setup-gradle@v4@v6, mention du retrait du cache .angular/cache côté frontend (le tente initial est explicitement consigné comme abandonné).

Racine

  • backend/build.gradle.kts : Commentaire au-dessus de mockwebserver mentionnait YahooClientTest (classe supprimée au moment du switch Yahoo → Twelve Data en Phase 1) → corrigé pour citer les deux consommateurs actuels TwelveDataClientTest + FinnhubClientTest (vérifié grep).

2026-05-04 (suite 3 — patch /doc-maintainer)

Sortie d'audit /doc-maintainer traitée en intégralité (3 HIGH, 4 MED, 4 LOW).

metier/

  • fonctionnalites.md : Phase 0 "Architecture frontend" précise que "4 repositories" est un bilan figé Phase 0 (les phases suivantes ont enrichi à 8). Évite la confusion pour qui lit Phase 0 sans poursuivre.

technique/

  • architecture.md : Schéma ASCII de la vue d'ensemble enrichi avec le module config/ (Phase 2) qui manquait à la carte de navigation, malgré sa documentation détaillée plus bas dans le fichier. Sous-titre des décisions techniques ### Frontend renommé en ### Phase 1+ — frontend pour aligner sur le pattern ### Phase 1 — pivot ticker voisin.
  • ddd.md : Bounded context portfolio aligné sur le pattern emoji des autres lignes (Actif✅ Phase 0+). Description de infrastructure/llm/ clarifiée — @ConditionalOnProperty est un pattern Phase 1 conservé en attendant l'item backlog "Config runtime v2 LLM" qui basculera sur un RoutingLlmClient (lève l'asymétrie avec market/news déjà en routing per-call).
  • developpement.md : Table des types Conventional Commits réalignée sur la source de vérité commit-conventions.md — ajout de docs, perf, audit, revert qui manquaient.
  • ops.md : Section "Roadmap CI / ops" nettoyée — "ESLint frontend" et "Settings runtime" retirés (livrés Phase 2), ajout d'une entrée "Cache Vitest en CI" (à mesurer avant code) reflétant la dette technique encore ouverte. Liens vers detekt.yml, eslint.config.js et dependabot.yml convertis en URLs GitHub absolues — ces fichiers vivent hors du docs_dir MkDocs et les liens relatifs ../../ cassaient sur le site déployé.

projet/

  • backlog.md : Phase 0 — entrées "Devise & valeur de marché" et "Persistance des jobs d'analyse" mises à jour. Les anciens numéros V5/V6/V7 étaient caducs après la consolidation du schéma — V1 contient désormais les colonnes currency / book_value_cad / market_value / unrealized_gain / gain_currency ainsi que la table analysis_job (vérifié sur disque). Dette technique — entrée FOUC du toggle thème : ⏳ retiré du sujet (incohérence ⏳ + ✅ Fait dans la même ligne).
  • etat-actuel.md : Liste des migrations Flyway complétée avec V5 asset_lifecycle qui manquait au bilan, alors qu'elle est le sujet d'un livrable Phase 2 documenté plus haut dans le même fichier.

Racine

  • README.md : Tableau Documentation enrichi de deux entrées qui manquaient — "État actuel" (docs/projet/etat-actuel.md, cible du lien "Pour aller plus loin" de developper.md) et "Changelog doc" (docs/CHANGELOG.md, source de vérité de l'évolution du doc set). Tous deux étaient présents dans mkdocs.yml nav mais invisibles à qui lit le README en local.

2026-05-04 (suite 2 — idées backlog Phase 2)

projet/

  • backlog.md : Deux nouvelles entrées Phase 2 ⏳ ajoutées par le user :
  • Réordonner les portfolios par drag-drop dans la sidebar 🟢 Basse — drag handle CDK + persistance localStorage portfolio-order. Sous-ensemble de l'entrée "Sidebar modulaire" déjà existante mais livrable indépendamment.
  • Config runtime v2 : LLM provider + model éditable depuis l'UI 🟡 Moyenne — extension naturelle de /settings/configuration Phase 2 v1. Trois clés à ajouter (llm.provider, ollama.model, anthropic.api.model), nouveau RoutingLlmClient @Primary, retrait des @ConditionalOnProperty sur Claude/Ollama clients. Bouton "Tester" qui mesure latence + parse correctness sur un mini prompt fixe. Motivation : fluidifier les tests A/B model quand on bascule sur une machine plus puissante (au-delà de qwen-3B / Mistral-7B testés en Phase 1).

2026-05-04 (suite — fix lifecycle position CSV)

projet/

  • backlog.md : Nouvelle entrée Phase 2 livrée — lifecycle de position OPEN/CLOSED dans l'import CSV (V5). Notes d'implémentation détaillées : justification observabilité (vs hard delete), comportement import, queries OPEN-only, UI counters.

Ce drift n'avait pas été remonté par l'agent doc-maintainer parce que c'était un bug fonctionnel (CSV import upsert sans cleanup) plutôt qu'un drift doc → code. Le code fix entraîne ensuite des updates doc en cascade — ils sont consignés ici.


2026-05-04

metier/

  • fonctionnalites.md : Phase 2 "Settings & config runtime" basculée en ✅ Livré, scope élargi à 5 clés (Twelve Data + Finnhub + cache TTL + switch providers market.provider / news.provider).

technique/

  • architecture.md : Nouveau module backend config/ documenté (AppConfigService, ConfigController, ConfigTestClient). Décisions techniques notables enrichies : "Configuration runtime éditable" et "Switch provider à chaud". "Trois clés" → "cinq clés" (intro module config). Settings tabs frontend listent désormais configuration. "7 repositories" → "8" côté frontend (ajout Config). V4 ajoutée au tableau des migrations Flyway.
  • developpement.md : Prérequis Java 21 mentionne le pin JVM via backend/gradle/gradle-daemon-jvm.properties. Section configuration locale renvoie vers la page runtime /settings/configuration comme alternative à l'édition application-local.yml. Nouvelle section "Lint et formatage" couvrant Spotless+Detekt côté back et ESLint+Prettier côté front. Arbre projet enrichi avec config/ côté backend et Config repository côté frontend.
  • developper.md : Section "Switcher les providers" promeut la page runtime comme alternative à l'édition YAML. Nouvelle entrée troubleshooting npm run lint (patterns récurrents : prefer-inject, a11y click-events-have-key-events, label-has-associated-control).
  • ddd.md : Nouveau bounded context config ajouté au tableau (Phase 2). Couche infrastructure/ enrichie avec RoutingMarketChartClient / RoutingNewsClient (@Primary, dispatch per-call). Nouvelle section "config/ (Phase 2)" qui documente la structure du module et le pattern event-driven (CacheTtlListener cross-context).
  • ops.md : Pipeline Frontend CI documenté avec npm run lint avant le build. Nouvelle section "ESLint — analyse statique TypeScript / Angular" en pendant de la section Detekt (extends, ruleset, commandes locales).
  • providers.md : Correction typo modèle Claude — claude-sonnet-4-6 (n'existe pas) → claude-sonnet-4-5.

projet/

  • backlog.md : 4 entrées Phase 2 / dette technique livrées (Settings runtime, Cleanup jobs orphelins au boot, Linter ESLint frontend, Agent Claude spécialiste doc). 1 nouvelle entrée dette technique ajoutée : provideRepositories() côté frontend (extraction des 8 lignes répétitives de app.config.ts).
  • etat-actuel.md : Section Phase 2 enrichie de 4 nouveaux livrables (settings runtime, jobs orphelins, ESLint, doc-maintainer). V4 ajoutée au compte des migrations. "Restant à attaquer" nettoyé (settings runtime retiré, items vraiment ouverts conservés).
  • sources.md : Note "Switch runtime" sous le tableau Finnhub — depuis Phase 2, market.provider et news.provider sont éditables en direct depuis /settings/configuration sans reboot backend.

.claude/

  • CLAUDE.md : Compteur frontend "7 repositories" → "8". npm run lint ajouté aux Frontend Commands. Nouvelle convention ESLint flat config + eslint-config-prettier + non-recommandation de recommended-type-checked. Ligne ajoutée au tableau "Documentation" pour docs/CHANGELOG.md (à updater en fin de chaque /doc-maintainer patch session).
  • agents/doc-maintainer.md (nouveau) : Subagent read-only (Read, Glob, Grep ; pas de Bash, pas d'Edit) qui audite le doc set. 3 capacités : cross-check factuel, ton, cross-link integrity. Sortie = punch-list HIGH/MED/LOW. docs/CHANGELOG.md ajouté au tableau des docs sous responsabilité (cross-link checked, mais jamais écrit par l'agent).
  • skills/doc-maintainer/SKILL.md (nouveau) : Slash command /doc-maintainer qui spawne l'audit en contexte isolé. Section "After patches are applied — update the CHANGELOG" ajoutée pour codifier la discipline post-patch (le main thread écrit l'entrée, format Keep-a-Changelog allégé groupé par area).

Racine

  • mkdocs.yml : Nav enrichie avec technique/ddd.md et projet/etat-actuel.md qui étaient orphelins (présents en repo mais pas servis sur le site). docs/CHANGELOG.md ajouté en première section "Accueil" de la nav.
  • docs/CHANGELOG.md (nouveau) : Création de ce fichier. Log doc reverse-chronologique maintenu en fin de chaque session /doc-maintainer par le main thread. Le subagent reste read-only et ne touche pas ce fichier.