diff --git a/.ideai/background-tasks/97b49ac2-8376-4aa3-8ea9-bf3ac81d0023.json b/.ideai/background-tasks/97b49ac2-8376-4aa3-8ea9-bf3ac81d0023.json index 3cffa95..96ba5f2 100644 --- a/.ideai/background-tasks/97b49ac2-8376-4aa3-8ea9-bf3ac81d0023.json +++ b/.ideai/background-tasks/97b49ac2-8376-4aa3-8ea9-bf3ac81d0023.json @@ -8047,10 +8047,114 @@ "ticket_id": "5b46b1e5-0498-4a6f-98e9-73bc80881839", "conversation_id": "6bc594e8-a37c-0dbd-1de6-6e3b73002cb4" }, - "state": "running", + "state": "completed", "wakePolicy": "recordOnly", "createdAtMs": 1784271185727, - "updatedAtMs": 1784271185727, + "updatedAtMs": 1784271285452, + "deadlineMs": null, + "result": { + "outcome": "success", + "finished_at_ms": 1784271285452, + "exit_code": null, + "summary": "Headless rendezvous completed with Final", + "stdout_tail": "Branche créée, arbre propre, base tranchée. Voici mes décisions.\n\n## 1. État réel de `HEAD`\n\nPas de détachement : `HEAD` était bien attaché à `feature/ticket68-appimage-web-assets`, qui pointait sur le même commit que `develop` (a13a6c1) — une branche créée mais encore sans commit propre. Rien à corriger.\n\n## 2. Les modifications #68 : committées sur leur branche, pas stashées\n\nLe point décisif que ton contexte soulignait, je l'ai confirmé et il tranche la question : **le bloc `bundle.resources: {\"../../frontend/dist\": \"web\"}` et le `beforeBuildCommand` n'existent pas sur `develop`** — ils n'existaient que dans le travail non committé. Or ce sont exactement les lignes que le lot B1 doit modifier.\n\nConséquence : partir d'une base propre était exclu. #74 aurait dû recréer le bloc de zéro, et les deux versions seraient entrées en conflit frontal sur les mêmes lignes au moment de réintégrer #68. **#74 n'est pas une feature indépendante, c'est le correctif d'un packaging #68 non encore mergé** — il doit s'empiler dessus.\n\nJ'ai donc committé le travail #68 sur sa branche, en deux commits atomiques (code séparé de l'état runtime `.ideai/`), après vérification que ça compile (`cargo check -p app-tauri`, vert) — je ne fige pas du travail cassé :\n\n- `166adc3` — `feat(app-tauri)` : plomberie du resource dir + candidat packagé dans `resolve_web_root`\n- `50d237e` — `chore(ideai)` : ouverture du ticket #74\n\nLe stash était le mauvais outil ici : il aurait mis en suspens du travail dont #74 dépend directement.\n\n## 3. La branche de travail\n\n**`feature/ticket74-web-transport-bundle`**, basée sur `feature/ticket68-appimage-web-assets` (à `50d237e`). C'est là que F1, B1 et Q1 se font.\n\n**Ma décision d'intégration, pour plus tard** : #74 remontera dans `feature/ticket68-appimage-web-assets`, pas directement dans `develop`. #68 est en statut QA et son packaging sert un bundle cassé — il ne peut pas être déclaré vert sans le fix #74. Les deux redescendront ensemble dans `develop` quand QA validera l'ensemble. Ça évite qu'un merge de #74 vers `develop` traîne les commits #68 par effet de bord et court-circuite leur propre porte QA.\n\n## 4. `.gitignore`\n\nDéjà fait, rien à ajouter — `frontend/dist-web/` y figure ligne 14, ajouté au titre de #65, avec le commentaire qui le classe explicitement dans la même catégorie que `dist/`.\n\nAucune action sortante effectuée : tout est strictement local.", + "stderr_tail": null + }, + "completionDelivered": false + }, + { + "id": "75e788ad-0754-438f-928d-c0f245a5aa95", + "projectId": "97b49ac2-8376-4aa3-8ea9-bf3ac81d0023", + "ownerAgentId": "af7f86da-76bc-48e1-9900-71f45a624800", + "kind": { + "kind": "headlessRendezvous", + "requester_agent_id": "a6ced819-b893-4213-b003-9e9dc79b9641", + "target_agent_id": "af7f86da-76bc-48e1-9900-71f45a624800", + "ticket_id": "378f3420-0771-4168-8547-e90daf284288", + "conversation_id": "09b15ec3-ce2f-0af2-2903-ef699df9de41" + }, + "state": "completed", + "wakePolicy": "recordOnly", + "createdAtMs": 1784271313941, + "updatedAtMs": 1784271606127, + "deadlineMs": null, + "result": { + "outcome": "success", + "finished_at_ms": 1784271606127, + "exit_code": null, + "summary": "Headless rendezvous completed with Final", + "stdout_tail": "Lot F1 livré et vert. Le bundle web n'est plus figé sur `tauri` : `resolveTransport()` fold bien vers `\"http\"` dans `dist-web`, et les trois scripts npm produisent les artefacts attendus.\n\n## Fichiers touchés\n\n| Fichier | Changement |\n|---|---|\n| `frontend/src/app/transport.ts` | **nouveau** — prédicat partagé `transportFromEnv()` + type `Transport` |\n| `frontend/src/app/di.tsx` | `shouldUseHttp()`/`resolveTransport()` dérivent du prédicat partagé ; `Transport` ré-exporté (API inchangée) |\n| `frontend/vite.config.ts` | passe en factory `({ mode })`, `loadEnv(mode)`, `define: __IDEA_TRANSPORT__` |\n| `frontend/.env.web` | **nouveau** — `VITE_TRANSPORT=http` |\n| `frontend/src/vite-env.d.ts` | `declare const __IDEA_TRANSPORT__: \"http\" \\| \"tauri\"` |\n| `frontend/src/app/main.tsx` | publie le marqueur sur `window` |\n| `frontend/vitest.config.ts` | même `define`, via le même prédicat |\n| `frontend/package.json` | scripts `build:web` / `build:bundle` (littéral du contrat) |\n| `frontend/src/app/di.test.tsx` | couverture de `transportFromEnv` |\n\n`.env.web` n'est pas gitignoré (vérifié) — il sera bien versionné.\n\n## Câblage de `__IDEA_TRANSPORT__`\n\nLe point dur : la constante ne doit pas être une valeur parallèle. J'ai donc fait converger les deux vers **une source unique**, et non vers deux expressions qui se ressemblent.\n\n`vite.config.ts` importe `transportFromEnv()` depuis `src/app/transport.ts` — la fonction que `resolveTransport()` appelle — et lui passe `VITE_TRANSPORT` lu par `loadEnv(mode)`, c'est-à-dire la résolution de fichiers `.env` exacte que Vite applique à `import.meta.env`. **Même variable, même prédicat, un seul endroit à changer.** Une divergence exigerait de modifier `transportFromEnv`, ce qui déplacerait constante *et* runtime ensemble.\n\nLa preuve dans le dist minifié est plus forte que ce que j'espérais : le prédicat lui-même fold, et la constante et le runtime tombent du même pli.\n\n```\n=== desktop === function YN(e){return\"tauri\"} … function Cb(){return YN()}\n=== web === function YN(e){return\"http\"} … function Cb(){return YN()}\n```\n\n`YN` = `transportFromEnv`, `Cb` = `resolveTransport` (le `Cb(){return\"tauri\"}` du ticket). Le web résout donc réellement vers les adapters HTTP.\n\nDeux choix à signaler :\n- **Publication sur `window`** plutôt qu'une simple constante : `define` ne remplace que les occurrences présentes, et une constante non référencée serait tree-shakée. L'affectation est un effet de bord, donc elle survit à la minification — et le marqueur est aussi lisible en devtools. QA grep `__IDEA_TRANSPORT__=\"http\"`.\n- **Le mock reste hors périmètre de la constante** : elle reporte le transport de build hors mock (`VITE_USE_MOCK` est un switch distinct, antérieur). Documenté dans `vite-env.d.ts`.\n\n## Sorties réelles\n\n```\n$ npm run typecheck\n> tsc --noEmit # exit 0, aucune sortie\n\n$ npx vitest run\n Test Files 87 passed (87)\n Tests 797 passed (797)\n Duration 13.39s\n```\n(Les stack traces `useGateways must be used within a ` dans le log sont le test de garde qui attend ce throw.)\n\n```\n$ npm run build → dist/assets/index-B9bzrt7e.js 962.03 kB ✓ built in 2.30s\n$ npm run build:web → dist-web/assets/index-2XU1NMO5.js 962.03 kB ✓ built in 2.31s\n$ npm run build:bundle → les deux (vite v5.4.21 building for web…) ✓ 2.24s + 2.27s\n\ndist: __IDEA_TRANSPORT__=\"tauri\"\ndist-web: __IDEA_TRANSPORT__=\"http\"\n```\n\n`npm run build` est inchangé côté comportement (mêmes hash `index-B9bzrt7e.js` avant/après `build:bundle`). Je n'ai touché ni `tauri.conf.json` (lot B1), ni les adapters, ni le protocole du serveur.\n\n```idea-memory\nslug: ticket74-f1-web-bundle-transport-seam\ntitle: Seam Vite du bundle web (#74 F1) — mode `web` + constante __IDEA_TRANSPORT__\ntype: reference\ndescription: Deux artefacts Vite (dist/dist-web), mode `web` portable Windows, et le câblage anti-divergence de la constante de transport.\n---\n\nLe frontend produit **deux** artefacts Vite depuis les mêmes sources :\n- `frontend/dist` → transport `tauri` (desktop, `build.frontendDist`) — `npm run build`.\n- `frontend/dist-web` → transport `http` (packagé en ressource Tauri `web/`) — `npm run build:web`.\n- `npm run build:bundle` produit les deux : c'est le point d'entrée du packaging Tauri.\n\n**Le mode web passe par `--mode web` + `frontend/.env.web` (`VITE_TRANSPORT=http`), jamais par un préfixe inline `VITE_TRANSPORT=http vite build`** : non portable Windows/NSIS, cible active du bundle. `.env.web` est versionné (non gitignoré) ; `dist-web/` est ignoré.\n\n**Anti-divergence de `__IDEA_TRANSPORT__`** : `frontend/src/app/transport.ts` expose `transportFromEnv(value)`, importé à la fois par `di.tsx` (`resolveTransport`/`shouldUseHttp`) et par `vite.config.ts`, qui lui passe `VITE_TRANSPORT` via `loadEnv(mode)`. Même variable + même prédicat = une seule source. Modifier le prédicat déplace constante et runtime ensemble. `vitest.config.ts` réplique le `define` via le même helper.\n\nLa constante est **publiée sur `window` dans `main.tsx`** : `define` ne remplace que les occurrences existantes et une constante non référencée serait tree-shakée ; l'affectation est un effet de bord qui survit à la minification. Elle reporte le transport hors mock (`VITE_USE_MOCK` reste un switch distinct).\n\nPour identifier le transport d'un bundle bâti : `grep -o '__IDEA_TRANSPORT__=\"[a-z]*\"' dist*/assets/*.js`. **Ne pas se fier à la présence de `__TAURI_INTERNALS__` ni à un nom de symbole minifié** : les deux jeux d'adapters sont présents dans les deux bundles, un grep ne les discrimine pas.\n```", + "stderr_tail": null + }, + "completionDelivered": false + }, + { + "id": "453586ff-8b76-4f1b-88ba-aca6dae335cd", + "projectId": "97b49ac2-8376-4aa3-8ea9-bf3ac81d0023", + "ownerAgentId": "73c853d1-c0fd-463b-ad17-1d24fefa371f", + "kind": { + "kind": "headlessRendezvous", + "requester_agent_id": "a6ced819-b893-4213-b003-9e9dc79b9641", + "target_agent_id": "73c853d1-c0fd-463b-ad17-1d24fefa371f", + "ticket_id": "918e08bc-8431-43b6-8ca5-5a31a8264d8c", + "conversation_id": "d5068bc8-786e-0428-1d14-83b93961a15e" + }, + "state": "completed", + "wakePolicy": "recordOnly", + "createdAtMs": 1784271455055, + "updatedAtMs": 1784271484858, + "deadlineMs": null, + "result": { + "outcome": "success", + "finished_at_ms": 1784271484858, + "exit_code": null, + "summary": "Headless rendezvous completed with Final", + "stdout_tail": "Patch minimal appliqué sur `crates/app-tauri/tauri.conf.json`.\n\n**Diff**\n```diff\ndiff --git a/crates/app-tauri/tauri.conf.json b/crates/app-tauri/tauri.conf.json\nindex fd84eab..cf9f52d 100644\n--- a/crates/app-tauri/tauri.conf.json\n+++ b/crates/app-tauri/tauri.conf.json\n@@ -7,7 +7,7 @@\n \"frontendDist\": \"../../frontend/dist\",\n \"devUrl\": \"http://localhost:5173\",\n \"beforeDevCommand\": \"npm --prefix ../frontend run dev\",\n- \"beforeBuildCommand\": \"npm --prefix ../frontend run build\"\n+ \"beforeBuildCommand\": \"npm --prefix ../frontend run build:bundle\"\n },\n \"app\": {\n \"windows\": [\n@@ -27,7 +27,7 @@\n \"targets\": [\"appimage\", \"nsis\"],\n \"icon\": [\"icons/icon.png\"],\n \"resources\": {\n- \"../../frontend/dist\": \"web\"\n+ \"../../frontend/dist-web\": \"web\"\n }\n }\n }\n```\n\n**Commande**\n```text\n$ cargo check -p app-tauri\nFatal Python error: Failed to import encodings module\nPython runtime state: core initialized\nModuleNotFoundError: No module named 'encodings'\n\nCurrent thread 0x00007ff285fcff80 [vapoursynth] (most recent call first):\n \n Compiling app-tauri v0.3.0 (/home/anthony/Documents/Projects/IdeA/crates/app-tauri)\n Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.07s\n```\n\n`cargo check -p app-tauri` a terminé avec exit 0. Aucune découverte bloquante côté B1.", + "stderr_tail": null + }, + "completionDelivered": false + }, + { + "id": "707ca3b1-f9c8-41c3-8a6f-3387286c3366", + "projectId": "97b49ac2-8376-4aa3-8ea9-bf3ac81d0023", + "ownerAgentId": "aefdbd61-e3d4-4bc1-9f42-c259446a97b5", + "kind": { + "kind": "headlessRendezvous", + "requester_agent_id": "a6ced819-b893-4213-b003-9e9dc79b9641", + "target_agent_id": "aefdbd61-e3d4-4bc1-9f42-c259446a97b5", + "ticket_id": "696aff55-992c-4351-89d3-7342954f5bcf", + "conversation_id": "08336578-5b47-09d2-2f41-5cc483f101f4" + }, + "state": "completed", + "wakePolicy": "recordOnly", + "createdAtMs": 1784271632209, + "updatedAtMs": 1784272098942, + "deadlineMs": null, + "result": { + "outcome": "success", + "finished_at_ms": 1784272098942, + "exit_code": null, + "summary": "Headless rendezvous completed with Final", + "stdout_tail": "Verdict : **vert pour Q1**, avec une réserve d’environnement sur la validation live locale réseau.\n\nTests rejoués :\n```text\nnpm run typecheck\n> tsc --noEmit\nexit 0\n\nnpx vitest run\nTest Files 87 passed (87)\nTests 797 passed (797)\nexit 0\n\ncargo check -p app-tauri\nFinished `dev` profile [unoptimized + debuginfo] target(s) in 1.67s\nexit 0\n```\n\nTest ajouté :\n- [frontend/scripts/assert-bundle-transport.mjs](/home/anthony/Documents/Projects/IdeA/frontend/scripts/assert-bundle-transport.mjs:1)\n- [frontend/package.json](/home/anthony/Documents/Projects/IdeA/frontend/package.json:11) ajoute `npm run test:bundle-transport`.\n\nJ’ai choisi un test sur artefacts existants, sans rebuild interne : le DoD porte sur le bundle bâti réellement packagé. Sortie réelle :\n```text\nnpm run build:bundle && npm run test:bundle-transport\ndist: __IDEA_TRANSPORT__=\"tauri\" (1 marker)\ndist-web: __IDEA_TRANSPORT__=\"http\" (1 marker)\nexit 0\n```\n\nPackaging vérifié :\n```text\ngrep AppImage extrait:\nsquashfs-root/usr/lib/IdeA/web/assets/index-2XU1NMO5.js:__IDEA_TRANSPORT__=\"http\"\n```\n\nLes SHA-256 confirment que `usr/lib/IdeA/web` dans l’AppImage est byte-for-byte identique à `frontend/dist-web` :\n```text\n35d2...7632 frontend/dist-web/assets/index-2XU1NMO5.js\n35d2...7632 /tmp/idea-ticket74-appimage/squashfs-root/usr/lib/IdeA/web/assets/index-2XU1NMO5.js\n4ff0...5351 frontend/dist-web/index.html\n4ff0...5351 /tmp/idea-ticket74-appimage/squashfs-root/usr/lib/IdeA/web/index.html\n```\n\nAppImage produit :\n```text\n/home/anthony/Documents/Projects/IdeA/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage\n-rwxr-xr-x 109M\n```\n\nNote build : `tauri build --bundles appimage` a bien généré l’AppDir mais a échoué au téléchargement du runtime AppImage, réseau indisponible. J’ai finalisé avec le runtime déjà en cache :\n```text\nappimagetool --runtime-file /home/anthony/.cache/tauri/runtime-x86_64 ...\nSuccess\n```\n\nValidation live locale : impossible dans ce sandbox.\n- Exécution directe AppImage : KO FUSE (`No suitable fusermount`, `fuse: device not found`).\n- Lancement `--serve` extrait : KO bind TCP (`Operation not permitted`), même sur `127.0.0.1:0`.\n\nReste à valider par l’utilisateur : lancer cet AppImage réel sur la machine cible, sans `IDEA_WEB_ROOT`, puis vérifier via le navigateur/reverse proxy que l’UI charge et que le bundle servi annonce `window.__IDEA_TRANSPORT__ === \"http\"`.", + "stderr_tail": null + }, + "completionDelivered": false + }, + { + "id": "e6b46838-0b3e-45ea-a509-8d421cc4e20a", + "projectId": "97b49ac2-8376-4aa3-8ea9-bf3ac81d0023", + "ownerAgentId": "cd0b4cf1-1bef-4fae-ade5-f0a6b49bbaf5", + "kind": { + "kind": "headlessRendezvous", + "requester_agent_id": "a6ced819-b893-4213-b003-9e9dc79b9641", + "target_agent_id": "cd0b4cf1-1bef-4fae-ade5-f0a6b49bbaf5", + "ticket_id": "01938366-c3ec-44f5-87e6-a78a87886cb8", + "conversation_id": "6bc594e8-a37c-0dbd-1de6-6e3b73002cb4" + }, + "state": "running", + "wakePolicy": "recordOnly", + "createdAtMs": 1784272119205, + "updatedAtMs": 1784272119205, "deadlineMs": null, "result": null, "completionDelivered": false diff --git a/.ideai/memory/MEMORY.md b/.ideai/memory/MEMORY.md index a8f8587..d6f4438 100644 --- a/.ideai/memory/MEMORY.md +++ b/.ideai/memory/MEMORY.md @@ -64,3 +64,4 @@ - [idea-distribution-strategy-desktop-vs-docker](idea-distribution-strategy-desktop-vs-docker.md) — memory note idea-distribution-strategy-desktop-vs-docker - [web-client-is-single-column-no-desktop-shell](web-client-is-single-column-no-desktop-shell.md) — memory note web-client-is-single-column-no-desktop-shell - [sandbox-eperm-bind-false-green-web-server](sandbox-eperm-bind-false-green-web-server.md) — memory note sandbox-eperm-bind-false-green-web-server +- [ticket74-f1-web-bundle-transport-seam](ticket74-f1-web-bundle-transport-seam.md) — Deux artefacts Vite (dist/dist-web), mode `web` portable Windows, et le câblage anti-divergence de la constante de transport. diff --git a/.ideai/memory/ticket74-f1-web-bundle-transport-seam.md b/.ideai/memory/ticket74-f1-web-bundle-transport-seam.md new file mode 100644 index 0000000..81b42a5 --- /dev/null +++ b/.ideai/memory/ticket74-f1-web-bundle-transport-seam.md @@ -0,0 +1,18 @@ +--- +name: ticket74-f1-web-bundle-transport-seam +description: Deux artefacts Vite (dist/dist-web), mode `web` portable Windows, et le câblage anti-divergence de la constante de transport. +metadata: + type: reference +--- +Le frontend produit **deux** artefacts Vite depuis les mêmes sources : +- `frontend/dist` → transport `tauri` (desktop, `build.frontendDist`) — `npm run build`. +- `frontend/dist-web` → transport `http` (packagé en ressource Tauri `web/`) — `npm run build:web`. +- `npm run build:bundle` produit les deux : c'est le point d'entrée du packaging Tauri. + +**Le mode web passe par `--mode web` + `frontend/.env.web` (`VITE_TRANSPORT=http`), jamais par un préfixe inline `VITE_TRANSPORT=http vite build`** : non portable Windows/NSIS, cible active du bundle. `.env.web` est versionné (non gitignoré) ; `dist-web/` est ignoré. + +**Anti-divergence de `__IDEA_TRANSPORT__`** : `frontend/src/app/transport.ts` expose `transportFromEnv(value)`, importé à la fois par `di.tsx` (`resolveTransport`/`shouldUseHttp`) et par `vite.config.ts`, qui lui passe `VITE_TRANSPORT` via `loadEnv(mode)`. Même variable + même prédicat = une seule source. Modifier le prédicat déplace constante et runtime ensemble. `vitest.config.ts` réplique le `define` via le même helper. + +La constante est **publiée sur `window` dans `main.tsx`** : `define` ne remplace que les occurrences existantes et une constante non référencée serait tree-shakée ; l'affectation est un effet de bord qui survit à la minification. Elle reporte le transport hors mock (`VITE_USE_MOCK` reste un switch distinct). + +Pour identifier le transport d'un bundle bâti : `grep -o '__IDEA_TRANSPORT__="[a-z]*"' dist*/assets/*.js`. **Ne pas se fier à la présence de `__TAURI_INTERNALS__` ni à un nom de symbole minifié** : les deux jeux d'adapters sont présents dans les deux bundles, un grep ne les discrimine pas. \ No newline at end of file