chore(ideai): état runtime — permissions et tâche de fond

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-23 12:44:10 +02:00
parent 1784027f5d
commit ef747cd156
2 changed files with 170 additions and 2 deletions

View File

@ -12519,10 +12519,140 @@
"ticket_id": "661fe0c9-6313-45f0-939f-34f4fecf3df4", "ticket_id": "661fe0c9-6313-45f0-939f-34f4fecf3df4",
"conversation_id": "6bc594e8-a37c-0dbd-1de6-6e3b73002cb4" "conversation_id": "6bc594e8-a37c-0dbd-1de6-6e3b73002cb4"
}, },
"state": "running", "state": "completed",
"wakePolicy": "recordOnly", "wakePolicy": "recordOnly",
"createdAtMs": 1784789813681, "createdAtMs": 1784789813681,
"updatedAtMs": 1784789813681, "updatedAtMs": 1784789843631,
"deadlineMs": null,
"result": {
"outcome": "success",
"finished_at_ms": 1784789843631,
"exit_code": null,
"summary": "Headless rendezvous completed with Final",
"stdout_tail": "**Décision Git — ticket #92 (rouvert) :**\n\n- L'état runtime (tickets #7/#43/#64/#92, tâche de fond, compteur/index) a été committé directement sur `develop` : `162e3ae` — pas de code de feature dedans, cohérent avec l'usage déjà en place sur cette branche.\n- Le travail précédent (`feature/ticket92-opencode-provider-cloud`) est terminé et déjà mergé (`12c7d10`) ; la branche est supprimée. Ce nouveau lot est un complément post-livraison avec un scope distinct (catalogue dynamique via cache `~/.cache/opencode/models.json` + fallback statique, option \"provider personnalisé\" en saisie libre) → **nouvelle branche de feature** plutôt qu'une reprise, pour ne pas mélanger l'historique de la livraison initiale avec ce complément.\n- Créée depuis `develop` (à jour, contient le merge du ticket #92 initial) : **`feature/ticket92-opencode-provider-dynamic-catalog`**, checkout effectué.\n\nDevBackend et DevFrontend peuvent implémenter sur cette branche. Je repasserai commiter et statuer sur le merge une fois l'implémentation terminée et testée par QA.",
"stderr_tail": null
},
"completionDelivered": false
},
{
"id": "401e1617-1652-4da8-88d1-784347a871d6",
"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": "f48b9320-8458-44bf-8505-22a7ee5955e8",
"conversation_id": "d5068bc8-786e-0428-1d14-83b93961a15e"
},
"state": "completed",
"wakePolicy": "recordOnly",
"createdAtMs": 1784789862061,
"updatedAtMs": 1784789872415,
"deadlineMs": null,
"result": {
"outcome": "success",
"finished_at_ms": 1784789872415,
"exit_code": null,
"summary": "Headless rendezvous completed with Final",
"stdout_tail": "You've hit your session limit · resets 12:20pm (Europe/Paris)",
"stderr_tail": null
},
"completionDelivered": false
},
{
"id": "54bbad3a-894c-4114-bec1-79ddf862b30a",
"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": "18b42fb1-2ce8-4151-a87f-d4106721d55d",
"conversation_id": "d5068bc8-786e-0428-1d14-83b93961a15e"
},
"state": "completed",
"wakePolicy": "recordOnly",
"createdAtMs": 1784802330858,
"updatedAtMs": 1784802882874,
"deadlineMs": null,
"result": {
"outcome": "success",
"finished_at_ms": 1784802882874,
"exit_code": null,
"summary": "Headless rendezvous completed with Final",
"stdout_tail": "All 6 new targeted tests pass. Full workspace build and test suite (`--test-threads=1`) are green.\n\n## Summary\n\nOn `feature/ticket92-opencode-provider-dynamic-catalog`:\n\n**1. Dynamic provider catalogue** (`crates/application/src/agent/provider_catalogue.rs`)\n- `opencode_provider_catalogue()` now reads `<home>/.cache/opencode/models.json` (via `dirs::home_dir()` + literal suffix, respecting `$XDG_CACHE_HOME` — never `dirs::cache_dir()`, per OpenCode's upstream issue #8235), parses the models.dev shape, and falls back permanently to the original 3-entry static catalogue on any absence/read/parse failure.\n- Split fallback logic into a pure `catalogue_from_cache_bytes` for testability; validated the parsing against the real local OpenCode cache (`~/.cache/opencode/models.json`, 170 providers) to confirm the schema.\n- `OpenCodeProviderCatalogEntry` changed from `&'static str`/`&'static [&'static str]` to owned `String`/`Vec<String>` (no longer static data); updated `backend/src/dto.rs`'s `From` impl accordingly.\n- Added `dirs = \"6\"` as a workspace + `application` dependency (already resolved transitively, so no lockfile surprises).\n\n**2. Custom provider option**\n- `domain::profile::OpenCodeProviderConfig` gained an additive `custom: Option<CustomProviderConfig>` field (`#[serde(default, skip_serializing_if = \"Option::is_none\")]`, backward-compatible), plus a new `CustomProviderConfig` value object (`npm`, `base_url`, optional `display_name`) and a `.with_custom()` builder.\n- `SaveOpenCodeProviderProfileInput`/`SaveOpenCodeProviderProfile` extended with the same optional `custom` field, wired through `backend/src/dto.rs`'s request DTO.\n- Both `opencode_provider_config_json` implementations (`infrastructure/src/assistant/mod.rs` and `application/src/agent/lifecycle.rs`) now emit `npm`, `options.baseURL`, and a `models` block when `custom` is set, leaving the known-provider path (just `options.apiKey`) untouched.\n\nAdded unit tests for cache parsing (present/absent/malformed/empty) and for standard-vs-custom JSON generation in both duplicated sites. `cargo build --workspace` and `cargo test --workspace -- --test-threads=1` are fully green.",
"stderr_tail": null
},
"completionDelivered": false
},
{
"id": "b8710867-a325-4966-9c42-d7fe73b5a651",
"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": "2be17edc-f009-46ee-9a26-bf70632a4109",
"conversation_id": "09b15ec3-ce2f-0af2-2903-ef699df9de41"
},
"state": "completed",
"wakePolicy": "recordOnly",
"createdAtMs": 1784802903931,
"updatedAtMs": 1784803232024,
"deadlineMs": null,
"result": {
"outcome": "success",
"finished_at_ms": 1784803232024,
"exit_code": null,
"summary": "Headless rendezvous completed with Final",
"stdout_tail": "Frontend complement of ticket #92 is implemented and green.\n\n**Summary of changes** (all in `frontend/src/`):\n- `domain/index.ts`: added `CustomProviderConfig` type and `custom?` field on `OpenCodeProviderConfig`.\n- `ports/index.ts`: added optional `custom?` to `SaveOpenCodeProviderProfileInput`.\n- `adapters/profile.ts`, `adapters/http/requestResponseGateways.ts`, `adapters/mock/index.ts`: pass `custom` through to the backend call / mock save.\n- `features/first-run/FirstRunWizard.tsx` (`OpenCodeProviderFields`): \n - Added a filter `<input>` above the provider `<select>` (shown once the catalogue is loaded) that narrows the up-to-~170 options by display name/id, while always keeping the current selection visible in the list.\n - Added an \"Autre / personnalisé…\" sentinel entry at the bottom of the provider select; picking it switches to a custom sub-form (provider id, npm package prefilled with `@ai-sdk/openai-compatible`, base URL, model id, optional display name), with a \"← Choisir un provider du catalogue\" button to switch back.\n - Inline validation extended for the two new required custom fields (npm, base URL); API key behavior (never prefilled, masked, re-entered on every save) is unchanged.\n - Edit mode: an existing profile with `opencodeProvider.custom` set preselects custom mode and prefills all custom fields except the API key.\n\n**UX calls I made without asking**, per your instructions:\n- Kept it a plain `<select>` + separate filter `<input>` rather than building a new combobox component — matches the codebase's existing \"no custom combobox\" convention and needed no new shared UI primitive.\n- Filter input only renders once the catalogue is ready (not during loading/error), reusing existing loading/error states.\n- Placed \"Autre / personnalisé…\" as the last `<option>` in the same select rather than a separate toggle, consistent with picking it out of a single list.\n\nVerified: `npx tsc --noEmit`, `npm run build`, and `npx vitest run` are all green (958 tests, including 6 new tests covering select→custom mode swap, submit validation, save mapping, blank-optional-field omission, back-to-catalogue toggle, and edit-mode prefill).",
"stderr_tail": null
},
"completionDelivered": false
},
{
"id": "a333da6b-a09d-4147-a43e-5c0cae9c6a86",
"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": "f14729e1-557f-4a97-9548-2d552d7a8b33",
"conversation_id": "08336578-5b47-09d2-2f41-5cc483f101f4"
},
"state": "completed",
"wakePolicy": "recordOnly",
"createdAtMs": 1784803246008,
"updatedAtMs": 1784803415051,
"deadlineMs": null,
"result": {
"outcome": "success",
"finished_at_ms": 1784803415051,
"exit_code": null,
"summary": "Headless rendezvous completed with Final",
"stdout_tail": "## Verdict : ✅ VERT\n\n**Backend** — `cargo build --workspace` : succès. `cargo test --workspace -- --test-threads=1` : **0 échec** sur l'ensemble du workspace (des centaines de tests, tous \"ok\").\n\nVérifications de contrat :\n- **Catalogue dynamique** (`provider_catalogue.rs`) : lit `<home>/.cache/opencode/models.json` en respectant `$XDG_CACHE_HOME` (avec le bug OpenCode documenté — convention XDG même hors Linux — reproduit intentionnellement, commenté). Les 4 cas sont couverts par des tests dédiés : absent (`falls_back_when_absent`), malformé (`falls_back_when_malformed`), objet vide/illisible (`falls_back_when_empty_object`), 0 provider avec modèles (`falls_back_when_no_provider_has_models`), plus le cas valide (`parses_a_valid_models_dev_payload`). `ListOpenCodeProviders::execute()` ne prend aucun `Result` — infaillible par construction, confirmé par `opencode_provider_catalogue_is_never_empty`.\n- **Champ `custom`** additif sur `OpenCodeProviderConfig` (domain) et `SaveOpenCodeProviderProfileInput`/DTO, avec `#[serde(default, skip_serializing_if = \"Option::is_none\")]` des deux côtés → un profil existant sans `custom` sérialise/désérialise exactement comme avant (`profile_with_opencode_provider_round_trips_camelcase` le confirme implicitement, `custom: None` omis du JSON).\n- **Les deux générateurs dupliqués** (`infrastructure/src/assistant/mod.rs:424` et `application/src/agent/lifecycle.rs:2749`) sont logiquement identiques : même bloc `npm`/`options.baseURL`/`models` quand `custom` est `Some`, sinon juste `apiKey`. Testés séparément dans chaque crate (`custom_provider_emits_npm_base_url_and_models`, `..._model_name_prefers_display_name`), verdict identique des deux côtés.\n\n**Frontend** — `npx tsc --noEmit` : propre. `npm run build` : succès (aucune erreur, juste un warning de taille de chunk non lié). `npx vitest run` : **958/958 tests passés** (104 fichiers), les stacktraces \"Error: render exploded\" / \"useGateways must be used within...\" visibles dans la sortie sont des tests intentionnels de error-boundary/garde-fou, pas des échecs.\n\nVérifications de contrat :\n- Picker `OpenCodeProviderFields` (`FirstRunWizard.tsx:439`) : filtre texte au-dessus du select (garde la sélection courante visible même hors-filtre), option `\"Autre / personnalisé…\"` en dernière entrée basculant vers le sous-formulaire libre (`provider_id`, `npm` préremplig `@ai-sdk/openai-compatible`, `base_url`, `model_id`, `display_name` optionnel), validation inline par champ, clé API jamais préremplie (`useState(\"\")` inconditionnel).\n- Édition d'un profil existant avec `custom` défini : `mode` préinitialisé à `\"custom\"` si `existing?.custom`, tous les champs prérempli depuis `existing.custom.*` sauf la clé — couvert explicitement par le test `\"editing an existing custom-provider profile preselects custom mode and prefills fields (except the key)\"`.\n\nAucun écart de contrat détecté par rapport au périmètre demandé. Seule remarque mineure (non bloquante) : `profile_usecases.rs` (application) n'a pas de test end-to-end passant `custom: Some(...)` à travers `SaveOpenCodeProviderProfile` — seul `None` y est exercé ; le passage `custom` y est un simple mapping DTO→Input déjà couvert côté domaine/infra, donc sans risque réel.",
"stderr_tail": null
},
"completionDelivered": false
},
{
"id": "eec2387a-31f0-4384-9604-b83a4010f4ae",
"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": "45503854-33c2-4ddd-a5e5-94a305dabd81",
"conversation_id": "6bc594e8-a37c-0dbd-1de6-6e3b73002cb4"
},
"state": "running",
"wakePolicy": "recordOnly",
"createdAtMs": 1784803425296,
"updatedAtMs": 1784803425296,
"deadlineMs": null, "deadlineMs": null,
"result": null, "result": null,
"completionDelivered": false "completionDelivered": false

View File

@ -187,6 +187,44 @@
], ],
"fallback": "allow" "fallback": "allow"
} }
},
{
"agentId": "5d07e4a7-8676-4a71-aea1-5b64caefa944",
"permissions": {
"rules": [
{
"capability": "read",
"effect": "allow",
"paths": [
"**"
],
"commands": []
},
{
"capability": "write",
"effect": "deny",
"paths": [
"**"
],
"commands": []
},
{
"capability": "delete",
"effect": "deny",
"paths": [
"**"
],
"commands": []
},
{
"capability": "executeBash",
"effect": "allow",
"paths": [],
"commands": []
}
],
"fallback": "allow"
}
} }
] ]
} }