diff --git a/.ideai/agents/main.md b/.ideai/agents/main.md index 4043b02d..5344d69a 100644 --- a/.ideai/agents/main.md +++ b/.ideai/agents/main.md @@ -99,6 +99,12 @@ Si le contexte d'un agent manque une consigne qui relève de son rôle, **mets contexte** au lieu de gonfler le tien. Ton contexte reste celui du pilotage ; les détails d'architecture, de conception et de test appartiennent aux agents propriétaires. +Pour tout changement du SDK, de ses APIs publiques, de ses exemples, de ses workflows +de plugins ou des capacités agent-facing associées, fais explicitement vérifier et mettre +à jour la documentation SDK dans `sdk/IdeaSDK/docs` et le README SDK si nécessaire. Une +évolution SDK n'est pas considérée complète tant que la documentation pertinente n'a pas +été relue ou ajustée. + Si une réflexion est récurrente et aboutit toujours à la même solution, créés en un skill réutilisable Si aucun ticket n'existe pour la demande de l'utilisateur, créé le. S'il existe, réouvre le. diff --git a/.ideai/memory/MEMORY.md b/.ideai/memory/MEMORY.md index a2ce023e..7d6be9f9 100644 --- a/.ideai/memory/MEMORY.md +++ b/.ideai/memory/MEMORY.md @@ -85,3 +85,5 @@ - [toolchain-rust-partage-acces-agents](toolchain-rust-partage-acces-agents.md) — Diagnostic et solution durable pour l'accès partagé au toolchain Rust/Tauri dans les run-dirs agents isolés. - [web-ui-refactor-surface-addressable-architecture](web-ui-refactor-surface-addressable-architecture.md) — memory note web-ui-refactor-surface-addressable-architecture - [plugin-hot-reload-agent-facing-delivered](plugin-hot-reload-agent-facing-delivered.md) — memory note plugin-hot-reload-agent-facing-delivered +- [plugin-soft-refresh-and-agent-install-tools-delivered](plugin-soft-refresh-and-agent-install-tools-delivered.md) — memory note plugin-soft-refresh-and-agent-install-tools-delivered +- [cross-project-ask-drop-cleans-live-state](cross-project-ask-drop-cleans-live-state.md) — memory note cross-project-ask-drop-cleans-live-state diff --git a/.ideai/memory/cross-project-ask-drop-cleans-live-state.md b/.ideai/memory/cross-project-ask-drop-cleans-live-state.md new file mode 100644 index 00000000..83d5eb59 --- /dev/null +++ b/.ideai/memory/cross-project-ask-drop-cleans-live-state.md @@ -0,0 +1,40 @@ +--- +name: cross-project-ask-drop-cleans-live-state +description: memory note cross-project-ask-drop-cleans-live-state +metadata: + type: project +--- +# Cross-project ask drop cleans live-state + +Date: 2026-09-08 +Type: reference + +## Cause racine + +Un `idea_project_ask_agent` abandonné/annulé côté appelant peut dropper le futur `ask_structured` côté cible avant qu'une branche explicite du watchdog ne s'exécute. `BusyTurnGuard` nettoyait déjà FIFO + busy state, mais la ligne live-state humaine passait à `Working` via `mark_target_working_best_effort` et ne revenait à `Done` que dans les branches explicites succès/erreur/timeout. Résultat : LED jaune / agent `working` persistant jusqu'au restart ou TTL, même si le tour a été abandonné. + +## Fix + +`crates/application/src/orchestrator/service.rs` ajoute `LiveStateTurnGuard` : + +- armé après la publication live-state `Working` ; +- désarmé après les sorties explicites qui appellent déjà `mark_target_done_best_effort` ; +- sur `Drop`, lance un cleanup async best-effort vers `Done` avec `last_delegation` = ticket. + +Test de régression : `dropped_ask_future_marks_live_state_done` dans `crates/application/tests/orchestrator_service.rs`. + +## Validation + +Verts le 2026-09-08 : + +- `cargo fmt` +- `cargo test -p application dropped_ask_future_marks_live_state_done --test orchestrator_service` +- `cargo test -p application --test orchestrator_service` : 78/78 +- `npm --prefix frontend run build` +- AppImage rebuild via `CARGO_HOME=/tmp/idea-cargo-home APPIMAGE_EXTRACT_AND_RUN=1 NO_STRIP=1 ../../frontend/node_modules/.bin/tauri build --bundles appimage` + +Artefact : `target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage`. + +## Réserve + +`cargo test -p application` échouait encore sur `launch_conventionfile_injects_assigned_skills_in_order`, hors périmètre du correctif cross-project/workstate. \ No newline at end of file diff --git a/.ideai/memory/plugin-soft-refresh-and-agent-install-tools-delivered.md b/.ideai/memory/plugin-soft-refresh-and-agent-install-tools-delivered.md new file mode 100644 index 00000000..947d73d3 --- /dev/null +++ b/.ideai/memory/plugin-soft-refresh-and-agent-install-tools-delivered.md @@ -0,0 +1,64 @@ +--- +name: plugin-soft-refresh-and-agent-install-tools-delivered +description: memory note plugin-soft-refresh-and-agent-install-tools-delivered +metadata: + type: project +--- +# Plugin soft refresh et tools agent-facing livrés + +Le ticket #279 a livré sur `develop` le flux plugins sans redémarrage applicatif quand un soft refresh UI suffit. + +## Contrat livré + +- Nouveau tool MCP agent-facing `idea_plugin_install_from_directory({ path, expectedPluginId? })`. +- Le tool réutilise l’installation depuis dossier existante côté application. +- Le chemin fourni par l’agent est résolu relativement au project root du requester, ou accepté en absolu seulement s’il reste sous ce project root après canonicalization. +- Rejets couverts : chemin hors project root, fichier au lieu de dossier, symlink sortant, manifest absent/id invalide, mismatch `expectedPluginId`. +- Tool classé `WriteAction`, masqué/refusé sans permission explicite. +- Installation depuis dossier renvoie `restartRequired: false` quand le soft refresh suffit et publie `PluginInstalled`. +- `idea_plugin_reload({ pluginId, expectedContentHash? })` reste le tool canonique pour hot reload d’un plugin déjà installé depuis sa source enregistrée ; il reste `WriteAction` et publie `PluginReloaded`. + +## Contrat frontend + +- `PluginRuntimeProvider` n’est plus un bootstrap one-shot : il écoute `pluginInstalled` et `pluginReloaded`. +- Sur événement, il relit `plugin.listRuntimeContributions()` et reconstruit un `PluginRuntimeRegistry` via `loadPlugins`. +- Le refresh remplace uniquement le contexte plugin ; il ne fait pas `window.location.reload()`, ne remonte pas `App`/`ProjectsView`, ne ferme pas les fenêtres, ne coupe pas les agents, terminaux, PTY, conversations ou background tasks. +- Les layouts `customPluginLayout` restent en place et retrouvent leur contribution par `providerPluginId/layoutId`. +- Si une contribution disparaît ou échoue, le fallback existant est conservé sans supprimer la cellule. +- Les fenêtres secondaires qui montent leur propre `PluginRuntimeProvider` bénéficient du même mécanisme. + +## Commits + +- `979965a` — `feat(plugins): idea_plugin_install_from_directory + reload couvrant le refresh UI — backend #279` +- `44dc677` — `feat(plugins): soft refresh UI des contributions après install/reload — frontend #279` +- `0b709b9` — merge local vers `develop` +- `3ff6208` — sync tickets #278/#279 + +## QA + +QA verte : + +- `cargo fmt --all --check` +- `git diff --check` +- `cargo test -p infrastructure --test mcp_server -- --nocapture` — 39 OK +- `cargo test -p infrastructure --test plugin_install_load -- --nocapture` — 5 OK +- `cargo test -p backend plugin_directory_resolution -- --nocapture` — 4 OK +- `cargo test -p application plugin -- --nocapture` — vert, incluant install soft-refresh, mismatch id, reload/hash/reconcile +- `cargo test -p application open_plugin_layout_window -- --nocapture` — 2 OK +- `cargo test -p application restore_open_windows -- --nocapture` — 2 OK +- `cd frontend && npm run typecheck` +- `cd frontend && npm test -- --run src/features/plugins/PluginRuntimeProvider.test.tsx src/features/plugins/plugins.test.tsx` — 13 OK +- `cd frontend && npm test -- --run src/features/plugins/PluginLayoutCellView.test.tsx src/features/projects/pluginGitRepository.test.tsx src/features/layout/layoutTabsGitGraph.test.tsx src/features/agents/CustomAgentChatView.test.tsx` — 78 OK + +## AppImage + +Rebuild AppImage effectué après merge avec le skill `build-appimage` : + +- `npm --prefix frontend run build` — OK +- depuis `crates/app-tauri` : `CARGO_HOME=/tmp/idea-cargo-home APPIMAGE_EXTRACT_AND_RUN=1 NO_STRIP=1 ../../frontend/node_modules/.bin/tauri build --bundles appimage` — OK + +Artefact produit : `/home/anthony/Documents/Projects/IdeA/target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage`, `114M`, généré le 2026-09-08 à 13:11. + +## Limite live + +L’AppImage installée n’a pas été remplacée et IdeA n’a pas été relancé pendant le cycle, afin de ne pas tuer l’orchestrateur actif. Pour rendre le correctif actif dans l’application courante, remplacer l’AppImage installée puis relancer IdeA quand l’utilisateur est prêt. \ No newline at end of file diff --git a/.ideai/tickets/280/carnet.md b/.ideai/tickets/280/carnet.md new file mode 100644 index 00000000..5ca451d1 --- /dev/null +++ b/.ideai/tickets/280/carnet.md @@ -0,0 +1,6 @@ +--- +issueRef: "#280" +version: 2 +updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} +updatedAt: 1788867597987 +--- diff --git a/.ideai/tickets/280/issue.md b/.ideai/tickets/280/issue.md new file mode 100644 index 00000000..05dec442 --- /dev/null +++ b/.ideai/tickets/280/issue.md @@ -0,0 +1,17 @@ +--- +id: "61a45327-8c7d-4267-8caf-5e77ab0f784f" +number: 280 +title: "Clarifier le contrat SDK plugin UI, fenêtres et skills Codex" +status: "qa" +priority: "high" +sprint: null +links: [] +agentRefs: [] +attachments: [] +createdBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} +updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} +createdAt: 1788867143625 +updatedAt: 1788867597987 +version: 2 +--- +Demande issue de UnityPlugin ticket #6 : clarifier le comportement normatif du SDK pour les commandes de plugin visibles par l'humain, l'ouverture de layouts/fenêtres, les capacités manifest ui/tooling, et la différence entre installation runtime IdeA et découverte des skills Codex bundlés. Mettre à jour la documentation SDK si ambiguë. \ No newline at end of file diff --git a/.ideai/tickets/281/carnet.md b/.ideai/tickets/281/carnet.md new file mode 100644 index 00000000..39036f17 --- /dev/null +++ b/.ideai/tickets/281/carnet.md @@ -0,0 +1,39 @@ +--- +issueRef: "#281" +version: 3 +updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} +updatedAt: 1788868377302 +--- +# Diagnostic + +Symptôme utilisateur : demande cross-project depuis UnityPlugin vers Main IdeA échouée/annulée ; côté IdeA, l'agent cible reste visible en travail (`working`, LED jaune) et le chat paraît bloqué jusqu'à cancel manuel. + +Cause racine identifiée dans `crates/application/src/orchestrator/service.rs` : le chemin `ask_structured` posait bien `Working` via `mark_target_working_best_effort`, et `BusyTurnGuard` nettoyait déjà FIFO + busy state quand le futur `ask` était droppé. En revanche, le live-state humain n'était remis à `Done` que dans les branches explicites du `match` watchdog ou en succès. Si le client MCP/cross-project abandonne la requête, aucune branche ne s'exécute : la ligne live-state persiste en `working` jusqu'au restart/TTL. + +# Correctif + +Ajout de `LiveStateTurnGuard` dans `crates/application/src/orchestrator/service.rs` : +- armé après publication live-state `Working` ; +- désarmé après les sorties explicites qui appellent déjà `mark_target_done_best_effort` ; +- sur `Drop`, lance un cleanup async best-effort vers `Done` avec `last_delegation` = ticket, afin de nettoyer la LED même si le futur `ask` est abandonné. + +Ajout du test de régression `dropped_ask_future_marks_live_state_done` dans `crates/application/tests/orchestrator_service.rs`. + +# Validations + +Commandes vertes : +- `cargo fmt` +- `cargo test -p application dropped_ask_future_marks_live_state_done --test orchestrator_service` +- `cargo test -p application --test orchestrator_service` : 78/78 +- `npm --prefix frontend run build` +- Build AppImage via skill `build-appimage` : `CARGO_HOME=/tmp/idea-cargo-home APPIMAGE_EXTRACT_AND_RUN=1 NO_STRIP=1 ../../frontend/node_modules/.bin/tauri build --bundles appimage` + +Artefact produit : `target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage` (114M, 2026-09-08 13:52). + +# Réserve + +`cargo test -p application` échoue sur `launch_conventionfile_injects_assigned_skills_in_order` dans `agent_lifecycle.rs`, échec hors périmètre du correctif : assertion existante sur l'ordre persona/skills dans le fichier de convention. Le test ciblé orchestration cross-project/workstate et la suite `orchestrator_service` sont verts. + +# Étape manuelle + +Comme rappelé par le skill AppImage : pour rendre le correctif actif, remplacer l'AppImage installée puis relancer IdeA. Cette étape tue l'orchestrateur courant, donc elle reste manuelle. \ No newline at end of file diff --git a/.ideai/tickets/281/issue.md b/.ideai/tickets/281/issue.md new file mode 100644 index 00000000..98054578 --- /dev/null +++ b/.ideai/tickets/281/issue.md @@ -0,0 +1,17 @@ +--- +id: "23e11f1c-387e-4201-a651-c91b70df6f7f" +number: 281 +title: "Fix blocage cross-project ask et workstate après timeout" +status: "closed" +priority: "critical" +sprint: null +links: [] +agentRefs: [] +attachments: [] +createdBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} +updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} +createdAt: 1788867892521 +updatedAt: 1788868377302 +version: 3 +--- +Reproduction rapportée : Main du projet UnityPlugin a tenté une demande cross-project vers Main IdeA ; l'appel a échoué, l'agent IdeA est resté avec LED jaune / chat bloqué jusqu'à cancel manuel, et le workstate a déclaré un timeout de la demande. Objectif : identifier la cause, corriger le nettoyage d'état/rendez-vous cross-project, valider par tests réels, puis rebuild l'AppImage. \ No newline at end of file diff --git a/.ideai/tickets/282/carnet.md b/.ideai/tickets/282/carnet.md new file mode 100644 index 00000000..8a03d4ed --- /dev/null +++ b/.ideai/tickets/282/carnet.md @@ -0,0 +1,98 @@ +--- +issueRef: "#282" +version: 3 +updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} +updatedAt: 1788871877354 +--- +# Cadrage initial + +Besoin utilisateur : les plugins IdeA doivent pouvoir apporter des skills et des tools MCP consommables par les agents, avec une configuration par projet. L'utilisateur veut un panneau Settings projet permettant de choisir quels plugins sont utilisés dans ce projet, puis d'attribuer les skills/tools de ces plugins aux agents, comme les skills IdeA et les tools MCP IdeA. + +# Décision produit proposée + +Modèle en trois niveaux : + +1. Plugin installé globalement dans IdeA. +2. Plugin activé explicitement pour un projet. +3. Skills/tools du plugin attribués explicitement à un agent du projet. + +Un plugin installé globalement ne rend pas automatiquement ses capabilities disponibles partout. L'activation projet rend les capabilities visibles/attribuables ; l'attribution agent décide ce que l'agent peut réellement utiliser. + +# Contrainte multi-harness + +La feature doit être IdeA-native et fonctionner pour tous les harness/profils supportés par IdeA : Claude Code, Codex, OpenCode local/cloud, et futurs adapters. Elle ne doit pas dépendre d'un mécanisme propre à Codex, ni de `.codex-plugin/plugin.json`, ni d'un dossier `$CODEX_HOME`. + +Le contrat attendu : IdeA résout les plugins activés pour le projet et les assignments de l'agent, puis projette les capabilities effectives dans la surface adaptée à chaque harness : + +- Claude Code : convention file / contexte injecté + MCP config selon les mécanismes déjà projetés pour Claude. +- Codex : AGENTS.md/contexte injecté + config MCP Codex selon les mécanismes déjà projetés pour Codex. +- OpenCode : contexte/config OpenCode selon les mécanismes déjà projetés pour OpenCode. + +`idea_skill_read(name)` doit rester une API IdeA, pas une API Codex : elle lit les skills effectivement assignés à l'agent quelle que soit son implémentation de harness, dès lors que ce harness dispose du pont/outillage IdeA correspondant. En mode sans MCP, conserver les stratégies existantes de projection fichier/contexte. + +# UX recommandé + +Ajouter `Settings projet > Plugins` : + +- liste des plugins installés globalement ; +- toggle `Activé pour ce projet` ; +- résumé des capabilities : `N skills`, `N tools MCP`, etc. ; +- détail `Voir les capabilities` avec sections `Skills fournis`, `Tools MCP fournis` ; +- dans la configuration agent, section `Capabilities de plugins`, groupée par plugin, séparée de `Skills IdeA` et `Tools MCP IdeA`. + +Libellés à préserver : + +- `Installé globalement` = existe dans IdeA ; +- `Activé pour ce projet` = chargé/exposé dans ce projet ; +- `Attribué à l'agent` = utilisable par cet agent. + +# Architecture recommandée + +Étendre `idea-plugin.json` avec `contributes.skills` read-only, package-relative, par exemple : + +```json +{ + "capabilities": ["ui", "mcp", "tooling"], + "contributes": { + "skills": [ + { + "id": "unity.build-debug", + "name": "unity-build-debug", + "description": "Diagnose Unity build failures.", + "kind": "workflow", + "path": "skills/unity-build-debug/SKILL.md" + } + ], + "mcpServers": [ + { + "id": "unity-tools", + "displayName": "Unity Tools", + "command": "dist/mcp-server.js", + "transport": "stdio", + "autoStart": true + } + ] + } +} +``` + +Créer un document projet versionné dédié, par exemple `.ideai/project-plugins.json`, séparé du registry global plugin et des agents natifs. Il stocke plugins activés et assignments agent vers `{ source: "plugin", pluginId, skillId/serverId/toolName }`. + +Au lancement agent : charger agent + settings projet plugins + plugins activés, vérifier état installed/runtime-active/hash, résoudre skills natifs + plugin skills assignés, injecter seulement le manifeste `# Skills disponibles`, et faire en sorte que `idea_skill_read(name)` lise uniquement les skills effectivement assignés à cet agent. + +Pour MCP tools plugin : garder la policy existante, namespace stable des tools plugin (`{ pluginId, serverId, toolName }`) et refuser l'exécution si non autorisé dans les permissions effectives de l'agent. La projection MCP doit passer par les adapters déjà responsables de chaque harness, pas par une configuration spécifique à Codex. + +# Contraintes + +- Hot reload plugin doit relire `contributes.skills` et `contributes.mcpServers`, publier `plugin_reloaded`, recalculer le catalogue projet effectif et marquer les assignments cassés `missing/stale` sans les supprimer. +- Uninstall global conserve des tombstones projet/agent, pour éviter de perdre une config lors d'un uninstall temporaire. +- Scope cross-project : un plugin installé est global, mais son usage est project-scoped ; `idea_project_ask_agent` utilise uniquement les assignments du projet cible. +- Ne pas mélanger avec `.codex-plugin/plugin.json` : cette feature est IdeA-native et multi-harness. Les skills Codex bundlés restent hors contrat tant qu'un pont explicite n'est pas conçu. + +# Découpage proposé + +B1 Domaine : contributions skills plugin, settings projet plugins, refs skills/tools plugin, validation. +B2 Infrastructure : validator manifest, store `.ideai/project-plugins.json`, lecture package-relative des SKILL.md, catalogue tools plugin. +B3 Application : use cases settings/assignments, resolver skills, `idea_skill_read`, lancement agent, permissions MCP dynamiques avec projection par harness. +F1 Frontend : Settings projet > Plugins, détail plugin, attribution agent, catalogue permissions groupé. +QA : validator, store, injection/read-skill sur Claude Code/Codex/OpenCode, permissions MCP par harness, hot reload, uninstall tombstones. \ No newline at end of file diff --git a/.ideai/tickets/282/issue.md b/.ideai/tickets/282/issue.md new file mode 100644 index 00000000..2c27a291 --- /dev/null +++ b/.ideai/tickets/282/issue.md @@ -0,0 +1,17 @@ +--- +id: "3d907761-17f1-4f39-8725-b7d6d856807b" +number: 282 +title: "Paramètres projet pour activer les plugins et attribuer leurs skills/tools aux agents" +status: "inProgress" +priority: "high" +sprint: null +links: [] +agentRefs: [] +attachments: [] +createdBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} +updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} +createdAt: 1788871620038 +updatedAt: 1788871877354 +version: 3 +--- +Besoin utilisateur : un plugin IdeA doit pouvoir apporter des skills et des tools MCP consommables par les agents, de façon comparable aux tools MCP IdeA. Ajouter un panneau Settings projet permettant de choisir quels plugins sont chargés pour le projet, puis d'attribuer les skills et tools MCP fournis par ces plugins aux agents. Clarifier le modèle de scope projet, découverte, permissions, hot reload, désinstallation et injection dans les contextes agents. \ No newline at end of file diff --git a/.ideai/tickets/counter.json b/.ideai/tickets/counter.json index 5dd53841..e158d6b7 100644 --- a/.ideai/tickets/counter.json +++ b/.ideai/tickets/counter.json @@ -1,3 +1,3 @@ { - "nextNumber": 280 + "nextNumber": 283 } \ No newline at end of file diff --git a/.ideai/tickets/index.json b/.ideai/tickets/index.json index 3849bdf6..171201d5 100644 --- a/.ideai/tickets/index.json +++ b/.ideai/tickets/index.json @@ -3819,6 +3819,48 @@ "agent_id": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d" }, "updatedAt": 1788865967900 + }, + { + "issueRef": "#280", + "path": "280", + "title": "Clarifier le contrat SDK plugin UI, fenêtres et skills Codex", + "status": "qa", + "priority": "high", + "sprint": null, + "assignedAgentIds": [], + "createdBy": { + "kind": "agent", + "agent_id": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d" + }, + "updatedAt": 1788867597987 + }, + { + "issueRef": "#281", + "path": "281", + "title": "Fix blocage cross-project ask et workstate après timeout", + "status": "closed", + "priority": "critical", + "sprint": null, + "assignedAgentIds": [], + "createdBy": { + "kind": "agent", + "agent_id": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d" + }, + "updatedAt": 1788868377302 + }, + { + "issueRef": "#282", + "path": "282", + "title": "Paramètres projet pour activer les plugins et attribuer leurs skills/tools aux agents", + "status": "inProgress", + "priority": "high", + "sprint": null, + "assignedAgentIds": [], + "createdBy": { + "kind": "agent", + "agent_id": "a6c6ea12-bfc6-4bdc-8031-324102dfa34d" + }, + "updatedAt": 1788871877354 } ] } \ No newline at end of file diff --git a/sdk/IdeaSDK b/sdk/IdeaSDK index 9829ccdf..1239104b 160000 --- a/sdk/IdeaSDK +++ b/sdk/IdeaSDK @@ -1 +1 @@ -Subproject commit 9829ccdf2a6ad61861303e3d554e106ef30f7d56 +Subproject commit 1239104b0f3985e95ddd338b150fd1f66a3c84d4