chore(ideai): état d'orchestration du sprint #13 (transport HTTP/WS + surface web)
Versionne l'état runtime IdeA produit pendant le sprint #13 : store de tickets (dont les nouveaux #55 à #67), compteur et index, notes de mémoire projet des lots F0 à F5, et journal des tâches de fond. Séparé du code applicatif conformément à la convention du dépôt (cf.ad1f225,a244f32) : métadonnée d'orchestration last-writer-wins, sans impact sur le build. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -1,6 +1,54 @@
|
||||
---
|
||||
issueRef: "#13"
|
||||
version: 4
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1783329397368
|
||||
version: 18
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784184002362
|
||||
---
|
||||
|
||||
# Ticket #13 — Server/client mode (carnet de chantier)
|
||||
|
||||
Ticket gated : review agent-utilisateur faite 2026-07-15. Base develop. Aucune action sortante.
|
||||
|
||||
## Arbitrages produit validés (utilisateur)
|
||||
- Remote perso, 1 SEUL user. Pas de multi-tenant. CLI natif via WS PTY, xterm.js inchangé, PTY serveur. SSH écarté.
|
||||
- Desktop Tauri ET client/serveur sur cœur backend commun. Exposition Internet ⇒ TLS/reverse proxy obligatoire.
|
||||
- Packaging `idea --serve`, artefact unique. Auth pairing code + cookie session, jamais dans l'URL.
|
||||
- Multi-fenêtres OS HORS V1 web. B6→B8 + servir dist, validation live groupée à la fin.
|
||||
|
||||
## Contrat de transport figé (Architect)
|
||||
- `POST /api/invoke {command,args}` (allowlist read-only). Cookie session `HttpOnly,Secure,SameSite=Strict` au pairing (`POST /api/pair`). `POST /api/logout` = révocation (B8). Cookie invalide→401 ; mauvais code→403 ; Origin refusée→403.
|
||||
- Frames WS JSON base64, ack unifié `terminal.attached`, output APRÈS l'ack. structured→UNSUPPORTED.
|
||||
- Flags : `--listen`, `--public-origin`, `--allow-remote`, `--trust-reverse-proxy`, `--app-data-dir`, `--web-root` (B8).
|
||||
|
||||
## Plan de lots — TOUS LES LOTS DEV LIVRÉS ✅
|
||||
B0→B8 + F1→F6 livrés/verts/committés. + correctifs live écarts 1/2. Reste : finir la validation live groupée (utilisateur) puis merge develop + clôture.
|
||||
|
||||
## Livrés VERT
|
||||
- develop (e457152) : B0 5505acc · B1 955db79 · B2 c8fef2a · F1 e0cdb4a · B3 4ed0b16 · B4 fa353f6 · F2 e500e31.
|
||||
- feature/ticket13-pty-websocket : B5 917be99 · F3 7487902 · B6 6391d1c · F4 5254f16 · B7 1bc5217 · F5 dd1d083 · B8 d538808 · F6 6117177 · **fix live écarts 1+2 c9ce3d7**.
|
||||
- B8 : sert dist/ same-origin, serve_static durci, `/api/logout`, logs sécurité, doc remote. 57/57.
|
||||
- F6 : logout, 401→pairing, bannière reconnexion, serveur indispo. build+vitest 758/758.
|
||||
|
||||
## ⚙️ Validation live 1er passage (utilisateur) — 3 écarts remontés
|
||||
1. **Erreur `__TAURI_INTERNALS__ undefined` + projets vides** = MA commande de test était incomplète (build sans `VITE_TRANSPORT=http` → build desktop dans le navigateur ; et `--app-data-dir` non pointé sur le dossier desktop). PAS un bug de code.
|
||||
2. **Défaut app-data-dir désaligné** (RÉSOLU c9ce3d7) : `default_app_data_dir()` retournait `~/.local/share/IdeA` alors que le desktop = identifier tauri `app.idea.ide` (`~/.local/share/app.idea.ide`). Corrigé : précédence `--app-data-dir` > `IDEA_APP_DATA_DIR` > `$XDG_DATA_HOME/app.idea.ide` > `$HOME/.local/share/app.idea.ide` + log démarrage `idea --serve: app data dir = <path>`. Doc corrigée (npm + VITE_TRANSPORT=http + avertissement double-writer). Garde-fou lock inter-process app-data-dir = ticket court à créer AVANT d'ouvrir des écritures web.
|
||||
3. **Bouton Browse (créer/ajouter projet) inopérant en web** = `pickFolder()`→`UNSUPPORTED_ON_WEB` (dialogue natif OS absent du navigateur). PAS régression : report documenté F1. Sélection d'un projet EXISTANT couverte par list_projects/open_project (OK une fois écart 2 corrigé). Créer un NOUVEAU projet en web = nouveau lot (folder browser serveur sandboxé + create_project write) → **ticket #64 créé** (dependsOn #13), cadré par Architect, hors #13.
|
||||
|
||||
## ⚠️ RESTE : re-passage validation live (utilisateur, HORS SANDBOX)
|
||||
Commande corrigée :
|
||||
1. `cd frontend && VITE_TRANSPORT=http npx vite build` (npm, jamais pnpm — voir mémoire `frontend-uses-npm-not-pnpm`).
|
||||
2. Fermer l'app desktop, puis `cargo run --release -p app-tauri -- --serve --web-root frontend/dist --app-data-dir ~/.local/share/app.idea.ide` (pairing code + app data dir sur stderr).
|
||||
3. Navigateur `http://127.0.0.1:17373` → écran pairing → code.
|
||||
4. Vérifier : liste projets (existants visibles), ouvrir projet, terminal CLI (frappe→serveur, reload→réattache/scrollback), cellule agent, surfaces live (workstate/background Cancel-Retry/inbox), reconnexion, logout→pairing.
|
||||
|
||||
## Réserves / écarts V1 (acceptés)
|
||||
- Replay V1 = repaint scrollback complet. Multi-onglets « dernier gagne » silencieux. F4 write-portal NON câblé web. Browse créer-projet web → #64. Lock double-writer app-data-dir → ticket court à créer.
|
||||
|
||||
## Branches
|
||||
`feature/ticket13-pty-websocket` (courante). À merger dans develop après validation live OK.
|
||||
|
||||
## Prochaine étape
|
||||
Re-passage validation live (commande corrigée ci-dessus). Si OK → Git merge dans develop → clôture #13. #64 (Browse web) à planifier après. Aucune action sortante sans validation utilisateur.
|
||||
|
||||
## Dette hors chantier
|
||||
Warnings clippy pré-existants crates/domain (fileguard/profile/sprint).
|
||||
|
||||
@ -2,15 +2,15 @@
|
||||
id: "036783fa-9856-4643-8c28-653b93ac36e1"
|
||||
number: 13
|
||||
title: "Server/client mode"
|
||||
status: "open"
|
||||
status: "inProgress"
|
||||
priority: "low"
|
||||
sprint: "028179b1-eaf4-41e9-9c1f-7c37125117e6"
|
||||
links: []
|
||||
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1783184188272
|
||||
updatedAt: 1783329397368
|
||||
version: 4
|
||||
updatedAt: 1784184002362
|
||||
version: 18
|
||||
---
|
||||
J'aimerais pouvoir utiliser IdeA sous forme de client/server. C'est a dire que IdeA aurait son backend sur une machine et son interface qui serait un frontend web. Les agents etc seraient tous côté server. C'est a dire que la cli de Claude serait celle côté serveur, l'interface client ne serait que l'affichage frontend, une UI. Je pense que tout est faisable, il faudrait cependant parler du côté CLI. Est ce qu'il serait possible de garde rle CLI natif comme il est actuellement, de l'afficher côté client tout en gardant l'installation claude code, codex etc côté serveur ? Peut etre qu'en ssh ça passerait ? Ce ticket ne pourra pas être fait en autonomie par un agent sans une review agent-utilisateur avant.
|
||||
@ -1,8 +1,8 @@
|
||||
---
|
||||
issueRef: "#15"
|
||||
version: 3
|
||||
version: 5
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1783940786085
|
||||
updatedAt: 1784093861343
|
||||
---
|
||||
## Cadrage Architect — DIFFÉRÉ (2026-07-13)
|
||||
|
||||
|
||||
@ -1,22 +1,28 @@
|
||||
---
|
||||
id: "5de121f3-1cd1-4a73-bcab-b0a43a7c257f"
|
||||
number: 15
|
||||
title: "Limites de session — re-livraison automatique parquée au reset (stretch B5, délégation durable)"
|
||||
title: "[Bloqué par #7 — design durable] Limites de session — re-livraison auto parquée au reset (stretch B5)"
|
||||
status: "open"
|
||||
priority: "low"
|
||||
sprint: "d8f3f37b-87ca-4509-9116-45a99bc711df"
|
||||
sprint: "e28a4d53-8bd2-446a-b0ac-2a017373b8b2"
|
||||
links: [{"target":"#7","kind":"dependsOn"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1783208599385
|
||||
updatedAt: 1783940786085
|
||||
version: 3
|
||||
updatedAt: 1784093861343
|
||||
version: 5
|
||||
---
|
||||
Extrait du cadrage Architect du ticket #7 (baseline livrée : propagation inter-agent B1→B3 + F1/F2). Stretch non retenu dans #7.
|
||||
|
||||
⛔ STATUT (2026-07-15) : BLOQUÉ / PARQUÉ. Dépend de #7 qui est encore en QA (non stabilisé). Architect (triage sprint « Gestion des bugs ») : « le waiter A→B n'est pas parqué/re-livré, l'ask retourne encore une erreur Process ; à traiter APRÈS stabilisation QA de #7/LS7 ». De plus c'est un item de « délégation durable » (in-memory fragile aux redémarrages) qui relève d'un design dédié, pas d'une rustine. Non lançable tant que #7 n'est pas vert et que le design durable-delegation n'est pas cadré. Sorti de la production active du sprint.
|
||||
|
||||
--- Cadrage conservé ---
|
||||
|
||||
Objectif : quand A délègue à B via idea_ask_agent et que B atteint sa limite, au lieu de demander à A de ré-interroger après le reset, PARQUER le waiter de A par (requester,target) ; au execute_resume de B (reprise auto au reset), RE-LIVRER la tâche d'origine et router la complétion vers le waiter de A s'il est encore vivant (sinon drop, contrainte in-memory).
|
||||
|
||||
Coût/risque (Architect) : rapproche du modèle de « délégation durable » tout en restant in-memory → fragile aux redémarrages/interruptions de A, ré-introduit une forme de blocage long côté A. À rattacher au design durable-delegation-runtime-agent-identity-design plutôt que de le traiter en rustine in-memory.
|
||||
État actuel (triage) : le rendez-vous délégué détecte RateLimited et arme une reprise de la cible (crates/application/src/orchestrator/service.rs:1854), execute_resume relance l'agent (crates/application/src/agent/session_limit.rs:226) ; mais le waiter A→B n'est pas parqué/re-livré.
|
||||
|
||||
Coût/risque (Architect) : rapproche du modèle « délégation durable » tout en restant in-memory → fragile aux redémarrages/interruptions de A, ré-introduit un blocage long côté A. À rattacher au design durable-delegation-runtime-agent-identity-design plutôt que traité en rustine in-memory.
|
||||
|
||||
Dépend de : #7 (baseline inter-agent B1→B3). Voir mémoire ticket7-session-limit-interagent-cadrage.
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
issueRef: "#2"
|
||||
version: 2
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1783184142093
|
||||
version: 6
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784065385002
|
||||
---
|
||||
|
||||
@ -1,24 +1,39 @@
|
||||
---
|
||||
id: "0a492d45-e195-4df7-a2ad-65649372abf0"
|
||||
number: 2
|
||||
title: "Tâches de fond — dette A : PtyPort::wait/try_wait + tee live UI + robustesse détection de fin"
|
||||
status: "open"
|
||||
title: "Tâches de fond — dette A : découpler fin de process et flux output PTY (PtyPort::wait/try_wait)"
|
||||
status: "closed"
|
||||
priority: "low"
|
||||
sprint: "e28a4d53-8bd2-446a-b0ac-2a017373b8b2"
|
||||
links: []
|
||||
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1783082860742
|
||||
updatedAt: 1783184142093
|
||||
version: 2
|
||||
updatedAt: 1784065385002
|
||||
version: 6
|
||||
---
|
||||
Dette issue de l'arbitrage B8 (mémoire projet: b8-arbitration-outcomes, point 1).
|
||||
Ticket #2 — Tâches de fond : découpler fin de process et flux output PTY.
|
||||
|
||||
B8 a été accepté sans rendu live des tâches de fond. Le cœur complétion→wake fonctionne, mais la détection de fin du runner repose sur l'EOF du stream PTY single-consumer, ce qui est fragile et empêche un tee UI (une re-souscription UI casserait la détection EOF).
|
||||
REQUALIFIÉ (2026-07-14) : le hub broadcast PTY existe désormais (crates/infrastructure/src/pty/mod.rs), donc l'ancienne contrainte « pas de broadcast multi-consommateur » est OBSOLÈTE. Le volet « tee live UI » est sorti en sous-ticket B/F séparé (voir lien blocks). La dette restante ici est purement backend.
|
||||
|
||||
Reste à faire :
|
||||
- Ajouter `PtyPort::wait` / `try_wait` pour découpler la détection d'exit de la consommation d'output (retirer l'EOF-comme-proxy-de-fin).
|
||||
- Une fois découplé, brancher un tee de la sortie PTY vers l'UI pour le rendu live des tâches de fond (F-live).
|
||||
- NE PAS introduire de broadcast multi-consommateur (décision Architect).
|
||||
Constat : `CommandBackgroundRunner` détecte encore la fin d'une tâche en drainant `PtyPort::subscribe_output` jusqu'à EOF (crates/infrastructure/src/background_task/runner.rs:187), ce qui couple la lifecycle du process à la consommation de sortie et empêche un tee sans casser la détection.
|
||||
|
||||
Périmètre touchant un port figé (PtyPort) : cadrage Architect requis avant implémentation.
|
||||
Attendu :
|
||||
- Ajouter au port figé `PtyPort` (crates/domain/src/ports.rs:952) deux opérations explicites :
|
||||
- `async fn wait(&self, handle: &PtyHandle) -> Result<ExitStatus, PtyError>` (attend la fin naturelle + status ; documenter l'idempotence : premier wait consomme, suivants retournent le status mémorisé).
|
||||
- `fn try_wait(&self, handle: &PtyHandle) -> Result<Option<ExitStatus>, PtyError>` (non bloquant : Ok(None) si vivant, Ok(Some(status)) si terminé).
|
||||
- `kill` reste « forcer l'arrêt puis retourner status » ; si déjà terminé, retourne le status mémorisé. NotFound si handle inconnu/purgé. L'exit status est mémorisé dans le registre live tant que la session existe (cohérence wait/try_wait/kill).
|
||||
- Implémenter dans `PortablePtyAdapter` (état d'exit partagé, attendre le child sans dépendre du reader EOF, éviter double wait/kill) et dans tous les fakes de test.
|
||||
- Migrer le runner : remplacer l'attente EOF (runner.rs:187) par une attente sur `pty.wait(&handle)`, concurrencée avec cancel/deadline. La sortie de completion reste prise depuis le scrollback borné ; le runner n'a plus besoin de subscribe_output pour savoir si le process est fini.
|
||||
|
||||
Impact contractuel : port figé modifié → changement source-breaking intra-workspace (tous les adapters/fakes ajoutent wait/try_wait), mais PAS de breaking IPC/front, pas de migration de données.
|
||||
|
||||
Lots : B1 wait/try_wait au port + fakes ; B2 impl PortablePtyAdapter ; B3 migrer CommandBackgroundRunner vers wait ; B4 tests. Taille M.
|
||||
|
||||
QA (point de vérité) :
|
||||
- Fake PtyPort dont l'output n'est jamais drainé (ou consommé par 2 subscribers) mais `wait` résout → le runner complète quand même.
|
||||
- Fake avec subscriber UI actif + runner → completion via `wait`, pas via EOF.
|
||||
- Deadline : si `wait` ne résout pas avant deadline → runner retourne Expired et tue/cleanup correctement.
|
||||
- Cancel : cancel gagne même si l'output continue.
|
||||
- Portable PTY réel : commande courte (`echo hi`) → completion exit 0 sans dépendre d'un drain output.
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
issueRef: "#20"
|
||||
version: 2
|
||||
version: 5
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1783331176736
|
||||
updatedAt: 1784049665491
|
||||
---
|
||||
|
||||
@ -2,16 +2,16 @@
|
||||
id: "1be19ee5-fd03-42ea-8df6-da18704807a9"
|
||||
number: 20
|
||||
title: "[Backend] Dette ticket_list — matching #ref dans text + curseur opaque/stable"
|
||||
status: "open"
|
||||
status: "closed"
|
||||
priority: "low"
|
||||
sprint: null
|
||||
sprint: "e28a4d53-8bd2-446a-b0ac-2a017373b8b2"
|
||||
links: [{"target":"#18","kind":"relatesTo"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1783331172226
|
||||
updatedAt: 1783331176736
|
||||
version: 2
|
||||
updatedAt: 1784049665491
|
||||
version: 5
|
||||
---
|
||||
Dette backend identifiée pendant le cadrage du sprint UI rework (gate G4), non bloquante pour la popup #18 qui démarre sur le contrat actuel.
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
issueRef: "#3"
|
||||
version: 2
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1783184146749
|
||||
version: 6
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784093835732
|
||||
---
|
||||
|
||||
@ -1,22 +1,28 @@
|
||||
---
|
||||
id: "fedd343e-f9c1-467b-bab4-455f51907de7"
|
||||
number: 3
|
||||
title: "Tâches de fond — dette B : persistance sûre de l'invocation (retry-after-reboot, secrets) + énumération terminale/projet du store"
|
||||
title: "[Différé — design à mûrir] Tâches de fond — dette B : retry durable après reboot"
|
||||
status: "open"
|
||||
priority: "low"
|
||||
sprint: "e28a4d53-8bd2-446a-b0ac-2a017373b8b2"
|
||||
links: []
|
||||
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1783082860758
|
||||
updatedAt: 1783184146749
|
||||
version: 2
|
||||
updatedAt: 1784093835732
|
||||
version: 6
|
||||
---
|
||||
Dette issue de l'arbitrage B8 (mémoire projet: b8-arbitration-outcomes, points 2 et 5).
|
||||
Ticket #3 — Tâches de fond : retry après redémarrage sans fuite de secrets dans le projet.
|
||||
|
||||
En V1, le registre d'invocations pour le retry est in-memory et session-scoped (le SpawnSpec porte des secrets, interdit dans .ideai/background-tasks/*.json qui voyage avec le projet). Conséquences à résorber :
|
||||
⛔ DÉCISION UTILISATEUR (2026-07-15) : DIFFÉRÉ. On ne persiste PAS de secrets en clair machine-local pour l'instant. À reprendre plus tard avec un vrai design sécurisé (keyring OS multiplateforme — Secret Service / Keychain / Credential Manager — ou modèle d'invocation déclaratif avec secret-refs re-résolus au retry depuis profil/env). Le retry-after-reboot reste indisponible en attendant. Ticket sorti de la production active du sprint « Gestion des bugs ».
|
||||
|
||||
- Retry-after-reboot : persistance SÛRE de l'invocation (redaction des secrets et/ou store machine-local hors projet) pour permettre le retry après un redémarrage d'IdeA.
|
||||
- Énumération store : aujourd'hui `list_background_tasks` sans agentId ne renvoie que les complétions non livrées du projet ; le store n'énumère pas les tâches terminales/ouvertes par projet (historique completed/failed partiel). Enrichir BackgroundTaskStore pour l'énumération terminale/projet.
|
||||
--- Cadrage conservé pour la reprise ---
|
||||
|
||||
Cadrage Architect requis (persistance de secrets = sensible).
|
||||
REQUALIFIÉ (2026-07-14) :
|
||||
- Volet « énumération terminale/projet du BackgroundTaskStore » : RÉSOLU par l'implémentation actuelle (store durable par projet sous <root>/.ideai/background-tasks, listes open par agent + completions non livrées, routage par projet). RETIRÉ du périmètre.
|
||||
- Reste uniquement le retry après redémarrage. L'invocation d'origine (SpawnSpec) est conservée dans un registre in-memory session-scoped (BackgroundCommandArchive, crates/application/src/background/mod.rs:203) volontairement non persistée dans .ideai car elle peut contenir des secrets. Après reboot, une tâche persistée ne peut donc pas être relancée.
|
||||
|
||||
Option A (écartée pour l'instant par décision utilisateur) : store machine-local hors projet (app-data OS/Tauri), SpawnSpec complet par (project_id, task_id), permissions 0700/0600 best-effort, JSON projet sans secret, retry échouant explicitement si invocation absente. Simple (M) mais persiste des secrets en clair local → refusée à ce stade.
|
||||
|
||||
Piste retenue pour la reprise : keyring OS multiplateforme OU secret-refs déclaratifs (chantier plus large que « low », à cadrer comme design dédié le moment venu).
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
issueRef: "#31"
|
||||
version: 1
|
||||
version: 4
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1783491197541
|
||||
updatedAt: 1784064442657
|
||||
---
|
||||
|
||||
@ -2,16 +2,16 @@
|
||||
id: "83d320f1-766b-4744-b518-8e39a2518e9c"
|
||||
number: 31
|
||||
title: "Détection de profils séquentielle : N × 800 ms au pire, résultat potentiellement partiel"
|
||||
status: "open"
|
||||
status: "closed"
|
||||
priority: "low"
|
||||
sprint: null
|
||||
sprint: "e28a4d53-8bd2-446a-b0ac-2a017373b8b2"
|
||||
links: [{"target":"#28","kind":"relatesTo"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1783491197541
|
||||
updatedAt: 1783491197541
|
||||
version: 1
|
||||
updatedAt: 1784064442657
|
||||
version: 4
|
||||
---
|
||||
## Origine
|
||||
Relevé par Git en revue du diff du ticket #28 (merge 710fa8f), hors périmètre du fix.
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
issueRef: "#33"
|
||||
version: 1
|
||||
version: 4
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1783492190391
|
||||
updatedAt: 1784048928225
|
||||
---
|
||||
|
||||
@ -2,16 +2,16 @@
|
||||
id: "c3e1fbcc-cedc-48a5-a00e-b22ea8dc90de"
|
||||
number: 33
|
||||
title: "[Dette] Fallthrough silencieux du routage structuré (lifecycle.rs:1705)"
|
||||
status: "open"
|
||||
status: "closed"
|
||||
priority: "high"
|
||||
sprint: null
|
||||
sprint: "e28a4d53-8bd2-446a-b0ac-2a017373b8b2"
|
||||
links: [{"target":"#32","kind":"relatesTo"},{"target":"#14","kind":"relatesTo"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1783492190391
|
||||
updatedAt: 1783492190391
|
||||
version: 1
|
||||
updatedAt: 1784048928225
|
||||
version: 4
|
||||
---
|
||||
Cause racine structurelle identifiée par Architect lors du cadrage de #32.
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
issueRef: "#34"
|
||||
version: 1
|
||||
version: 4
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1783492562056
|
||||
updatedAt: 1784050010085
|
||||
---
|
||||
|
||||
@ -2,16 +2,16 @@
|
||||
id: "8dca00bf-ec0f-4da1-a231-46b27c711b78"
|
||||
number: 34
|
||||
title: "[Risque] ChatBridge.scrollback non borné (croissance mémoire)"
|
||||
status: "open"
|
||||
status: "closed"
|
||||
priority: "medium"
|
||||
sprint: null
|
||||
sprint: "e28a4d53-8bd2-446a-b0ac-2a017373b8b2"
|
||||
links: [{"target":"#32","kind":"relatesTo"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1783492562056
|
||||
updatedAt: 1783492562056
|
||||
version: 1
|
||||
updatedAt: 1784050010085
|
||||
version: 4
|
||||
---
|
||||
Identifié par Architect lors du cadrage de #32 (variante B).
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
---
|
||||
issueRef: "#54"
|
||||
version: 5
|
||||
version: 6
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1783976286882
|
||||
updatedAt: 1784018300921
|
||||
---
|
||||
## Cadrage Architect (2026-07-13)
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
id: "18641885-1b38-43c1-a81b-42f4c407690d"
|
||||
number: 54
|
||||
title: "[UI] Ajouter le handle du téléchargement des modeles lors du démarage llamacpp"
|
||||
status: "inProgress"
|
||||
status: "closed"
|
||||
priority: "medium"
|
||||
sprint: "5afd6780-0f76-40d7-a10f-32ee52469d74"
|
||||
links: []
|
||||
@ -10,7 +10,7 @@ agentRefs: []
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1783965036142
|
||||
updatedAt: 1783976286882
|
||||
version: 5
|
||||
updatedAt: 1784018300921
|
||||
version: 6
|
||||
---
|
||||
Lorsqu'un serveur llamacpp se démarre, dans le cas ou le modele se télécharge, il faudrait que le téléchargement soit affiché au lieu d'afficher un timeout, avec en idéal l'affichage de la préogression du téléchargement en supperposition de la (ou les cellules) qui cherche à afficher le modèle
|
||||
28
.ideai/tickets/55/carnet.md
Normal file
28
.ideai/tickets/55/carnet.md
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
issueRef: "#55"
|
||||
version: 11
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784097757001
|
||||
---
|
||||
## Réouverture (2026-07-15) — le fix 141c13d ne suffit pas
|
||||
|
||||
Le fix #55 (commit 141c13d) a supprimé le timeout **prématuré** (~5 s) en distinguant process vivant-en-warmup / mort, avec `ReadinessPolicy::warmup_deadline = 120 s`. Mais l'utilisateur reproduit toujours « model server error (timeout): readiness timed out » **au lancement d'IdeA**, et ça finit par marcher après plusieurs essais.
|
||||
|
||||
### Cause racine
|
||||
`crates/application/src/model_server.rs` → `wait_for_started_server` (~l.437) : tant que process `Running` + endpoint `Unreachable`, poll jusqu'à `deadline = now + warmup_deadline` (120 s), puis `stop_started_server` + `ModelServerError::Timeout`.
|
||||
|
||||
- **1er lancement à froid** : page cache OS froid, GGUF multi-Go, chargement RAM/VRAM, I/O disque en concurrence avec le reste du boot IdeA ⇒ warmup > 120 s ⇒ timeout + kill.
|
||||
- **Essais suivants** : fichier en page cache chaud ⇒ chargement < 120 s ⇒ succès. Explique le « au bout de plusieurs essais ça finit ».
|
||||
|
||||
### Fix Lot 1 backend (livré, commit f7cae4c sur feature/ticket55-model-warmup-deadline)
|
||||
Cadré par Architect (mémoire `architecture-local-model-readiness-timeout`), approche A configurable :
|
||||
- Défaut `ReadinessPolicy::warmup_deadline` : 120 s → **600 s** (`crates/application/src/model_server.rs`).
|
||||
- Nouveau `LocalModelServerConfig::warmup_deadline_secs: Option<u64>`, rétrocompatible, validation stricte `[30, 1800]` (`crates/domain/src/model_server.rs`).
|
||||
- Policy effective dérivée **par config serveur** (deadline par-serveur), `probe`/`backoff` gardés séparés. DTO IPC `warmupDeadlineSecs` (`app-tauri/dto.rs`).
|
||||
- Contrat readiness inchangé : Exited⇒échec rapide ; Running+HTTP OK⇒prêt ; Running+HTTP KO⇒continuer jusqu'à deadline ; Running au-delà⇒stop+Timeout.
|
||||
|
||||
### QA — VERT (ports mockés)
|
||||
`cargo test -p domain` (252), `-p application` (81 + 22 model_server ciblés), `-p app-tauri --test dto_model_servers` (6), `-p infrastructure model_server` (2), build OK. Tests clés : slow_local_warmup...succeeds_without_stop, warmup_deadline_reached_returns_timeout_and_stops, process_exit_during_warmup_fails_fast, effective_policy_uses_configured_deadline, default_is_ten_minutes, hors-bornes⇒INVALID.
|
||||
|
||||
### RESTE À FAIRE — vérification manuelle utilisateur (bloquant merge)
|
||||
Les tests prouvent le **mécanisme**, PAS que 600 s guérit le vrai cold-start `llama-server` (bind réel hors sandbox). Git a committé mais **retient le merge sur develop** jusqu'à confirmation « cold-start OK » de l'utilisateur (vrai lancement à froid, après reboot / cache purgé). develop non divergé (ce5aa28) ⇒ merge --no-ff trivial au feu vert.
|
||||
17
.ideai/tickets/55/issue.md
Normal file
17
.ideai/tickets/55/issue.md
Normal file
@ -0,0 +1,17 @@
|
||||
---
|
||||
id: "9ab1eb21-d6dd-435a-b088-377f0ec7e04f"
|
||||
number: 55
|
||||
title: "[Bug] Error on loading local model"
|
||||
status: "inProgress"
|
||||
priority: "high"
|
||||
sprint: "e28a4d53-8bd2-446a-b0ac-2a017373b8b2"
|
||||
links: []
|
||||
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784045935720
|
||||
updatedAt: 1784097757001
|
||||
version: 11
|
||||
---
|
||||
Quand jke cherche a lancer un modele local sur une cellule, il commence apr charger le serveur, ce qui est bon mais au bout de quelques seconde, le chargement du serveur disparait et j'ai cette erreur qui s'affiche en bandeau rouge: Échec du lancement de l'agent : model server error (timeout): readiness timed out.
|
||||
Si j'insiste assez en changeant d'agent et en remettant l'agent, au bourt d'un moment ça fini par fonctionner
|
||||
6
.ideai/tickets/56/carnet.md
Normal file
6
.ideai/tickets/56/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#56"
|
||||
version: 6
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784048212123
|
||||
---
|
||||
16
.ideai/tickets/56/issue.md
Normal file
16
.ideai/tickets/56/issue.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "12943bca-4c29-46ec-8cad-936c30a95d5e"
|
||||
number: 56
|
||||
title: "[Bug] selection desactivée d'agent non affichés"
|
||||
status: "closed"
|
||||
priority: "low"
|
||||
sprint: "e28a4d53-8bd2-446a-b0ac-2a017373b8b2"
|
||||
links: []
|
||||
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784046090493
|
||||
updatedAt: 1784048212123
|
||||
version: 6
|
||||
---
|
||||
Si j'affiche un agent dans une cellule, que dans cette même cellule je change d'agent et que dans une autre cellule j'essaie d'afficher le premier agent, la selection d el'agent est désactivée comme si cet agent était déjà affiché dans une cellule alors que ce n'est pas le cas
|
||||
6
.ideai/tickets/58/carnet.md
Normal file
6
.ideai/tickets/58/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#58"
|
||||
version: 1
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784064521992
|
||||
---
|
||||
26
.ideai/tickets/58/issue.md
Normal file
26
.ideai/tickets/58/issue.md
Normal file
@ -0,0 +1,26 @@
|
||||
---
|
||||
id: "495699aa-d5d1-409f-8830-cdfaba7a4be3"
|
||||
number: 58
|
||||
title: "[B/F] Rendu live des tâches de fond — subscriber UI + canal IPC attachable au task output"
|
||||
status: "open"
|
||||
priority: "low"
|
||||
sprint: null
|
||||
links: [{"target":"#2","kind":"dependsOn"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784064521992
|
||||
updatedAt: 1784064521992
|
||||
version: 1
|
||||
---
|
||||
Sorti du ticket #2 lors de sa requalification (2026-07-14).
|
||||
|
||||
Le hub broadcast PTY (crates/infrastructure/src/pty/mod.rs) rend désormais possible un tee de la sortie d'une tâche de fond vers l'UI sans voler le flux au runner. Une fois #2 livré (découplage fin-de-process via PtyPort::wait, l'ajout d'un subscriber UI ne casse plus la détection de fin), brancher le rendu live.
|
||||
|
||||
Attendu :
|
||||
- Backend : créer un subscriber `pty.subscribe_output(&handle)` pour la tâche de fond ; canal dédié par task vers le frontend (idéalement Tauri `Channel`, comme les terminaux) ; commande type `attach_background_task_output(taskId, channel)` (ou intégration au panneau workstate).
|
||||
- Frontend : composant/UX affichant le flux live, gérant attach/detach, repaint via `scrollback` au (ré)attachement, état terminal.
|
||||
|
||||
QA : deux subscribers reçoivent les mêmes chunks ; le runner complète même pendant qu'un subscriber UI reste attaché ; reattach UI récupère le scrollback puis reçoit les nouveaux chunks.
|
||||
|
||||
Dépend de #2 (PtyPort::wait/try_wait + découplage runner).
|
||||
6
.ideai/tickets/60/carnet.md
Normal file
6
.ideai/tickets/60/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#60"
|
||||
version: 2
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784095176619
|
||||
---
|
||||
35
.ideai/tickets/60/issue.md
Normal file
35
.ideai/tickets/60/issue.md
Normal file
@ -0,0 +1,35 @@
|
||||
---
|
||||
id: "623b1719-c71d-448e-a0ef-3c7d5823b8a3"
|
||||
number: 60
|
||||
title: "[Bug] Assistant IA d'édition de ticket : aucune réponse dans la conversation (stream sans Final non signalé)"
|
||||
status: "inProgress"
|
||||
priority: "high"
|
||||
sprint: null
|
||||
links: [{"target":"#25","kind":"relatesTo"},{"target":"#27","kind":"relatesTo"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784094260499
|
||||
updatedAt: 1784095176619
|
||||
version: 2
|
||||
---
|
||||
Rapporté par l'utilisateur (2026-07-15) : dans l'édition d'un ticket, le bouton « Assistant IA » ouvre une conversation, mais l'envoi d'un message ne produit JAMAIS de réponse visible — la conversation reste muette. PROFIL UTILISÉ : modèle local OpenAI-compatible (llamacpp).
|
||||
|
||||
CAUSE (Architect) :
|
||||
1. CONFIRMÉ code — silence sur stream sans Final : agent_send draine le flux et détache le canal SANS rien émettre si aucun ReplyChunk::Final n'est produit (commands.rs:1661-1691). Le frontend ne termine que sur `final` (useTicketAssistant.ts:138-144). + finally setBusy(false) prématuré (useTicketAssistant.ts:147-151).
|
||||
2. PROBABLE runtime (local OpenAI-compatible) — Final VIDE : le chemin HTTP émet bien un Final quand le serveur renvoie choices[0].message.content ou SSE delta.content (openai_compat.rs:348-397/517-538/286-292), mais llama.cpp peut renvoyer un flux sans delta.content utile (reasoning_content / tool_calls / contenu vide) → ReplyEvent::Final { content: "" } → le frontend remplace le tour par une chaîne vide (useTicketAssistant.ts:138-140) = « muet ».
|
||||
|
||||
PÉRIMÈTRE DE CE TICKET (#60) — Lot A robustesse + gestion Final vide, B+F, S/M :
|
||||
- Backend : étendre ReplyChunk avec Error { message } (crates/app-tauri/src/dto.rs). agent_send : si le stream finit sans Final → émettre ReplyChunk::Error avant detach_if. Traiter Final { content: "" } comme terminal visible (Error « réponse vide du modèle » ou fallback explicite). Si l'adapter dispose d'un reasoning_content non vide, envisager de le surfacer plutôt que de le jeter (à confirmer selon payload).
|
||||
- Frontend : ReplyChunk TS + useTicketAssistant gère `error`, termine le pending, affiche le message, busy tombe sur final OU error, suppression du finally setBusy(false) prématuré.
|
||||
|
||||
Effet attendu : l'assistant ne reste JAMAIS muet — soit une réponse, soit une erreur/vide explicite visible.
|
||||
|
||||
QA :
|
||||
- Backend agent_send : fake AgentSession renvoie Heartbeat puis EOF sans Final → le channel reçoit ReplyChunk::Error.
|
||||
- Backend OpenAI-compatible : fake HTTP renvoie SSE [DONE] sans delta.content → terminal visible (erreur/non vide), jamais un tour muet.
|
||||
- Frontend : sendTicketChat reçoit `error` → tour non pending, busy=false, message visible.
|
||||
|
||||
HORS PÉRIMÈTRE (→ ticket de suivi) : identité requester explicite (port AgentSessionFactory::start, factory.rs:153-157 dérive le requester du cwd → devient le n° de ticket au lieu de ticket-assistant:<project>:<issue>) + branchement de ToolPolicyRegistry sur l'invoker OpenAI-compatible (openai_tools.rs:92-145) + éventuel local_model_server_id sur HttpChatConfig. Concerne un port figé + la sécurité des tool calls.
|
||||
|
||||
Relations : #25, #27 (mêmes surfaces, déjà fermées).
|
||||
6
.ideai/tickets/61/carnet.md
Normal file
6
.ideai/tickets/61/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#61"
|
||||
version: 5
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1784182133142
|
||||
---
|
||||
16
.ideai/tickets/61/issue.md
Normal file
16
.ideai/tickets/61/issue.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
id: "b0568a4e-4804-480c-ada4-bc9d6b7b40c6"
|
||||
number: 61
|
||||
title: "[UI] rafraichissement des cellule"
|
||||
status: "open"
|
||||
priority: "low"
|
||||
sprint: "e28a4d53-8bd2-446a-b0ac-2a017373b8b2"
|
||||
links: []
|
||||
agentRefs: [{"agentId":"a6ced819-b893-4213-b003-9e9dc79b9641","role":"assigned"}]
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"user"}
|
||||
createdAt: 1784095163730
|
||||
updatedAt: 1784182133142
|
||||
version: 5
|
||||
---
|
||||
Lorsque j'ajoute une nouvelle cellule ou que j'en enlève une, je suis obligé de redimentionner un coup la fenêtre ou les cellule pour rafraichir le scaling des cli
|
||||
6
.ideai/tickets/62/carnet.md
Normal file
6
.ideai/tickets/62/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#62"
|
||||
version: 1
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784095187734
|
||||
---
|
||||
31
.ideai/tickets/62/issue.md
Normal file
31
.ideai/tickets/62/issue.md
Normal file
@ -0,0 +1,31 @@
|
||||
---
|
||||
id: "dd897d74-4e88-4d34-a6fb-d7e7f330094f"
|
||||
number: 62
|
||||
title: "[Sécurité/Cohérence] Identité requester explicite pour les sessions structurées + policy des tools OpenAI-compatible"
|
||||
status: "open"
|
||||
priority: "medium"
|
||||
sprint: null
|
||||
links: [{"target":"#60","kind":"relatesTo"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784095187734
|
||||
updatedAt: 1784095187734
|
||||
version: 1
|
||||
---
|
||||
Sorti de #60 (assistant IA de ticket muet) — volet distinct, touchant un port figé et la sécurité des tool calls.
|
||||
|
||||
CONSTAT (Architect, cadrage #60) :
|
||||
1. Le requester d'une session structurée est dérivé implicitement du nom du cwd/run dir (crates/infrastructure/src/session/factory.rs:153-157). Pour l'assistant de ticket, le requester devient donc le NUMÉRO DU TICKET, alors que la policy MCP est posée sur ticket-assistant:<project>:<issue> (crates/application/src/ticket_assistant.rs:100-119). Mismatch → incohérence d'attribution et policy potentiellement non appliquée.
|
||||
2. L'invoker de tools OpenAI-compatible (AppOpenAiToolInvoker, openai_tools.rs:92-145) dispatch directement SANS consulter ToolPolicyRegistry, alors que le serveur MCP stdio applique bien la policy (mcp/server.rs:349-355, :470-512). Les tool calls d'un profil OpenAI-compatible ne sont donc pas soumis à la policy.
|
||||
|
||||
ATTENDU :
|
||||
- Faire évoluer le port AgentSessionFactory::start pour recevoir une identité requester explicite (Option<&str> ou petit SessionIdentity) ; fallback sur cwd.file_name() seulement si absente. Impact : port figé → propagation aux impls/fakes.
|
||||
- OpenTicketAssistant passe requester = ticket-assistant:<project>:<issue>.
|
||||
- LaunchAgent passe l'agent id comme requester pour les cellules normales (au lieu de dépendre du nom du run dir).
|
||||
- Brancher ToolPolicyRegistry sur l'invoker OpenAI-compatible (parité avec le serveur MCP stdio).
|
||||
- Décider pour le modèle local managé : étendre HttpChatConfig avec local_model_server_id (appeler EnsureLocalModelServer avant OpenAiCompatibleSession::new) OU documenter que OpenAI-compatible = endpoint externe.
|
||||
|
||||
Cadrage Architect requis (port figé + sécurité). QA : un assistant ticket OpenAI-compatible qui appelle idea_ticket_update reçoit __ideaRequester = ticket-assistant:<project>:<issue> et la policy ticket est appliquée ; un tool refusé par la policy l'est aussi sur le chemin OpenAI-compatible.
|
||||
|
||||
Dépend de / relié à #60.
|
||||
6
.ideai/tickets/63/carnet.md
Normal file
6
.ideai/tickets/63/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#63"
|
||||
version: 1
|
||||
updatedBy: {"kind":"user"}
|
||||
updatedAt: 1784098438671
|
||||
---
|
||||
15
.ideai/tickets/63/issue.md
Normal file
15
.ideai/tickets/63/issue.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
id: "b5753734-1474-48d5-af6c-4a41b78005dc"
|
||||
number: 63
|
||||
title: "Systeme de test de l'UI"
|
||||
status: "open"
|
||||
priority: "medium"
|
||||
sprint: null
|
||||
links: []
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"user"}
|
||||
updatedBy: {"kind":"user"}
|
||||
createdAt: 1784098438671
|
||||
updatedAt: 1784098438671
|
||||
version: 1
|
||||
---
|
||||
6
.ideai/tickets/64/carnet.md
Normal file
6
.ideai/tickets/64/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#64"
|
||||
version: 2
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784187610934
|
||||
---
|
||||
31
.ideai/tickets/64/issue.md
Normal file
31
.ideai/tickets/64/issue.md
Normal file
@ -0,0 +1,31 @@
|
||||
---
|
||||
id: "c8f1c5b3-7674-4c6c-bba5-bb2c5faf201b"
|
||||
number: 64
|
||||
title: "Web : créer/ajouter un projet depuis le navigateur (folder browser serveur)"
|
||||
status: "open"
|
||||
priority: "medium"
|
||||
sprint: null
|
||||
links: [{"target":"#13","kind":"dependsOn"},{"target":"#66","kind":"dependsOn"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784183727404
|
||||
updatedAt: 1784187610934
|
||||
version: 2
|
||||
---
|
||||
Issu de la validation live de #13 (mode client/serveur web). En web, le bouton « Browse » (choisir un dossier de projet) est inopérant : `pickFolder()` renvoie `UNSUPPORTED_ON_WEB` car il s'appuie sur le dialogue natif OS de Tauri, absent du navigateur. Sélectionner un projet EXISTANT est déjà couvert par la liste (list_projects/open_project). Il manque CRÉER/AJOUTER un projet = choisir un dossier côté SERVEUR.
|
||||
|
||||
Cadrage Architect (post-#13) : nouvelle surface backend exposant le filesystem serveur au navigateur, à sandboxer.
|
||||
|
||||
Backend :
|
||||
- Commande read `list_server_dir` (racine parcourable explicite via `--server-browse-root PATH` / `IDEA_SERVER_BROWSE_ROOT` ; défaut prudent : aucun browsing ou $HOME en loopback ; jamais `/` implicite).
|
||||
- `create_project` sur l'allowlist WRITE, avec sa propre validation de racine (ne fait pas confiance au folder browser).
|
||||
- Invariants sécurité (mêmes gardes que serve_static) : canonicalisation browse_root + cible, refus si hors racine, refus `..`/segments vides/backslash/`%2e`/`%2f`/`%5c`, dossiers seulement, pas de symlink escape, dotdirs masqués (`hidden:true`), réponse bornée + tri stable.
|
||||
|
||||
DTO :
|
||||
- ListServerDirRequest { path?: string } → ListServerDirResponse { root, current, parent|null, entries: [{name,path,kind:'directory',hidden}], canCreateProjectHere }
|
||||
- CreateProjectRequest { name, root(absolu serveur validé) }
|
||||
|
||||
Découpage : B1 list_server_dir + tests traversal/symlink/bounds ; B2 allowlist write create_project + validation racine ; F1 nouveau port UI `ServerFolderGateway` (ne pas surcharger `SystemGateway.pickFolder()`) ; F2 composant folder browser web (desktop garde le picker natif) ; F3 tests adapter HTTP + vue projet.
|
||||
|
||||
Dépend de #13 (mode web) et du garde-fou lock inter-process app-data-dir (à créer) avant d'activer les écritures web.
|
||||
6
.ideai/tickets/65/carnet.md
Normal file
6
.ideai/tickets/65/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#65"
|
||||
version: 1
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784187583229
|
||||
---
|
||||
27
.ideai/tickets/65/issue.md
Normal file
27
.ideai/tickets/65/issue.md
Normal file
@ -0,0 +1,27 @@
|
||||
---
|
||||
id: "f9f7067b-92b7-4737-ad99-4487ad8c8b13"
|
||||
number: 65
|
||||
title: "Serveur headless : extraire idea-serve du binaire Tauri (cœur partagé)"
|
||||
status: "open"
|
||||
priority: "high"
|
||||
sprint: null
|
||||
links: [{"target":"#13","kind":"dependsOn"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784187583229
|
||||
updatedAt: 1784187583229
|
||||
version: 1
|
||||
---
|
||||
Objectif : produire un binaire serveur headless (`idea-serve`) SANS dépendance Tauri/WebKit, bâti sur le même cœur backend que le desktop (pas de fork). Prérequis de l'image Docker serveur/client. Cadré par Architect (suite #13).
|
||||
|
||||
Point pivot : `server.rs` vit aujourd'hui dans `crates/app-tauri` et réutilise indirectement la présentation Tauri via `crate::state::AppState`, `crate::dto::*`, `crate::events::DomainEventDto`, `crate::pty::PtyChunk`, `crate::mcp_endpoint::*`, `ResumeContext`. Le protocole HTTP/WS lui est déjà autonome.
|
||||
|
||||
Plan de lots (Architect) :
|
||||
- **L1 — Extraire les DTO transport-neutres** dans un module/crate partagé (ex. `crates/presentation-dto` ou `backend-api`) : ErrorDto, Health*Dto, ProjectDto/ProjectListDto, ProjectWorkStateDto, BackgroundTaskDto, TerminalSessionDto, LaunchAgentRequestDto, OpenTerminalRequestDto, parseurs d'IDs, DomainEventDto. Adapter app-tauri::commands ET server.rs pour les consommer. JSON INCHANGÉ.
|
||||
- **L2 — Extraire le serveur HTTP/WS** dans `crates/web-server` (lib) sans dépendance Tauri : déplacer server.rs, remplacer `AppState` par `BackendCore`, garder strictement routes + allowlist + sécurité B8 (POST /api/pair|invoke|logout, /api/ws, static same-origin). Aucune modif frontend.
|
||||
- **L3 — Créer le bin `idea-serve`** (crate bin) : parse CLI/env → config → `web_server::run(config)`. Flags identiques à `idea --serve` (--listen/--app-data-dir/--web-root/--allow-remote/--public-origin/--trust-reverse-proxy). Défaut local = même app-data-dir que desktop (identifier app.idea.ide). Validation : le build/`ldd` ne tire PAS WebKitGTK. Garder éventuellement `app-tauri --serve` en compat dev, mais qui appelle `web-server`.
|
||||
|
||||
Invariants : desktop AppImage ne perd RIEN, aucun import Tauri dans le bin headless, même cœur/use cases/stores, contrat HTTP/WS inchangé. Non-régression desktop validée avant tout merge.
|
||||
|
||||
Dépend de #13.
|
||||
6
.ideai/tickets/66/carnet.md
Normal file
6
.ideai/tickets/66/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#66"
|
||||
version: 1
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784187597004
|
||||
---
|
||||
27
.ideai/tickets/66/issue.md
Normal file
27
.ideai/tickets/66/issue.md
Normal file
@ -0,0 +1,27 @@
|
||||
---
|
||||
id: "f805cd9b-8ecd-401a-8f03-665a27fe73cb"
|
||||
number: 66
|
||||
title: "Image Docker serveur/client IdeA"
|
||||
status: "open"
|
||||
priority: "medium"
|
||||
sprint: null
|
||||
links: [{"target":"#65","kind":"dependsOn"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784187597004
|
||||
updatedAt: 1784187597004
|
||||
version: 1
|
||||
---
|
||||
Objectif : livrer une image Docker exécutant le mode serveur/client d'IdeA, bâtie sur le binaire headless `idea-serve` (#65), pas sur le binaire Tauri. Cadré par Architect (suite #13).
|
||||
|
||||
Plan de lots (Architect) :
|
||||
- **L4 — Build web transport HTTP** : produire les assets client Vite en `VITE_TRANSPORT=http` (npm, jamais pnpm), vérifier qu'aucun import Tauri ne fuit dans ce mode, packager dans `/usr/share/idea/web`.
|
||||
- **L5 — Docker runtime** : Dockerfile multi-stage (build Rust headless + build frontend + runtime minimal Debian/Ubuntu selon deps PTY/process). Défauts : `IDEA_APP_DATA_DIR=/data`, `--listen 0.0.0.0:17373`, `--web-root /usr/share/idea/web`. Volumes `/data` (app-data : projects/profiles/templates/tasks/logs) et `/workspace` (projets manipulés par les agents). Entrypoint `idea-serve`. Healthcheck HTTP local. Conteneur reste HTTP interne ; reverse proxy TLS externe obligatoire en prod distante (`--allow-remote`/`--public-origin https`/`--trust-reverse-proxy`, doc B8). Pas de TLS applicatif V1.
|
||||
- **L6 — Agents CLI en conteneur** : décider image minimale (profils détectés au runtime, exécutables attendus dans PATH) vs image `idea-server-agents` (CLIs redistribuables installées si licence OK). Seed profils compatibles conteneur. Documenter env (OPENAI_API_KEY, ANTHROPIC_API_KEY, vars opencode) et montages de credentials. Tester au moins un agent bout en bout. Hors périmètre : installer automatiquement des CLIs propriétaires sans validation licence.
|
||||
|
||||
Lock app-data-dir : peu critique en Docker mono-conteneur mono-writer ; multi-conteneurs sur le même /data explicitement hors support sans lock distribué.
|
||||
|
||||
Hors périmètre V1 : Kubernetes, multi-tenant, auth externe, TLS intégré.
|
||||
|
||||
Dépend de #65 (binaire headless).
|
||||
6
.ideai/tickets/67/carnet.md
Normal file
6
.ideai/tickets/67/carnet.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
issueRef: "#67"
|
||||
version: 1
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedAt: 1784187618710
|
||||
---
|
||||
22
.ideai/tickets/67/issue.md
Normal file
22
.ideai/tickets/67/issue.md
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
id: "e3d16c70-01c5-448e-9b5e-37ba2ccbbb58"
|
||||
number: 67
|
||||
title: "Lock inter-process de l'app-data-dir (desktop ↔ idea --serve)"
|
||||
status: "open"
|
||||
priority: "low"
|
||||
sprint: null
|
||||
links: [{"target":"#13","kind":"dependsOn"}]
|
||||
agentRefs: []
|
||||
createdBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
||||
createdAt: 1784187618710
|
||||
updatedAt: 1784187618710
|
||||
version: 1
|
||||
---
|
||||
Issu de la validation live #13. `idea --serve` et l'app desktop peuvent écrire le même app-data-dir (`~/.local/share/app.idea.ide`) simultanément — pas de lock inter-process → risque de corruption d'état (projects.json/profiles.json/tasks). Aujourd'hui contourné par une simple consigne de doc (« ne pas lancer les deux en même temps »).
|
||||
|
||||
À faire : garde-fou lock inter-process partagé desktop/serveur sur l'app-data-dir (le desktop DOIT le prendre aussi, un lock côté serveur seul ne suffit pas). À poser AVANT d'ouvrir des commandes write web au-delà de l'existant (cancel/retry background) — notamment avant `create_project` (#64).
|
||||
|
||||
Peu critique en Docker mono-conteneur mono-writer (cf. #66) ; surtout pertinent pour l'usage AppImage `--serve` local sur la même machine que le desktop. Cadrage lock détaillé à confirmer avec Architect au démarrage.
|
||||
|
||||
Dépend de #13.
|
||||
@ -1,3 +1,3 @@
|
||||
{
|
||||
"nextNumber": 55
|
||||
"nextNumber": 68
|
||||
}
|
||||
@ -16,26 +16,26 @@
|
||||
{
|
||||
"issueRef": "#2",
|
||||
"path": "2",
|
||||
"title": "Tâches de fond — dette A : PtyPort::wait/try_wait + tee live UI + robustesse détection de fin",
|
||||
"status": "open",
|
||||
"title": "Tâches de fond — dette A : découpler fin de process et flux output PTY (PtyPort::wait/try_wait)",
|
||||
"status": "closed",
|
||||
"priority": "low",
|
||||
"sprint": null,
|
||||
"sprint": "e28a4d53-8bd2-446a-b0ac-2a017373b8b2",
|
||||
"assignedAgentIds": [
|
||||
"a6ced819-b893-4213-b003-9e9dc79b9641"
|
||||
],
|
||||
"updatedAt": 1783184142093
|
||||
"updatedAt": 1784065385002
|
||||
},
|
||||
{
|
||||
"issueRef": "#3",
|
||||
"path": "3",
|
||||
"title": "Tâches de fond — dette B : persistance sûre de l'invocation (retry-after-reboot, secrets) + énumération terminale/projet du store",
|
||||
"title": "[Différé — design à mûrir] Tâches de fond — dette B : retry durable après reboot",
|
||||
"status": "open",
|
||||
"priority": "low",
|
||||
"sprint": null,
|
||||
"sprint": "e28a4d53-8bd2-446a-b0ac-2a017373b8b2",
|
||||
"assignedAgentIds": [
|
||||
"a6ced819-b893-4213-b003-9e9dc79b9641"
|
||||
],
|
||||
"updatedAt": 1783184146749
|
||||
"updatedAt": 1784093835732
|
||||
},
|
||||
{
|
||||
"issueRef": "#4",
|
||||
@ -149,13 +149,13 @@
|
||||
"issueRef": "#13",
|
||||
"path": "13",
|
||||
"title": "Server/client mode",
|
||||
"status": "open",
|
||||
"status": "inProgress",
|
||||
"priority": "low",
|
||||
"sprint": "028179b1-eaf4-41e9-9c1f-7c37125117e6",
|
||||
"assignedAgentIds": [
|
||||
"a6ced819-b893-4213-b003-9e9dc79b9641"
|
||||
],
|
||||
"updatedAt": 1783329397368
|
||||
"updatedAt": 1784184002362
|
||||
},
|
||||
{
|
||||
"issueRef": "#14",
|
||||
@ -172,12 +172,12 @@
|
||||
{
|
||||
"issueRef": "#15",
|
||||
"path": "15",
|
||||
"title": "Limites de session — re-livraison automatique parquée au reset (stretch B5, délégation durable)",
|
||||
"title": "[Bloqué par #7 — design durable] Limites de session — re-livraison auto parquée au reset (stretch B5)",
|
||||
"status": "open",
|
||||
"priority": "low",
|
||||
"sprint": "d8f3f37b-87ca-4509-9116-45a99bc711df",
|
||||
"sprint": "e28a4d53-8bd2-446a-b0ac-2a017373b8b2",
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1783940786085
|
||||
"updatedAt": 1784093861343
|
||||
},
|
||||
{
|
||||
"issueRef": "#16",
|
||||
@ -229,11 +229,11 @@
|
||||
"issueRef": "#20",
|
||||
"path": "20",
|
||||
"title": "[Backend] Dette ticket_list — matching #ref dans text + curseur opaque/stable",
|
||||
"status": "open",
|
||||
"status": "closed",
|
||||
"priority": "low",
|
||||
"sprint": null,
|
||||
"sprint": "e28a4d53-8bd2-446a-b0ac-2a017373b8b2",
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1783331176736
|
||||
"updatedAt": 1784049665491
|
||||
},
|
||||
{
|
||||
"issueRef": "#21",
|
||||
@ -333,11 +333,11 @@
|
||||
"issueRef": "#31",
|
||||
"path": "31",
|
||||
"title": "Détection de profils séquentielle : N × 800 ms au pire, résultat potentiellement partiel",
|
||||
"status": "open",
|
||||
"status": "closed",
|
||||
"priority": "low",
|
||||
"sprint": null,
|
||||
"sprint": "e28a4d53-8bd2-446a-b0ac-2a017373b8b2",
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1783491197541
|
||||
"updatedAt": 1784064442657
|
||||
},
|
||||
{
|
||||
"issueRef": "#32",
|
||||
@ -355,21 +355,21 @@
|
||||
"issueRef": "#33",
|
||||
"path": "33",
|
||||
"title": "[Dette] Fallthrough silencieux du routage structuré (lifecycle.rs:1705)",
|
||||
"status": "open",
|
||||
"status": "closed",
|
||||
"priority": "high",
|
||||
"sprint": null,
|
||||
"sprint": "e28a4d53-8bd2-446a-b0ac-2a017373b8b2",
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1783492190391
|
||||
"updatedAt": 1784048928225
|
||||
},
|
||||
{
|
||||
"issueRef": "#34",
|
||||
"path": "34",
|
||||
"title": "[Risque] ChatBridge.scrollback non borné (croissance mémoire)",
|
||||
"status": "open",
|
||||
"status": "closed",
|
||||
"priority": "medium",
|
||||
"sprint": null,
|
||||
"sprint": "e28a4d53-8bd2-446a-b0ac-2a017373b8b2",
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1783492562056
|
||||
"updatedAt": 1784050010085
|
||||
},
|
||||
{
|
||||
"issueRef": "#35",
|
||||
@ -581,11 +581,127 @@
|
||||
"issueRef": "#54",
|
||||
"path": "54",
|
||||
"title": "[UI] Ajouter le handle du téléchargement des modeles lors du démarage llamacpp",
|
||||
"status": "inProgress",
|
||||
"status": "closed",
|
||||
"priority": "medium",
|
||||
"sprint": "5afd6780-0f76-40d7-a10f-32ee52469d74",
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1783976286882
|
||||
"updatedAt": 1784018300921
|
||||
},
|
||||
{
|
||||
"issueRef": "#55",
|
||||
"path": "55",
|
||||
"title": "[Bug] Error on loading local model",
|
||||
"status": "inProgress",
|
||||
"priority": "high",
|
||||
"sprint": "e28a4d53-8bd2-446a-b0ac-2a017373b8b2",
|
||||
"assignedAgentIds": [
|
||||
"a6ced819-b893-4213-b003-9e9dc79b9641"
|
||||
],
|
||||
"updatedAt": 1784097757001
|
||||
},
|
||||
{
|
||||
"issueRef": "#56",
|
||||
"path": "56",
|
||||
"title": "[Bug] selection desactivée d'agent non affichés",
|
||||
"status": "closed",
|
||||
"priority": "low",
|
||||
"sprint": "e28a4d53-8bd2-446a-b0ac-2a017373b8b2",
|
||||
"assignedAgentIds": [
|
||||
"a6ced819-b893-4213-b003-9e9dc79b9641"
|
||||
],
|
||||
"updatedAt": 1784048212123
|
||||
},
|
||||
{
|
||||
"issueRef": "#58",
|
||||
"path": "58",
|
||||
"title": "[B/F] Rendu live des tâches de fond — subscriber UI + canal IPC attachable au task output",
|
||||
"status": "open",
|
||||
"priority": "low",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784064521992
|
||||
},
|
||||
{
|
||||
"issueRef": "#60",
|
||||
"path": "60",
|
||||
"title": "[Bug] Assistant IA d'édition de ticket : aucune réponse dans la conversation (stream sans Final non signalé)",
|
||||
"status": "inProgress",
|
||||
"priority": "high",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784095176619
|
||||
},
|
||||
{
|
||||
"issueRef": "#61",
|
||||
"path": "61",
|
||||
"title": "[UI] rafraichissement des cellule",
|
||||
"status": "open",
|
||||
"priority": "low",
|
||||
"sprint": "e28a4d53-8bd2-446a-b0ac-2a017373b8b2",
|
||||
"assignedAgentIds": [
|
||||
"a6ced819-b893-4213-b003-9e9dc79b9641"
|
||||
],
|
||||
"updatedAt": 1784182133142
|
||||
},
|
||||
{
|
||||
"issueRef": "#62",
|
||||
"path": "62",
|
||||
"title": "[Sécurité/Cohérence] Identité requester explicite pour les sessions structurées + policy des tools OpenAI-compatible",
|
||||
"status": "open",
|
||||
"priority": "medium",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784095187734
|
||||
},
|
||||
{
|
||||
"issueRef": "#63",
|
||||
"path": "63",
|
||||
"title": "Systeme de test de l'UI",
|
||||
"status": "open",
|
||||
"priority": "medium",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784098438671
|
||||
},
|
||||
{
|
||||
"issueRef": "#64",
|
||||
"path": "64",
|
||||
"title": "Web : créer/ajouter un projet depuis le navigateur (folder browser serveur)",
|
||||
"status": "open",
|
||||
"priority": "medium",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784187610934
|
||||
},
|
||||
{
|
||||
"issueRef": "#65",
|
||||
"path": "65",
|
||||
"title": "Serveur headless : extraire idea-serve du binaire Tauri (cœur partagé)",
|
||||
"status": "open",
|
||||
"priority": "high",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784187583229
|
||||
},
|
||||
{
|
||||
"issueRef": "#66",
|
||||
"path": "66",
|
||||
"title": "Image Docker serveur/client IdeA",
|
||||
"status": "open",
|
||||
"priority": "medium",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784187597004
|
||||
},
|
||||
{
|
||||
"issueRef": "#67",
|
||||
"path": "67",
|
||||
"title": "Lock inter-process de l'app-data-dir (desktop ↔ idea --serve)",
|
||||
"status": "open",
|
||||
"priority": "low",
|
||||
"sprint": null,
|
||||
"assignedAgentIds": [],
|
||||
"updatedAt": 1784187618710
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user