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-markdéfinies dansstyles.scsssur:root(dark default = tuile blanche + tracé sombre, pop sur toolbar Material primary violet) et[data-theme='light'](inversées). Le SVG consomme les vars viafill="var(...)"+stroke="var(...)"avec fallback inline. Favicon indépendant adaptatif viaprefers-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: nouveauprovideAppInitializerqui registre'portfolioai'dansMatIconRegistry.addSvgIcon('portfolioai', sanitizer.bypassSecurityTrustResourceUrl('img/logo/logo.svg'))au boot. Consommé partout via<mat-icon svgIcon="portfolioai">— toolbarAppet landing/login, zéro duplication, zéro SVG inline. CommeMatIconRegistryinline le SVG dans le DOM, les CSS variables du parent propagent → le toggle de thème viaThemeServicese 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/loginen 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_statsIndicateurs calculés /auto_storiesNarration IA /account_balance_walletPortefeuille connecté) + CTA Google + disclaimer. Plus de card framing — fond plein viewportvar(--color-bg)max-width 760 px, grid 3 cols qui stack sous 720 px. Nouvelles clés i18n FR+EN sousauth.login.tagline+auth.login.features.{indicators,narrative,portfolio}.{title,description}, ancienauth.login.subtitleretiré. Testlogin-page.spec.tsadapté 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. TicketsRevoir les redirections AngularPhase 5 fermé.src/app/app.spec.ts: testshould render the toolbar brand mark on a non-login routealigné sur la nouvelle structure logo-only (.brand-markmat-icon présent, plus le texte.brand-name).public/i18n/{fr,en}.json:auth.login.subtitleretiré,auth.login.tagline+ blocauth.login.features.{indicators,narrative,portfolio}ajouté en FR + EN.src/styles.scss: nouvelles CSS variables--logo-tile/--logo-markdéfinies dans les deux blocs de thème.src/index.html:<title>Frontend</title>boilerplate Angular CLI →<title>PortfolioAI</title>. Lien faviconimage/svg+xmlpointant surfavicon.svg. Pas detitle:par route — Angular Router ne touchedocument.titleque si déclaré, donc le tag HTML suffit comme défaut.
.github/workflows/
deploy.yml: nouveau workflow Cloud Run, ~95 lignes. Triggeron: release: published(pre-releases-rcNinclus),concurrency: deploy-productioncancel-in-progress: false,environment: production(required reviewer self-approve),permissions: { contents: read, id-token: write }. Pipeline :actions/checkout@v4→docker/setup-buildx-action@v3→google-github-actions/auth@v2via WIF →setup-gcloud@v2→gcloud auth configure-docker northamerica-northeast1-docker.pkg.dev→docker/build-push-action@v6platforms: linux/amd64(natif amd64 ubuntu-latest, pas de QEMU émulation, gain ~5-10×) + labels OCIorg.opencontainers.image.{source,revision,version}→gcloud run deploy portfolioai --service-account=portfolioai-runtime@... --update-secretsavec 4 secrets mountés depuis Secret Manager +--set-env-vars SPRING_PROFILES_ACTIVE=prod+ flags resources → smokecurl /actuator/healthpost-deploy + résumé markdown dans$GITHUB_STEP_SUMMARY. Pas deAPP_FRONTEND_URLexplicite — le default/du baseapplication.ymlmarche puisque backend + SPA sont même origine.backup-postgres.yml: nouveau workflow backup Postgres, ~80 lignes. Triggercron '0 4 * * 0'(dimanche 4 AM UTC, weekly) +workflow_dispatchmanuel. Concurrencybackup-postgresnon-cancelable. WIF auth → installpostgresql-client-16depuis l'apt repo officiel Postgres (forward-compat vs PG 15 Supabase, +1 majeur de buffer) → fetchsupabase-db-urlSecret Manager → strip préfixejdbc:pour libpq URI compat →pg_dump --no-owner --no-acl --format=plain | gzip > backup-<ISO-timestamp-UTC>.sql.gz→aws s3 cpvers 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-18avec liens versdeploy.yml+release-process.md+backup-process.md.
docs/devops/
release-process.md: nouveau. Rituel deploy pas-à-pas (tag → Draft Release → Publish → approveproductionenvironment → workflow part → smoke browser), 3 variantes de tag CLI (gh release createdirect, 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é dansmkdocs.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 secretsR2_*+ 3 GH vars repo-levelGCP_*+ grantsecretmanager.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 pasenvironment: production→ fix repo-level). Câblé dansmkdocs.yml.liens-utiles.md: section Cloudflare réécrite (de « pas encore configuré » à 3 liens directs bucket / dashboard / API tokens avec Account ID8f2780696b5e520f85b5fc80413c4c3fsubstitué) + bloc CLIaws s3pointé sur R2 + note DNS / proxy à venir. Sous-section GitHub Actions enrichie dedeploy.yml+backup-postgres.yml. Pointers internes versrelease-process.md+backup-process.mdajoutés à la section Référence projet.deploiement.md: (a) §5 Phase 5a tableau ligneBackup weekly(vsBackup nocturne) avec cron weekly + lien versbackup-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 squelettedeploy.ymlretiré, remplacé par pointer vers le fichier livré + lienrelease-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 dedeploy.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: writeau scope job pour WIF.
docs/projet/
backlog.mdPhase 5 : tickets fermés — Workflow GitHub Releases (🔴 → livré) et Backup Postgres nocturne (🔴 → livré weekly). Tickets ajoutés en session — Industrialiser le versionning (🟡, drift git tagsv0.6.0vs Artifact Registryv0.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 delocalStorage: 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.mdPhase 5 : 3 nouvelles entrées ✅ — Identité visuelle frontend + landing + wildcard 404, Workflowdeploy.ymlCloud 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 ID8f2780696b5e520f85b5fc80413c4c3f. - GitHub : 3 secrets
R2_ACCOUNT_ID/R2_ACCESS_KEY_ID/R2_SECRET_ACCESS_KEYposés au repo level + 3 variables repo-levelGCP_PROJECT/GCP_WIF_PROVIDER/GCP_SA_EMAIL(doublonnent celles déjà présentes dans l'environmentproduction— c'est voulu, voirdeploiement.md §6.1patché). 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→ workflowdeploy.ymldé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.com ∈ APP_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 lecp -r ./frontend/dist/*/browser/* ./backend/src/main/resources/static/du Stage 2 — Spring Boot ne crée passtatic/physiquement dans le repo (juste à la volée au runtime), donc le cp Docker plantait surtarget 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 blocapp:(overridesapp.frontend-url+app.admin.emailsredondants — le baseapplication.ymlles 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 blocspring.security.oauth2.client.registration.google:(l'override prod avait un placeholder${APP_FRONTEND_URL}sans default qui écrasait le default/du base et crashaitSecurityConfigau boot avecCould not resolve placeholder 'APP_FRONTEND_URL'). Le baseapplication.ymln'a intentionnellement pas ce bloc OAuth (ligne 36-55 commentaire qui explique pourquoi :ClientRegistration.Builder.build()crash surAssert.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/SECRETmounté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 depermitAllpuisanyRequest().authenticated()(conçu pour archi dev Phase 4 où la SPA Angular tourne sur:4201sé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 queSpaFallbackConfigpuisse forward surindex.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")WebMvcConfigurerqui câble un resource handler/**avec unPathResourceResolvercustom. Logique : (a) si le path commence parapi//actuator//oauth2//login/oauth2//swagger-ui//v3/api-docs/, retournenull(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 dansstatic/(e.g./main.abc.js), le sert directement, (c) sinon (toutes les routes Angular client-side), forward versindex.html— Angular Router prend le relais browser-side pour rendre la bonne vue. Actif uniquement enprodparce qu'en dev (tilt up) la SPA tourne sur Angular CLI port 4201 avec son propre SPA fallback, Springstatic/est vide.
Côté Cloud (manual ops, déjà côté GCP avant cette session)
- Cloud Run service
portfolioaicréé ennorthamerica-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-secretsmountent 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 danspublic.flyway_schema_history). Note opérationnelle : l'erreur dashboard Supabaserelation "supabase_migrations.schema_migrations" does not existobservé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 CLIsupabase 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 + Supabaseretiré de la section 🔴 Phase 5 (livré, voir journal). (2) Ticket⏳ Workflow GitHub Releasespromu 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 : commandedocker build --platform linux/amd64, path Artifact Registry, flags exactsgcloud run deployvalidés ce soir, état "tout est prêt" côté GCP/GitHub/Supabase, et modèle release-triggered explicite (on: release: published, NOTon: 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 + Supabaseajouté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 dansstatic/, Stage 3 Eclipse Temurin 21 JRE minimal (~200 MB final) runtime avec utilisateur non-rootspringet JVM tuning containerisée (-XX:MaxRAMPercentage=75 -XX:+UseG1GC). Build invocable depuis la racine viadocker 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 APIserving.knative.dev/v1) qui documente le shape complet attendu : annotations scaling (maxScale=3,minScale=0scale-to-zero,startup-cpu-boost=true), serviceAccount runtimeportfolioai-runtime@, healthchecksstartupProbe+livenessProbesur/actuator/health, ressourcesmemory=1Gi cpu=1, env vars (SPRING_PROFILES_ACTIVE=prod,APP_FRONTEND_URL) + secrets mountés viasecretKeyRefsur les 4 secrets Secret Manager (google-oauth-client-id,google-oauth-client-secret,app-admin-emails,supabase-db-url). TagsTODO_TAG(image) etTODO_URL(custom domain) à remplir au moment du 1er deploy. Sera utilisé soit en référence pourgcloud run deployimperatif soit pourgcloud run services replace --file service.yamldé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 ticketProvisionner v1(build local Docker → push Artifact Registry → premiergcloud run deploymanuel → smoke test end-to-end → workflow.github/workflows/deploy.ymlon: release: published). Pointe vers../deploiement.mdpour 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 profillocalpar construction → safe à committer.backend/src/main/resources/application-prod.yml:git mvdepuisdevops/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 quandSPRING_PROFILES_ACTIVE=prodest posé par Cloud Run viaservice.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 dansdevops/local/. Les symlinks racine créés pour préservertilt 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 + Supabase→Backup Postgres nocturne.journal-livraisons.md: entrée✅ Dossier devops/prod/ + commit des Spring profile YAMLsajoutée en tête de Phase 5 (au-dessus deGitHub Secrets vaultqui est aussi du jour). Documente la trajectoire scope (idée initiale symlinks → rollback) + le ré-cadrageapplication-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 fichierapplication-local.yml(gitignored) contient... » réécrit en « (committé depuis 2026-05-18, sans secrets) ». (3) Paragraphe « Ne jamais committerapplication-local.ymlni.env» réécrit pour cibler.envuniquement, avec ajout d'une règle explicite «application-local.ymlest désormais committé mais doit rester strictement secret-free ».developper.md: sectionConfigurer le LLMréécrite end-to-end. L'ancien flow disait « Crée le fichierapplication-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 BDDapp_config, aucun reboot) et (A.2) boot-timeANTHROPIC_API_KEY=...dans.env. Option B Ollama est désormais le défaut "rien à configurer". Encadré « ne jamais coller la clé Anthropic dansapplication-local.yml» ajouté. Les colonnes TwelveData et Finnhub du tableau providers reçoivent la même mise à jour (clé en.envou UI, jamais YAML committé).ops.md: (déjà mis à jour le matin pour mentionnersmoke-wif.ymlworkflow). Aucun changement supplémentaire ici.
.claude/
CLAUDE.md: 4 patches. (1) Repository Structure simplifié —devops/ne contient plus queprod/; les commentaires d'annotation symlink retirés deTiltfileetdocker-compose.ymlà la racine. (2) Local Development — précision queapplication-local.ymlest désormais committé sans secrets, avec renvoi versData & secretspour 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 queapplication-local.yml+application-prod.ymlsont 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 lignebackend/src/main/resources/application-local.ymlretiré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.envou GCP Secret Manager).application-prod.ymlheader (dans son nouveau homebackend/src/main/resources/) : note « À déplacer au moment duProvisionner v1ticket » retirée, remplacée par une explication du chargement automatique Spring native quandSPRING_PROFILES_ACTIVE=prodest 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 manuelworkflow_dispatchqui valide le pipeline Workload Identity Federation de bout en bout : (1) obtention OIDC token avecpermissions: id-token: write, (2)google-github-actions/auth@v2échange contre access token GCP via le poolgithubet le SAgithub-deploy@, (3)setup-gcloud@v2configure le CLI, (4)gcloud run services list --region=northamerica-northeast1exerceroles/run.admin, (5)gcloud artifacts repositories describe backend --location=northamerica-northeast1exerceroles/artifactregistry.writer. Utiliseenvironment: productionpour 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/prodretiré de la section 🔴 Phase 5 (livré, voir journal). Phase 5 🔴 restants :Dossier devops/skeleton →Provisionner v1 Cloud Run + Supabase→Backup Postgres nocturne. (2) Nouveau ticket dansDette 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 vaultajouté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 conditionrepository_owner == 'jv3n', binding sur principalSetattribute.repository/jv3n/trade, repo Artifact Registrybackendcréé à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 GitHubproductionavec required reviewer + branch policymasteronly, 3 Environment variables (pas secrets, parce qu'identifiants publics), Secret scanning + Push protection + Dependabot alerts activés, projet Supabaseportfolioai-prodcréé régionca-central-1Toronto. Header Phase 5 mis à jour pour refléter que la DB Supabase est enca-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 pourgcloud run deploy+ Workload Identity Federation (pas de SA JSON key) + workflow backup nocturnepg_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: sectionDécisions techniques notables > Phase 5 — déploiementréé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.io→Provisionner 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ésréécrit enBackup 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 Flyfusionné dansCloudflare devant Cloud Run— custom domain + TLS + cache + bypass egress quota Cloud Run free en un shot. (4)(Phase 5b) Ajouter Machine Ollama 4 GBretiré (caduc — Ollama exclu prod). (5) Nouveau ticketMonitoring uptime + Sentryajouté (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ébergementréé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 triggeron: release: publishedavecsuperfly/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-sectionDécisions techniques notables > Phase 5 — déploiementajoutée juste après le bloc Phase 4 (avantModèle pipeline d'analyse). Une décision documentée — Fly.io retenu comme hébergement v1 — qui condense les 3 arguments décisifs (latenceyyz~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 lignesfly.toml), et les rationales d'élimination (Railway pas de Canada, Render $7/mo par service, VPS self-hosted violerait la contrainte utilisateur). Pointe versdocs/devops/deploiement.mdpour 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égionyyzToronto), bootstrap ~$10/mo... » avec lien versdocs/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ébergementretiré 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 devientAnalyse hébergement → GitHub Secrets → devops/. Analyse hébergement est ensuite retirée (livrée) → l'ordre final 🔴 estGitHub Secrets → devops/ → Provisionner v1 Fly.io. (5) Quatre nouveaux tickets follow-up Phase 5 filed (issus dedeploiement.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 sectionPhase 5 — Déploiement (en cours)ajoutée en tête (avant Phase 4), avec header qui fixe le verdict provider + lien versdeploiement.md+ précision que les tickets ⏳ restent au backlog. Une entrée✅ Analyse hébergement — comparaison PaaS + recommandation Fly.iolivré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égionyyz, multi-Machine, Postgres unmanaged honnête), le plan phasé 5a/5b/5c, le pipeline GitOps tranché (triggeron: release: published,FLY_API_TOKENscoped), 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 sectionnav > Devops— « Déploiement (choix d'hébergement Phase 5) : devops/deploiement.md » entrecommandes-pratiques.mdetdecision-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 (563df74 → f9588c2) 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-tenantuser_idFK et (2) la redaction des emails dans les logs comme à faire — les deux sont livrés depuis03e8324ete553fc8. 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) SectionSché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é dansf9f5f23. Le nouveau paragraphe décrit la migration uniqueV1__init.sqlavec énumération des 11 tables dans l'ordre des FK + mentionbaseline-on-migrate: true+baseline-version: 0. (2) Sectionauth/ > Domain + persistence(ligne 208-210) — le bullet « Migration V9 crée uniquementapp_user. Aucune FKuser_idsur les autres tables en v1 » remplacé par un bullet qui décrit la FKuser_id NOT NULL ON DELETE CASCADEsurportfolioetwatchlist_entry(livrée dans03e8324, incluse dans le V1 unifié), avec mention de la contrainte UNIQUE(user_id, symbol)sur watchlist. (3) SectionModules frontend > core/(ligne 252) — description deauth.interceptor.tscorrigée : l'ancienne version disait « 5xx → redirect/erroravec query params status + url », faux depuis1e3a6d3qui a retiré ce comportement. Le nouveau texte précise « les 5xx ne sont pas interceptés — les composants gèrent en local ». (4) CompteurprovideRepositories(ligne 255) — « 14 bindings » → « 15 bindings » (Phase 4 a ajoutéAuth). (5) SectionDé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 decore/http/(interceptors) etcore/router/(guards) Phase 4. (6) Sectionauth/(ligne 220) — la mention de l'interface markerAppUserPrincipalenrichie d'une précision sur son emplacement enauth/domain/(déplacement Bloquant B2 du jour pour respecter la frontière hexagonaleapplication/ → domain/), et faute de typo « déliberément » → « délibérément » corrigée.ddd.md: (1) tableau Bounded Contexts (ligne 15) — entréewatchlistcorrigée : « single-table, pas de user_id » → « scopée user_id depuis Phase 4, UNIQUE(user_id, symbol)» (FK livrée dans03e8324). Statut passé à « Phase 2 — multi-tenant Phase 4 ». (2) Nouvelle ligneauthajoutée au tableau (Phase 4) — décrit le contexte OAuth2 + rôles + profile local-no-auth, comme source de vérité duuser_idconsommé parportfolio/watchlist. (3) SectionDépendances cross-contextes autorisées— 4 nouvelles lignes ajoutées au bloc ASCII pour documenter les flèchesportfolio.domain → auth.domain,watchlist.domain → auth.domain,portfolio.application → auth.application,watchlist.application → auth.application. Note explicative qui pointe vers la décision dansarchitecture.mdet fixe la limite de la tolérance (entities JPA seulement, pas les ports).developpement.md: (1) arborescence ASCIIcore/(lignes 157-164) — « split sur 3 axes » → « split sur 5 sous-dossiers » avec ajout des sous-dossiershttp/(interceptors Phase 4) etrouter/(guards Phase 4) ; « 8 bounded contexts » → « 9 bounded contexts » avecauth/ajouté à la liste des buckets ; mentionauth.service.tsajoutée au commentaire deapp-state/; « wires les 14 ports » → « wires les 15 ports ». (2) arborescence ASCIIbackend/(ligne 172) — nouveau moduleauth/listé en premier (Phase 4 — OAuth2 + ADMIN/USER + local-no-auth) ; les modulesportfolio/etwatchlist/annotés « multi-tenant via user_id FK depuis Phase 4 » ; mention « V4 » retirée deconfig/(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 decore/(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 +/logincôté frontend... la foundation backend est livrée en premier ; la migrationuser_idFK ... 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 avecbacklog.md:54qui marque la phase clôturée.audits/2026-05-17-pre-v0.6.0-phase4.md: nouveau fichier — audit pré-tagv0.6.0synthétisant 3 subagentscode-reviewerlancés en parallèle (sécurité multi-tenant + qualité backend + qualité frontend) sur la deltav0.5.1..HEAD. Verdict initialneeds-fixavec 2 Bloquants (driftarchitecture.mdschéma Flyway + violation hexagonaleAuthService→AppUserPrincipalen 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é — restreindreserver.forward-headers-strategyou 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 deauth.interceptor.tscorrigé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 commit1e3a6d3avait retiré ce comportement mais CLAUDE.md reflétait encore le plan initial. (2) Ligne 105 — description de la pageerror/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 litauth.lastErrorsignal. (3) Ligne 78 — bullet « Migration V9 créeapp_user... pas de FKuser_idsur les autres tables en v1 (ticket dédié) » remplacé par une description de la migration uniqueV1__init.sqlpost-squash + mention de la FKuser_id NOT NULL ON DELETE CASCADElivré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 unV9__app_user.sqlqui n'existe plus.
Racine
mkdocs.yml: sectionnav > Projet > Audits— ajout des deux entrées manquantes pour les audits récents (2026-05-17-pre-v0.6.0-phase4et2026-05-16-pre-v0.5.1). Les fichiers existaient sur disque depuis leur création mais n'étaient accessibles que viaaudits/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 ASCIIcore/réécrite — l'ancien commentaire « 14 repositories » à plat +*.repository.tsracine +adapters/*.http.ts+adapters/*.local.tsremplacé par les 3 axes (api/<bucket>/qui liste les 8 bounded contexts miroirs du backend + leuradapters/*.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 » — cheminfrontend/src/app/core/theme.service.tscorrigé enfrontend/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 — bloccore/é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) SectionModules 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 entreapi/etlocal/. 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)SymbolSearchServicedans la sectionmarket/ > Services applicatifs— ancienne formulation « expose aussivalidate(symbol)consommé parWatchlistService.add» remplacée par mention du split en deux beans (SymbolSearchServicecache +SymbolValidator@Componentséparé, 2026-05-15). Raison : un appel intra-beanthis.search()bypasserait le proxy AOP Spring et brûlerait un credit Twelve Data parwatchlist.add(). (5)AppConfigServicedans la section « Configuration runtime éditable » — mention des trois@Componentdata classes (SecretsDefaults,DataProvidersDefaults,LlmDefaults, 2026-05-15) qui regroupent les défauts YAML, avec note quemarket.cache.ttl-minutesreste 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 surcore/<name>.repository.tsetcore/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-previewsupprimée ») pour journaliser le refactorcore/3-axes. Inclut le détail du naming retenu (api/vshttp/ouremote/,local/vsclient-state/,app-state/vsui-state/), la cartographie des 8 buckets sousapi/, la note sur le cross-bucketllm-timeout.service→config/config.repositoryqui 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) : «PortfolioAggregationPhase 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é viagrep). 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 versarchitecture.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 6listait seulement 5 bullets sur les 9 tickets quebacklog.md > Phase 6contient.
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ètredoc-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 appelGrepavecglobcouvre la même surface qu'ungrep -rnBash et reste autorisé — préfère-le au réflexe Bash ». Le dry-run avait confessé avoir utiliségrep -rnBash plusieurs fois faute d'incitation à passer par le tool dédié. (3) Exception gros diffs — nouveau paragraphe qui décrit le fallback quandgit diff HEAD <file>excède la limite du sandbox (Read direct du fichier actuel + inférence, ou découpe par hunk viagit 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 selfest déprécié) ») — réécrit en « La correction est un bean séparé ; le pattern@Lazy selfest explicitement déprécié dans le projet (cf.spring-boot/SKILL.mdticket 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 rowSources de données(URL MkDocshttps://jv3n.github.io/trade/projet/sources/) entre « Journal des livraisons » et « Conventions de commit ». La pagesources.mdexistait dansmkdocs.yml > navdepuis 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ègledoc-maintainer.md:52(« nav additions might warrant a README row too »).
technique/
architecture.md: 2 occurrencesdette technique 🟡 ouverte/ouvertsur la stratégie de cache hétérogène réécrites endette technique 🟢 Basse — démoté 2026-05-16, cosmétique(lignes ~198 et ~254). Le ticket a été démoté ce matin dans lebacklog.md > Dette techniquemais le statut n'avait pas été propagé. Un lecteur dearchitecture.mdaurait 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 onaggregation/» → « 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 conventioncode-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 B2MarketUnavailableException → 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 dethis.timeoutService.refresh(). Régression runtime silencieuse introduite par22fa6f5(bascule Promise → Observable) :refresh()cold sans subscribe = no-op, le label « estimation max » sous le sliderllm.timeout-secondsrestait figé sur la valeur de boot jusqu'au prochain reload. Le test masquait le bug — il vérifiaittoHaveBeenCalledTimes(1)sans pin la subscription. Test durci dansconfiguration.spec.tsavec unSubject<void>sentinel qui vérifierefreshSubject.observed === truepost-action, et mocktimeoutServiceMock.refreshmigré devi.fn().mockResolvedValue(undefined)(Promise) àvi.fn(() => of(undefined))(Observable) pour aligner test et runtime.
projet/
backlog.mdDette 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,@Suppresswatchlist ; 3 frontend :effect()injection context contraint, pas de specsnapshot.repository.tsdédié, mocks Ollama Promise→Observable). Description self-contained pour ne pas dépendre de l'audit, mais pointer versaudits/2026-05-16-pre-v0.5.1.mdpour 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 vers2026-05-16-pre-v0.5.1.mdavec 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 candidatv0.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/biasavec 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) sectionanalysis/— 2 nouveaux paragraphes inline pour les livraisons #2 et #3 du 2026-05-14, qui décrivent leCoherenceScorer(heuristique pondérée sentiment 0.55 / keypoints 0.30 / length 0.15 discountée par price move, pas de LLM-as-judge) et leNarrativeBiasService(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 lisaitarchitecture.mdaprè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/observabilitypour inclureGET /bias, avec note sur la précédence routing (déclaré avant/{symbol}sinon Spring lieraitbiascomme path-variable). (3) Compteur frontend repositories bumpé 13 → 14 + ajout explicite deNarrativeBiasà la liste avec sa description (agrégats corpus narratif en 4 sections), phrase « regroupe les 12 bindings » alignée à 14. (4) Sectionfeatures/observability/étendue : énumère désormais les 3 routes (/observabilityindex avec chip biais,/observability/:symboltimeline + chip cohérence,/observability/bias4 sections cards) et mentionne l'entrée navbar « Observabilité » ajoutée après Dashboard.developpement.md: (1) arborescence ASCIIcore/— commentaire « 12 repositories » bumpé à 14, liste explicite des ports étendue deNarrativeFeedbackjusqu'àNarrativeBias(les 2 ports Phase 3 #1 et #3 manquaient), commentaireproviders.tsaligné « wires les 14 ports → adapters ». (2) Arborescence ASCIIfeatures/— ajout d'une entréeobservability/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 vsdevelopper.md(drift signalé par l'utilisateur post-audit) : retrait de la section « Prérequis » (Docker / Java 21 / Node 24 / Tilt) qui dupliquaitdevelopper.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 versdevelopper.mdpour le first-time onboarding. Retrait du bloc YAMLapplication-local.ymlminimal (anthropic / llm / ollama) qui dupliquait le détail narratif dedevelopper.md > Configurer le LLM—developper.mdreste single-source du first-time setup,developpement.mdne 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.envConflit de port (procédure + table 4 variables + notegitignored) qui dupliquaitdeveloppement.md— remplacé par un redirect compact qui pointe vers la section Conflit de port dedeveloppement.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 candidatv0.5.0). Format mirror du2026-05-10-fin-phase-2.5.md: scope + méthode (3 subagentsgeneral-purposeen 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 bloquantFALLBACK_TEMPLATEcaching patché en session avec test de régression, 1 importantpriceAtOrAfterborne basse filed dette tech 🟡, 8 mineurs filed dans une umbrella 🟢, 1 faux positif filtré (claim agent surpriceMoveBigDecimal scale qui n'existe pas).audits/index.md: nouvelle entrée en tête de l'historique pour pointer vers2026-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-lookuppriceAtOrAfter— 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 etmapperjacksonObjectMapper etnormalizeInstant). (2) Phase 3 marquée close 2026-05-14, tag candidatv0.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: sectionProjet > Audits— ajout des entrées2026-05-14 — Revue globale fin Phase 3et2026-05-10 — Revue globale fin Phase 2.5qui manquaient au nav. Sans ce sync, les liens depuisaudits/index.mdproduisaient un 404 sur le site MkDocs hébergé (MkDocs en modenav:explicite ne génère pas de page pour un fichier non listé).
.claude/
CLAUDE.md: sectionobservability/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-dossierbias/qui rend/observability/biasavec 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 gardeconfiguration/etprompt-preview/» était périmée depuis Phase 3 ; étendue pour inclureprompts/etprompts/:id/stats(gestion + scoring des prompts narratifs).
technique/
architecture.md: compteur frontend repositories bumpé de 10 → 12 (ajout dePromptPR3 +NarrativeFeedbackPR5) et phrase « regroupe les 10 bindings » alignée à 12. Sectionfeatures/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 queconfiguration/etprompt-preview/.developpement.md: arborescence ASCII du dossiercore/— commentaire « 10 repositories » et liste explicite des ports mis à jour pour inclurePrompt+NarrativeFeedback; commentaireproviders.tsaligné « wires les 12 ports → adapters ». Dossiersettings/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: ligneanalysisdu 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 sousinfrastructure/llm/qui explique pourquoi les prompts vivent dansanalysis/(détail d'implémentation du narratif, tableprompt_scorejointe à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 absolusgithub.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 dansjournal-livraisons.md > Phase 3, là où c'est sa place.
Racine
README.md: 14 liens de la table « Documentation » repointés des chemins relatifsdocs/<path>.mdvers les URLs MkDocs hostéeshttps://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.mdsont les sources. Pointer vers le.mdbrut 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 deREADME.mdau tableau « Doc set under your responsibility » avec la note explicite « doc-links table vers MkDocs hosted, pas vers les.mdrelatifs ». 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 contremkdocs.yml > nav(chaque row → URLhttps://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.mdajouté 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 avecmkdocs.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ésanalysis.repository.tsetsettings.repository.tsqui n'ont jamais existé) + 6 features dont 2 décommissionnées Phase 2.5 (history/,recommendations/) etticker/(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.tsmentionné comme wiring central, et les 5 features actuelles (dashboard,ticker,import,suivi,settings) avec courte description par feature. Lesettings/détaillé : 4 routes courantes (configuration,prompt-preview,prompts,prompts/:id/stats).skills/angular-component/SKILL.md+instructions/frontend/best-practices.md: retrait du mandatChangeDetectionStrategy.OnPushqui contredisait directementCLAUDE.md(« Default change detection strategy is fine, no need to addOnPusheverywhere »). Rationale ajouté en place : en mode zoneless (provideZonelessChangeDetection()dansapp.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'importChangeDetectionStrategyet la propriétéchangeDetection:sur le@Component({}), le frontmatterdescription: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-inOnPushlocalement.CLAUDE.md: trois zones synchronisées avec la livraison Phase 3. (1) Paragrapheanalysis/du tableau « Backend modules » étendu d'un bloc « Prompt management (Phase 3, livré 2026-05-10) » qui décrit leTickerNarrativePromptService(lookup BDD + cache 1 min + fallback hardcodé), lePromptScoreRecorder(row à chaque run, succès ou échec), et les endpoints/api/prompts+PATCH /api/narrative/snapshots/{id}/thumbs. (2) SectionFrontend modules > core/— compteur « Currently 10 repositories » corrigé en 12 avec ajout explicite dePromptetNarrativeFeedback. (3) SectionFeatures > settings/— sidenav décrit avec ses 4 routes actuelles au lieu de 2 (configuration/,prompt-preview/,prompts/Phase 3,prompts/:id/statsPhase 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 queTwelveDataSectorClassifiera été décommissionné le 2026-05-06 et remplacé parFinnhubSectorClassifier(/profileTwelve Data paid-tier only). Le chapeau de fin de phase 2 réécrit pour pointer la Phase 2.5 close (tagv0.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 colonneinstrument_type VARCHAR(20)nullable surwatchlist_entrypour persister le type capturé au POST add. Sectionconfig/ > Switch provider à chaud— la phrase « Cache key préfixée par adapter ⇒ pas de collision » remplacée par un paragraphe honnête qui distingue leMARKET_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. Sectionwatchlist/: rationale ajouté pour expliquer pourquoiWatchlistService.addest délibérément non-@Transactionaldepuis le commita608967(deux appels réseau hors Hikari, helper privépersistNewavec 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 —OrphanedJobCleanupListenerest en place depuis Phase 1 et flippe lesPENDINGorphelins enERRORau 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: sectiondomain/— exemple d'enums métier remplaceRecommendationStatus(Phase 0 décommissionné en V6, fichier supprimé) parInstrumentType+EarningsTime(Phase 2, vivants).ops.md: description de la décisionWildcardImportdésactivée — le commit cité « via Spotless+ktlint » corrigé en « custom step Spotless » avec un pointeur versarchitecture.md > Décisions techniques notablespour 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-5corrigé enclaude-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 « persistenceinstrumentTypeBDD 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), tagv0.4.0mentionné dans le titre. Le ticket dette🟢 Basse« Sweep doc drifts mineurs post-Phase 2.5 » narrowé : le findingdevelopper.md:195cleanup orphelins ferme via cette même session, reste seulement le sous-findingConfigTestClient.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: sectionanalysis/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 (JobPhaseenum 9 valeurs,JobEventdata class,JobEventPublisherpub/sub avec replay 60 s post-terminal, endpointGET /jobs/{id}/stream), ajout d'un item liste des 6 endpoints REST narratifs incluant le nouveauGET /jobs/pending(PR2.5 reattach). Sectionconfig/: retrait des références frontendPOLL_ABORT_SECONDS/NARRATIVE_POLL_ABORT_SECONDS(constantes supprimées en PR2) ; la phrase « lu par le pollermarket.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 ». SectionModules frontend > core/: 9 → 10 repositories, ajout deOllamaStatusdans la liste, mention deJobStreamServicecomme 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 unEventSourcesur/jobs/{id}/stream» et reçoit les transitionsLOADING_CONTEXT → … → DONE / ERRORen push avec compteur de secondes par phase (le bandeau de progression livré en PR3). La phrase « switcher LLM provider en éditantapplication-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: arborescencecore/mise à jour — 9 → 10 repositories avecOllamaStatusdans la liste, ajout dejob-stream.service.ts(Phase 2.5 SSE) à la liste des fichiers du dossier.ddd.md: table « Conventions de nommage » ligne « DTO entrée » — exempleUpdateSourceEnabledRequest(DTO Phase 0 décommissionné en V6) remplacé parUnloadModelRequest+UpdateConfigRequest(DTO vivants).
projet/
backlog.md: légende « Statuts » nettoyée —✅ Faitretiré (les ✅ vivent dansjournal-livraisons.mddepuis le split, plus aucun dans le backlog). Chapeau Phase 2.5 étendu pour refléter la densité réelle de la phase (Swagger UI,.envports, 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: sectionanalysis/— la phrase « doesn't leave the front polling forever » devient « doesn't leave the frontend SSE waiting indefinitely » (transport stale), ajout d'une mention duJobEventPublisher+ endpoint/jobs/{id}/streamqui surfacent la progression per-phase. SectionFrontend modules > core/: 9 → 10 repositories avecOllamaStatus, mention deJobStreamService(Phase 2.5 SSE), reformulation du rôle deLlmTimeoutService(« 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édurecp .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 versiondevelopper.md(mention de la mécanique d'injection viaserve_cmdcô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.exampledé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 (togglellm.provider, modèles autocomplete, slider timeout, cléanthropic.api.keySECRET).
technique/
architecture.md:config/— « onze clés » → « douze clés », ajout deanthropic.api.keyà la liste des SECRETs masqués.ConfigController— endpoint/test/{provider}étendu àanthropic.ConfigTestClient— mention dutestAnthropicKey(candidate). Lecture per-call dans les adapters —ClaudeClientajouté au trio (Twelve Data + Finnhub + Claude), avec note sur le pattern header per-request vsdefaultHeader()builder. LLM provider+model runtime — mention que la clé Anthropic suit désormais le même pattern. LLM timeout runtime — retrait deAnalysisJobStore.DEDUP_WINDOW_SECONDS(Phase 0 droppée), seulTickerNarrativeJobStore.pendingForreste,analysis.http.tsportfolio poller retiré (un seul pollermarket.http.tsnarrative depuis V6).developpement.md: (1) section/settings/configuration— « sept clés » → « douze clés », structuration en 4 catégories (secrets / toggles / strings / sliders) ; (2) arborescencecore/—(11 repositories)→(9 repositories), listing aligné (retraitAnalysisetSettingssupprimés V6), ajout deproviders.ts(provideRepositories()) et mention SSR-safe pourtheme.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 viaappConfig.getString(LLM_PROVIDER), les@ConditionalOnPropertyont é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 documentantPOST /api/config/test/anthropicet son round-trip Claude. Code YAML d'exemple — commentpull via Tilt button llm:pull-qwen→llm:ensure-model (idempotent).developper.md: tableau « Configurer le LLM » — bouton Tiltllm:pull-qwen→llm:ensure-modelaligné sur le vrai nom dans leTiltfile(ledeveloppement.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 folderdevops/rend la parenthèse redondante) ; (2) section Postgres — requêtes SQL suranalysis_job(table droppée V6) supprimées, ne reste queticker_narrative_job; (3) section « Vider la table RSS legacy (Phase 0 gelée) » — supprimée intégralement (tablefeed_articledroppée,AnalysisExecutorsupprimé,ingestion.rss.enabledn'existe plus) ; (4)AnalysisRunner.run→TickerNarrativeRunner.rundans la note restart Ollama ; (5)AnalysisExecutor.executeretiré du thread-dump exemple (seulTickerNarrativeExecutor.executereste depuis V6) ; (6) bouton Tilt UI exemple «llm:pull-qwen» → générique « boutons custom de pull modèle » (le nom exact change peu etllm:ensure-modelest documenté ailleurs).
projet/
commit-conventions.md: table « Scopes courants » — scopesingestionetrecommendationssupprimés (modules droppés V6, plus du gelé). Description du scopesettingsrafraîchie (« back-office, prompt-preview, configuration runtime » au lieu de « test-sources » qui n'existe plus). Exemple inlinefeat(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éesdocs/devops/(commandes-pratiques.mdetdecision-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 — clusterDevopsdé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.mdpour le narratif livré,backlog.mdpour ce qui reste,git logpour 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🟡 Brouillonjusqu'à arbitrage.
technique/
developper.md: section « Pour aller plus loin » — ligneetat-actuel.mdretiré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 ligneBacklogreformulée pour pointer le scope « ouvert seulement » et le split avec le journal.mkdocs.yml: navProjet:— entréeÉtat actuelretiré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 debacklog.mdnarrowée (« Open work only — ⏳/🚧/🧊/❌ + Dette technique. Shipped features live injournal-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 splitbacklog ↔ journal-livraisons(ticket⏳qui devrait être déplacé en✅dans le journal). (4) Section « Output format » — ancien exemple punch-list qui citaitPortfolio, Analysis, Settings(deux modules décommissionnés en V6) reformulé en placeholders<count>,<actual>,<noms>. Note :analysis_jobreste référencé danscommandes-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 dedocs/projet/* (sauf data-input/...)à inclure aussidocs/devops/*+ mention explicite duCHANGELOG.md. (2) Section « After patches are applied — update the CHANGELOG » — liste des areas étendue demetier/, technique/, projet/, .claude/, Racineà incluredevops/.
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 depuisbacklog.mdavec leurs notes d'implémentation détaillées intactes. Format reverse-chronological par phase (Phase 2.5 → 2 → 1 → 0 → Dette technique), à l'image dedocs/CHANGELOG.mdcô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 runtimeajoutée dans Phase 2.5 (parité avecmarket.twelvedata.api-keyetmarket.finnhub.api-keydéjà éditables au runtime ;ClaudeClient+ConfigTestClientlisent la clé via@Valuefigé aujourd'hui, pattern à aligner sur per-callappConfig.getString(ANTHROPIC_API_KEY)).
technique/
developper.md: section « Pour aller plus loin » scindée — lignebacklog.mdreformulée pour clarifier qu'elle ne liste que⏳/🚧, nouvelle ligne dédiée au journal des livraisons.
Racine
mkdocs.yml: navProjet:étend avec une nouvelle entréeJournal des livraisons: projet/journal-livraisons.mdplacée juste aprèsBacklog:.CHANGELOG.md: cette entrée elle-même.
.claude/
CLAUDE.md: (1) section### Backlogréécrite — explique le split en deux fichiers, le workflow « après livraison : entrée nouvelle dansjournal-livraisons.md, ligne⏳retirée debacklog.md», et retire l'item « ✅ Livré » de la convention d'ordering puisque le backlog ne contient plus de ✅. (2) Tableau « Documentation » : lignedocs/projet/backlog.mdreformulée (« holds only ⏳/🚧/❌/🧊 + Dette technique »), nouvelle lignedocs/projet/journal-livraisons.mdinsérée juste après. (3) Repository structure tree : commentaire de la ligneprojet/é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 : moduleingestion/supprimé, pipelineAnalysisExecutorsupprimé, pages/recommendations+/history+/settings/sourcessupprimé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 0supprimée intégralement. Frontend modules :recommendations/, history/ — gelé Phase 0retiré,settings/reformulé pour refléter les seules entrées restantes (configuration/+prompt-preview/). Schéma BDD : passe de 5 à 6 migrations Flyway avecV6__drop_phase0.sqldocumenté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 0retirée — la migration cible part maintenant d'un seulticker_narrative_job. Compteur repositories frontend : 11 → 9 (Analysis et Settings repositories supprimés).ddd.md: contexteingestionretiré du tableau des bounded contexts (était🧊 Legacy gelé Phase 0). Note sur le contexteanalysisreformulé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èchesanalysis (legacy) → portfolio.infrastructure.persistenceetc.) supprimée.developpement.md: arborescence projet retirerecommendations/,history/,ingestion/et la mention « legacy reco portfolio gelé » suranalysis/. La lignesettings/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 legacycomme é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 etgit logpour 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 schedulersupprimée ; ligneanalysis/ # Phase 1 ticker narrative (legacy reco pipeline frozen)reformulée. (3) Backend modules table : ligneingestion/ — 🧊 legacy Phase 0supprimée ; blurbanalysis/allégé (suppression de la phrase « Legacy portfolio-wide pipeline (AnalysisExecutor,RecommendationValidator, etc.) is frozen in place ») et étendu pour mentionnerOrphanedJobCleanupListener. (4) Frontend modules : compteur11 repositories→9 repositories(Analysis et Settings retirés), mentionLlmTimeoutServiceajoutée. Settings sub-list reformulée (retirerecommendations/,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) BulletMarketChartClient— la description du gating front d'instrumentTypepasse d'une mention « gate la Sector benchmark » à l'énumération des trois affordances gardées (toggle Sector + section Fondamentaux entière + fetchesloadAnalyst/loadEarningsnon lancés à l'init mais depuis le success deload()). (2) Même bullet —« dégrade ouvert »reformulé en « dégrade fermé » avec rationale (le toggle leakait sur les ETFs pendant lenullwindow initial du snapshot). (3) Même bullet — la phrase « Twelve Data le populate depuis/quote.type» étendue pour mentionner le fallbackseriesResponse.meta?.type(observé NVDA free tier).
projet/
etat-actuel.md: (1) Bullet « Settings & config runtime » —« cinq clés »→ « sept clés », ajout deanalyst.provideretearnings.providerdans la liste des toggles, ajout deRoutingAnalystClient+RoutingEarningsClientà la liste des dispatchers@Primary, mention du hint conditionnel Sector / Finnhub. (2) Bullet « Comparaison vs benchmark v2 » — le drift préexistantTwelveDataSectorClassifiercorrigé : la doc mentionne maintenant le swap 2026-05-06 versFinnhubSectorClassifier(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 modifieticker.tsou 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 (parentPortfolioAggregation+ N feuillesTickerAnalysis(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 tablejobunifiée (colonnesid,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 tablejob, la machine à états, la dedup déterministe parcache_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, parentPortfolioAggregationqui digère les N narratifs déjà persistés au lieu de re-prompter sur les indicateurs bruts, nouvelle tableportfolio_analysis_snapshotavec 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'invariantPOLL_ABORT_SECONDS ≥ DEDUP_WINDOW_SECONDS ≥ 2 × OllamaClient.readTimeout + margedevientPOLL_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é runtimellm.timeout-seconds(INT, range 60–900, par défaut 400) consommée par les trois bornes (OllamaClient,POLL_ABORT_SECONDSfront,DEDUP_WINDOW_SECONDSback). 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 inclureOllamaClient HTTP readdans 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.provideretearnings.providernon 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 dansbacklog.md).
technique/
architecture.md: (1) Section « Switch provider à chaud » — passe de 4 dispatchers@Primaryà 6 listés explicitement (RoutingMarketChartClient,RoutingSymbolSearchClient,RoutingSectorClassifierdansmarket/, plusRoutingNewsClient,RoutingAnalystClient,RoutingEarningsClient). Note ajoutée sur le routage Sector → Finnhub. (2) Phrase d'intro modulemarket/—« chacun avec un adapter TwelveData* et un adapter Mock* »assouplie pour refléter queSectorClassifierdévie côté live versFinnhubSectorClassifier. (3) Schéma ASCII vue d'ensemble — ordre des portschart + sector + symb.search→chart + symb.search + sectorpour 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 parFinnhubSectorClassifierqui hit/stock/profile2free 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ée2026-05-06 — Revue globale fin Phase 2ajouté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 touchebacklog.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 format2026-05-02-revue-globale.md(résumé exécutif + findings par sévérité avec reffichier: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: exposeranalyst.provideretearnings.providercô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 modemarket.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: Sectionmarket/Quote — mention du nouveau champTickerQuote.instrumentTypeet de l'enumInstrumentType. Twelve Data mapper note la consommation du champtypedu/quotejusqu'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: Sectionmarket/Sector —TwelveDataSectorClassifierremplacé parFinnhubSectorClassifier(mention explicite « RemplaceTwelveDataSectorClassifierdepuis 2026-05-06 — Twelve Data/profileest paid-tier only »). Description deRoutingSectorClassifierenrichie pour pin la décision de routage live → Finnhub (le togglemarket.providerreste, le détail Finnhub est interne).ddd.md: Coucheinfrastructure/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 —/profileretiré de la colonne Endpoints (avec note explicative),TwelveDataSectorClassifierretiré de la colonne Adapter,sector-by-symbolretiré de Cache (passe côté Finnhub). Tableau Finnhub —/stock/profile2ajouté avec note de remplacement,FinnhubSectorClassifierajouté à la colonne Adapter,sector-by-symbolajouté à Cache.
projet/
sources.md: Tableau Twelve Data — ligne/profilesupprimée. Tableau Finnhub — ligne/stock/profile2ajoutée avec noteRemplace /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: SectionModules frontend— description du moduleticker/enrichie pour refléter le passage en layout 2-col avec la sidenav outils chart à gauche (Amazon-style, foldable, sticky, état localStorageticker-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 moduleticker/dans## Frontend modulesenrichie 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, helpercomputeSurprisePercent, signaux scopés à la panel, helpersearningsCountdownDays+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 moduleearnings/à la liste backend. (2) SectionCaches Caffeine— passe de 5 caches à 6 avecearningsajouté. (3) Nouvelle section### earnings/ — nouveau, Phase 2modelée sur la sectionanalyst/voisine — port + adapters + routing + service + endpoint + erreurs, narratif (pas bullets secs) pour rester cohérent avec la voix du fichier. (4) Sectionconfig/—six clés→sept clésavecearnings.providerajouté à l'énumération. (5) Décision technique « Switch provider à chaud » — mention deRoutingEarningsClientà côté des trois routings existants. (6) SectionModules frontend—10 repositories→11avecEarningsajouté à la liste nominale.ddd.md: (1) Tableau Bounded Contexts — nouvelle ligneearnings | 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}/— ligneearnings/ajoutée sousanalyst/avec triplet adapters + routing. (3) Nouvelle section### infrastructure/earnings/ (earnings uniquement, Phase 2)en prose (pattern voisin denews/etanalyst/) qui décrit le triplet, le partage duRestClientFinnhub via@Qualifier, le placement du cache au niveau service et l'isolation des mappers purs.developpement.md: (1) Arbre projet backend — moduleearnings/ajouté entreanalyst/etconfig/. (2) Compteur frontend10 repositories→11avecEarningsajouté à l'énumération nominale. (3) Bloc « Alternative runtime » —six clés→sept clésavec mention du toggleearnings.provider.developper.md: (1) Section « Switcher les providers » —quatre providers configurables→cinq, ajout d'une nouvelle sous-section### Earnings — earnings.provideravec tableau mock/finnhub aligné sur le pattern news/analyst, mention explicite que le toggle est séparé denews.provideretanalyst.provider. (2) Liste des chemins de modification — mention deearnings.providerdans le toggle de la page/settings/configuration.providers.md: Tableau Finnhub — titrenews par ticker + recommandations analystes→news 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 documenteMockEarningsClientavec 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 toggleearnings.provider.
projet/
backlog.md: (1) Phase 2 « ⏳ À faire — items ticker restants » — ligne⏳ Earnings dates et derniers résultatsdé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 intro2 items ci-dessous→l'item ci-dessous(plus que news inline). (2) Phase 2.5 « refonte chart-toolbar » — référence2 items ticker restants (earnings, news inline)→l'item ticker restant (news inline).sources.md: Section Finnhub — titreNews par ticker et recommandations analystes→News par ticker, recommandations analystes et earnings, intro élargie pour expliquer le partage de la clémarket.finnhub.api-keyentrenews.provider,analyst.provideretearnings.provider. Tableau étendu aux deux nouveaux endpoints (/stock/earnings+/calendar/earningsavec note fail-soft). Note « Switch runtime » ajouteearnings.providerà la liste des toggles disponibles depuis/settings/configuration.commit-conventions.md: Tableau « Scopes courants » — nouvelle ligneearningssousanalyst(cohérence de forme avec les autres modules backendmarket,watchlist,news,analyst, etc.).
.claude/
CLAUDE.md: (1) Repository structure — module backendearnings/ajouté à l'arbre sousanalyst/. (2) Backend modules — paragraphe dédiéearnings/ajouté sousanalyst/(port + adapters + fail-soft + mock symboles réservés + routing + cache + endpoint + mention du domain helpercomputeSurprisePercent), modèle de la voix de la section voisine. (3) Section config — mention deRoutingEarningsClientà côté des trois routings existants,analyst.provider→analyst.provider / earnings.provider. (4) Frontend modules —10 repositories→11avecEarningsajouté à la liste, description du moduleticker/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### ⏳ À venirvers### ✅ Livréavec résumé technique du livrable (portAnalystRecommendationClient, deux adapters viaanalyst.provider, fail-soft sur/price-target, cacheanalyst-recommendations, helperderiveConsensus60/50 %, helperanalystBucketPct, computedanalystTrend, signaux scopés à la panel). La ligne⏳ Recommandations analystescorrespondante retirée du À venir.
technique/
architecture.md: (1) Schéma ASCII vue d'ensemble — titreVue d'ensemble (Phase 1)→Vue d'ensemble(le schéma couvre déjà la Phase 2 entière), ajout du moduleanalyst/à la liste backend et de Finnhub aux sources de données. (2) SectionCaches Caffeine— passe de 4 caches à 5 avecanalyst-recommendationsajouté, formulationTous partagent le TTLvalidée (le code partage bienmarket.cache.ttl-minutes). (3) Nouvelle section### analyst/ — nouveau, Phase 2modelée sur la sectionnews/voisine — port + adapters + routing + service + endpoint + erreurs, narratif (pas bullets secs) pour rester cohérent avec la voix du fichier. (4) Sectionconfig/—cinq clés→six clésavecanalyst.providerajouté à l'énumération. (5) Décision technique « Switch provider à chaud » — mention deRoutingAnalystClientà côté des deux routings existants. (6) SectionModules frontend—9 repositories→10avecAnalystajouté à la liste nominale.ddd.md: (1) Tableau Bounded Contexts — nouvelle ligneanalyst | Recommandations d'analystes par ticker (consensus monthly + price target 12 mois, Finnhub + mock), cache court | ✅ Phase 2. (2) Tableau structure{context}/— ligneanalyst/ajoutée sousnews/avec triplet adapters + routing. (3) Nouvelle section### infrastructure/analyst/ (analyst uniquement, Phase 2)en prose (pattern voisin denews/etmarket/) qui décrit le triplet, le partage duRestClientFinnhub via@Qualifier, le placement du cache au niveau service et l'isolation des mappers purs.developpement.md: (1) Arbre projet backend — moduleanalyst/ajouté entrenews/etconfig/. (2) Compteur frontend9 repositories→10avecAnalystajouté à l'énumération nominale + ajout des portsAnnotation, Analystà la ligne descriptive. (3) Bloc « Alternative runtime » —cinq clés→six clésavec mention du toggleanalyst.provider.developper.md: (1) Section « Switcher les providers » —trois providers configurables→quatre, ajout d'une nouvelle sous-section### Recommandations analystes — analyst.provideravec tableau mock/finnhub aligné sur le pattern news/market, mention explicite que le toggle est séparé denews.providerpour permettre les combinaisons live news + mock recos pendant l'itération. (2) Liste des chemins de modification — mention deanalyst.providerdans le toggle de la page/settings/configuration.providers.md: Tableau Finnhub — titrenews par ticker→news 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 documenteMockAnalystClientavec ses trois symboles réservés. Cache liste les deux caches (news-by-symbol+analyst-recommendations). Bloc YAML « Récapitulatif config locale » enrichi du toggleanalyst.provider.
projet/
backlog.md: (1) Phase 2 « ⏳ À faire — items ticker restants » — ligne⏳ Recommandations analystesdé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 intro3 items ci-dessous→2 items. (2) Phase 2.5 « refonte chart-toolbar » — référence3 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 — titreNews par ticker→News par ticker et recommandations analystes, intro élargie pour expliquer le partage de la clémarket.finnhub.api-keyentrenews.provideretanalyst.provider. Tableau étendu aux deux nouveaux endpoints (/stock/recommendation+/stock/price-targetavec 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 » ajouteanalyst.providerà la liste des toggles disponibles depuis/settings/configuration.commit-conventions.md: Tableau « Scopes courants » — nouvelle ligneanalystsousnews(cohérence de forme avec les autres modules backendmarket,watchlist,news, etc.).
.claude/
CLAUDE.md: (1) Repository structure — module backendanalyst/ajouté à l'arbre sousnews/. (2) Backend modules — paragraphe dédiéanalyst/ajouté sousnews/(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 deRoutingAnalystClientà côté des deux routings existants,news.provider→news.provider / analyst.provider. (4) Frontend modules —9 repositories→10avecAnalystajouté à la liste, description du moduleticker/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⏳ À venircorrespondante.
technique/
architecture.md: Section "Modules frontend" — passage de "8 repositories" à 9 avecAnnotationRepositoryajouté à la liste nominale, mention du nouveau pattern d'adapteradapters/*.local.ts(client-only) en complément du*.http.tshistorique. Description du moduleticker/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 deSPY/QQQ/IWMàSPY/QQQ/IWM/Sector/Custompour cohérence avec le livrable v2 du jour précédent.developpement.md: Arbre du projet — passage de "8 repositories" à 9 + ajoutAnnotationà la liste des ports + ajout de la ligneadapters/*.local.ts # localStorage impls (annotation v3)sous la ligne HTTP existante. Section "Lint et formatage" —import …\.\*(artefact d'escape Markdown) reformulé enpackage.*lisible en source.developper.md: Section troubleshooting "Les tests Vitest ne reconnaissent pasdescribe" — recommandationnpx ng test/npx ng test --watch=false(Karma builder Angular) corrigée ennpm run test/npx vitest run src/path/to/file.spec.ts. Le projet utilise Vitest etng testrate 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, portAnnotationRepository9ᵉ + adapter localStorage avecdefer()pour quota-safety, measure tools recovered par timestamp). Section "Frontend" — picker benchmark étendu enOff/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" — compteurprovideRepositories()mis à jour de 8 lignes répétitives à 9.
.claude/
CLAUDE.md: Repository structure — ajout module backendconfig/(Phase 2 runtime-editable settings) dans l'arbre + section Backend modules avec un paragraphe dédié décrivantAppConfigService/ConfigController/RoutingMarketChartClient+RoutingNewsClient(@Primary) /CacheTtlListener. Frontend modules — passage de 8 à 9 repositories avecAnnotationajouté, mention du patternadapters/*.local.ts(client-only) à côté du*.http.ts. Description du moduleticker/enrichie de la couche chart analyse interactive (zoom + brush + overlays + annotations + measure). Patternadapters/*.http.tsassoupli à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 versdocs/technique/ddd.mdqui n'avait pas de point d'entrée depuis le README (référencé pararchitecture.mdet 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 (portSectorClassifier, 2 adapters viamarket.provider, mappingSpdrSectorEtfs11 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 sectionmarket/— passe de "1 port + indicateurs + 2 endpoints" à 3 ports (chart / symbol-search / sector) + 2 services applicatifs (SymbolSearchService,SectorClassifierService) +SpdrSectorEtfsdomain helper + 4 endpoints + 4 caches Caffeine. Schéma ASCII Phase 1 mis à jour (lignemarket/) 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 +.editorconfigplutôt que ktlint, avec mention explicite du faux départ (152 fichiers consolidés en*parce que ktlint applique la sémantique IntelliJ deij_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 deLlmClient.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 dansbuild.gradle.kts).ddd.md: Sectioninfrastructure/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 tripletTwelveData* + Mock* + Routing*. Mention deSpdrSectorEtfscommeinternal objectcolocalisé. 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 Spotlessno-wildcard-importset du.editorconfigracine comme couche de prévention IntelliJ.
projet/
sources.md: Tableau Twelve Data — ajout de deux endpoints jusqu'ici non documentés (/symbol_searchPhase 2 v2,/profilePhase 2 v2 → SPDR viaSpdrSectorEtfs). 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### ⏳ À venirvers### ✅ 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 featuresfrontend/features/enrichie pour le moduleticker/— 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 Tiltllm:pull-qwen→llm:ensure-model(alignement avecdeveloppement.mdetdevelopper.mdpatché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 (autocompletemat-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. Glyphe4ᵉ(exposant unicode) →4eligne 23 — cohérence avecfonctionnalites.mdpost-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 unicode4ᵉ→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 panelpostgresviacmd_button, etllm:pull-qwen→llm:ensure-model(renommage pour rendre l'idempotence explicite).developper.md: Section "Configurer le LLM" — référencellm:pull-qwen→llm: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 stepCache Angular build(jamais alimenté car.angular/cachenon créé en mode prod,Path Validation Errorau post-step). (3) Dependabot — "Scan hebdo lundi 06:00 Europe/Paris" → "Scan quotidien 06:00 America/Toronto" suite au passageweekly→dailyet 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/cachecôté frontend (le tente initial est explicitement consigné comme abandonné).
Racine
backend/build.gradle.kts: Commentaire au-dessus demockwebservermentionnaitYahooClientTest(classe supprimée au moment du switch Yahoo → Twelve Data en Phase 1) → corrigé pour citer les deux consommateurs actuelsTwelveDataClientTest+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 moduleconfig/(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### Frontendrenommé en### Phase 1+ — frontendpour aligner sur le pattern### Phase 1 — pivot tickervoisin.ddd.md: Bounded contextportfolioaligné sur le pattern emoji des autres lignes (Actif→✅ Phase 0+). Description deinfrastructure/llm/clarifiée —@ConditionalOnPropertyest un pattern Phase 1 conservé en attendant l'item backlog "Config runtime v2 LLM" qui basculera sur unRoutingLlmClient(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 dedocs,perf,audit,revertqui 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 versdetekt.yml,eslint.config.jsetdependabot.ymlconvertis en URLs GitHub absolues — ces fichiers vivent hors dudocs_dirMkDocs 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 colonnescurrency/book_value_cad/market_value/unrealized_gain/gain_currencyainsi que la tableanalysis_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 V5asset_lifecyclequi 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" dedevelopper.md) et "Changelog doc" (docs/CHANGELOG.md, source de vérité de l'évolution du doc set). Tous deux étaient présents dansmkdocs.ymlnav 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/configurationPhase 2 v1. Trois clés à ajouter (llm.provider,ollama.model,anthropic.api.model), nouveauRoutingLlmClient@Primary, retrait des@ConditionalOnPropertysur 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 providersmarket.provider/news.provider).
technique/
architecture.md: Nouveau module backendconfig/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ésormaisconfiguration. "7 repositories" → "8" côté frontend (ajoutConfig). V4 ajoutée au tableau des migrations Flyway.developpement.md: Prérequis Java 21 mentionne le pin JVM viabackend/gradle/gradle-daemon-jvm.properties. Section configuration locale renvoie vers la page runtime/settings/configurationcomme alternative à l'éditionapplication-local.yml. Nouvelle section "Lint et formatage" couvrant Spotless+Detekt côté back et ESLint+Prettier côté front. Arbre projet enrichi avecconfig/côté backend etConfigrepository côté frontend.developper.md: Section "Switcher les providers" promeut la page runtime comme alternative à l'édition YAML. Nouvelle entrée troubleshootingnpm run lint(patterns récurrents :prefer-inject, a11yclick-events-have-key-events,label-has-associated-control).ddd.md: Nouveau bounded contextconfigajouté au tableau (Phase 2). Coucheinfrastructure/enrichie avecRoutingMarketChartClient/RoutingNewsClient(@Primary, dispatch per-call). Nouvelle section "config/(Phase 2)" qui documente la structure du module et le pattern event-driven (CacheTtlListenercross-context).ops.md: Pipeline Frontend CI documenté avecnpm run lintavant 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 deapp.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.provideretnews.providersont éditables en direct depuis/settings/configurationsans reboot backend.
.claude/
CLAUDE.md: Compteur frontend "7 repositories" → "8".npm run lintajouté aux Frontend Commands. Nouvelle convention ESLint flat config +eslint-config-prettier+ non-recommandation derecommended-type-checked. Ligne ajoutée au tableau "Documentation" pourdocs/CHANGELOG.md(à updater en fin de chaque/doc-maintainerpatch 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.mdajouté au tableau des docs sous responsabilité (cross-link checked, mais jamais écrit par l'agent).skills/doc-maintainer/SKILL.md(nouveau) : Slash command/doc-maintainerqui 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 avectechnique/ddd.mdetprojet/etat-actuel.mdqui étaient orphelins (présents en repo mais pas servis sur le site).docs/CHANGELOG.mdajouté 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-maintainerpar le main thread. Le subagent reste read-only et ne touche pas ce fichier.