[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6DHKBVTQ0CVQVWTD2KCS89P":3,"skill-same:01M6DHKBVTQ0CVQVWTD2KCS89P":52},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":27,"linkOnly":28,"markdownContent":29,"markdownBody":30,"frontMatter":31,"refName":32,"commitSha":33,"skillDirectory":34,"forksCount":13,"repositoryHtmlUrl":35,"repositoryHomepageUrl":6,"ownerHtmlUrl":36,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":37,"install":38,"resources":51},"01M6DHKBVTQ0CVQVWTD2KCS89P","review-commenter",null,"diff-first-based skill consuming git_diff, test_results, lint_results to produce review_comments","mirandaguillaume\u002Fforgent","mirandaguillaume","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F3329801?v=4","01M6DHHVZ6V5JZNWVNX5YJEY3C","01M6DHHW3M6R87RVHP7QXZ72ND",0,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fmirandaguillaume\u002Fforgent\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Freview-commenter\u002FSKILL.md",[],[18,19,20,21,22,23,24,25,26],"review","review commenter","test","lint","git","produce","first","results","diff","2026-11-10 10:12:30",false,"---\nname: review-commenter\ndescription: diff-first-based skill consuming git_diff, test_results, lint_results to produce review_comments\n---\n\n# Review Commenter\n\n## Guardrails\n- max_comments: 15\n- timeout: 5min\n- no_approve_without_tests\n\n## Context\nConsumes: git_diff, test_results, lint_results\nProduces: review_comments\nMemory: conversation\n\n## Output Format\n\n### Output: Review_comments\n\nProvide your review as a structured list. For each finding:\n\n1. **Severity**: critical | warning | suggestion\n2. **Location**: `file:line`\n3. **Issue**: One-sentence description\n4. **Fix**: Concrete code suggestion or action\n5. **Rationale**: Why this matters\n\nGroup findings by severity (critical first). End with a summary count.\n\n---\n\n# Review Comments — feat\u002Fscanner-enricher-bench → main\n\nBranch : `feat\u002Fscanner-enricher-bench`\nTests  : 706\u002F706 pass\nLint   : 103 mineurs, 0 critiques\n\n---\n\n## Critiques\n\nAucun problème bloquant identifié.\n\n---\n\n## Avertissements\n\n### 1\n\n**Severity**: warning\n**Location**: `internal\u002Fgenerator\u002Fclaude\u002Fagent.go:66`\n**Issue**: Quand un skill figure dans `agent.Skills` mais pas dans `resolvedSkills`, le générateur émet l'en-tête `### Step N: X` suivi d'une ligne vide sans bloc `Launch a subagent`, produisant une étape vide dans le markdown final.\n**Fix**: Déplacer l'ajout de l'en-tête à l'intérieur de la branche `if s.Skill == skillName` afin qu'il ne soit émis que si le skill est effectivement résolu, ou ajouter un commentaire `(skill not resolved)` explicite.\n\n```go\n\u002F\u002F Avant (ligne 66-88)\nlines = append(lines, fmt.Sprintf(\"### Step %d: %s\", i+1, generator.ToTitle(skillName)))\neffort := model.EffortMedium\nfor _, s := range resolvedSkills {\n    if s.Skill == skillName {\n        lines = append(lines, \"Launch a subagent:\")\n        ...\n        break\n    }\n}\nlines = append(lines, \"\")\n\n\u002F\u002F Après — déplacer l'en-tête à l'intérieur du if\nfor _, s := range resolvedSkills {\n    if s.Skill == skillName {\n        lines = append(lines, fmt.Sprintf(\"### Step %d: %s\", i+1, generator.ToTitle(skillName)))\n        lines = append(lines, \"Launch a subagent:\")\n        ...\n        break\n    }\n}\n```\n**Rationale**: Le même problème existe dans `internal\u002Fgenerator\u002Fcopilot\u002Fagent.go:66`. Un skill non résolu produit du markdown silencieusement cassé sans que le build échoue ni n'avertisse au niveau du générateur.\n\n---\n\n### 2\n\n**Severity**: warning\n**Location**: `internal\u002Fgenerator\u002Fcopilot\u002Feffort.go:6`\n**Issue**: `EffortToModel` du package `copilot` retourne `\"haiku\"`, `\"sonnet\"`, `\"opus\"` — des noms de modèles Anthropic — alors que GitHub Copilot utilise des modèles GPT\u002FOpenAI.\n**Fix**: Mapper vers les identifiants corrects pour Copilot (ex. `\"gpt-4o-mini\"`, `\"gpt-4o\"`, `\"o1\"`) ou documenter explicitement que ces noms sont intentionnellement génériques et laissés à l'utilisateur.\n**Rationale**: Un agent Copilot généré avec `Model: haiku` sera incompréhensible pour un utilisateur GitHub Copilot ; cela peut induire des erreurs de configuration silencieuses.\n\n---\n\n### 3\n\n**Severity**: warning\n**Location**: `internal\u002Fgenerator\u002Fcopilot\u002Feffort.go` (fichier entier)\n**Issue**: `EffortToModel` est dupliqué à l'identique dans `internal\u002Fgenerator\u002Fclaude\u002Feffort.go` et `internal\u002Fgenerator\u002Fcopilot\u002Feffort.go` sans factorisation.\n**Fix**: Extraire la logique commune dans `internal\u002Fgenerator\u002Feffort.go` (package `generator`) et appeler `generator.EffortToModel` depuis chaque package, ou au moins ajouter un commentaire signalant la divergence intentionnelle (noms de modèles différents par cible).\n**Rationale**: Toute évolution future (ajout d'un niveau `EffortUltra`) devra être dupliquée manuellement, avec risque de désynchronisation.\n\n---\n\n### 4\n\n**Severity**: warning\n**Location**: `internal\u002Fgenerator\u002Fclaude\u002Fagent.go:44`\n**Issue**: La section `## Execution` et le texte d'orchestration sont toujours émis, même quand `resolvedSkills` est vide (ex. aucun skill chargé), ce qui produit un agent avec `Execute 2 skills sequentially...` mais aucun step.\n**Fix**: Conditionner l'émission du bloc `## Execution` à `len(resolvedSkills) > 0`, ou au moins à `len(agent.Skills) > 0`.\n**Rationale**: Le test `TestGenerateAgentMd_NoSkills` passe `nil` comme `resolvedSkills` mais `agent.Skills` reste `[\"code-review\",\"security-scan\"]` — l'output contient le texte d'orchestration sans aucune étape concrète.\n\n---\n\n### 5\n\n**Severity**: warning\n**Location**: `pkg\u002Fmodel\u002Fskill.go:143–148` (suppression de `DependencyFacet` \u002F `DependsOn`)\n**Issue**: Le champ `DependsOn DependencyFacet` a été supprimé du modèle `SkillBehavior` sans versioning ni note de migration : les fichiers `.skill.yaml` existants contenant `depends_on:` seront silencieusement ignorés par le loader YAML.\n**Fix**: Ajouter un avertissement dans le loader YAML ou le linter quand un champ `depends_on` est détecté dans le fichier source (strict mode YAML ou vérification explicite).\n**Rationale**: Un breaking change silencieux sur le format YAML de définition des skills peut casser des projets utilisateurs sans message d'erreur.\n\n---\n\n## Suggestions\n\n### 6\n\n**Severity**: suggestion\n**Location**: `internal\u002Fgenerator\u002Fcopilot\u002Fagent.go:35`\n**Issue**: La valeur `tools: [\"task\"]` est un littéral de chaîne Go brut non construit programmatiquement, contrairement à `GenerateCompactCopilotAgentMd` (ligne 138) qui génère le format `tools: [\"read\", ...]` via `fmt.Sprintf`.\n**Fix**: Utiliser une constante ou une construction cohérente :\n\n```go\nconst copilotTaskTool = `tools: [\"task\"]`\nlines = append(lines, copilotTaskTool)\n```\n**Rationale**: Incohérence de style entre les deux fonctions du même fichier ; risque d'erreur typographique lors d'une future modification.\n\n---\n\n### 7\n\n**Severity**: suggestion\n**Location**: `internal\u002Fgenerator\u002Fclaude\u002Fagent.go:41`\n**Issue**: Le corps de l'agent en mode dispatch ne contient plus la `description` de l'agent (remplacée par le template fixe `An orchestrator that coordinates N specialized subagents`), alors que `GenerateCompactAgentMd` (ligne 150) conserve la description dans le corps.\n**Fix**: Soit inclure la description dans les deux modes de façon cohérente :\n\n```go\nlines = append(lines, fmt.Sprintf(\"You are %s. %s Orchestrates %d specialized subagents.\",\n    generator.ToTitle(agent.Agent), agent.Description, len(agent.Skills)))\n```\nsoit documenter que le mode dispatch n'expose pas la description dans le corps.\n**Rationale**: Incohérence comportementale entre `--compact` et mode standard ; peut surprendre les utilisateurs qui personnalisent la description.\n\n---\n\n### 8\n\n**Severity**: suggestion\n**Location**: `internal\u002Fgenerator\u002Fcopilot\u002Feffort.go` (absence de fichier `effort_test.go`)\n**Issue**: `EffortToModel` dans le package `copilot` n'a aucun test unitaire dédié, alors que le package `claude` possède `effort_test.go`.\n**Fix**: Créer `internal\u002Fgenerator\u002Fcopilot\u002Feffort_test.go` avec les quatre cas (`light`, `medium`, `heavy`, `\"\"`).\n**Rationale**: Couverture asymétrique entre les deux packages pour une fonction identique.\n\n---\n\n### 9\n\n**Severity**: suggestion\n**Location**: `internal\u002Fgenerator\u002Fclaude\u002Fagent.go:114`\n**Issue**: `formatIOPointers` est une fonction privée triviale (2 lignes) qui duplique partiellement la logique de formatage d'I\u002FO déjà présente dans `internal\u002Fgenerator\u002Futil.go`.\n**Fix**: Vérifier si `generator.FormatIO` ou une fonction similaire existe dans `util.go` et consolider, ou au moins ajouter un commentaire de justification.\n**Rationale**: Fragmentation des helpers de formatage entre `util.go` (package `generator`) et les sous-packages.\n\n---\n\n### 10\n\n**Severity**: suggestion\n**Location**: `internal\u002Fgenerator\u002Fclaude\u002Fagent_test.go:112`\n**Issue**: `TestGenerateAgentMd_NoSkills` passe `resolvedSkills=nil` mais l'agent a toujours `Skills: [\"code-review\",\"security-scan\"]` — la condition testée est donc `resolvedSkills vide` et non `agent.Skills vide` ; le cas `agent.Skills=[]` + `resolvedSkills=[]` n'est pas couvert.\n**Fix**: Ajouter un test explicite :\n\n```go\nfunc TestGenerateAgentMd_EmptySkillsList(t *testing.T) {\n    agent := testAgent()\n    agent.Skills = nil\n    md := claude.GenerateAgentMd(agent, nil, \".claude\")\n    assert.NotContains(t, md, \"## Execution\")\n}\n```\n**Rationale**: La validation `ValidateAgent` rejette `len(Skills)==0`, mais ce chemin de génération reste non couvert.\n\n---\n\n### 11\n\n**Severity**: suggestion\n**Location**: `internal\u002Fgenerator\u002Fclaude\u002Fagent_test.go` (absence de test d'étape orpheline)\n**Issue**: Aucun test ne vérifie le comportement quand `agent.Skills` référence un skill absent de `resolvedSkills` (ex. skill déclaré mais non chargé).\n**Fix**: Ajouter :\n\n```go\nfunc TestGenerateAgentMd_UnresolvedSkill_NoOrphanBlock(t *testing.T) {\n    agent := testAgent() \u002F\u002F Skills: [\"code-review\", \"security-scan\"]\n    \u002F\u002F Ne passer qu'un seul des deux skills\n    md := claude.GenerateAgentMd(agent, testResolvedSkills()[:1], \".claude\")\n    \u002F\u002F Vérifier que l'étape sans skill résolu ne produit pas de header vide\n    assert.NotContains(t, md, \"### Step 2: Security Scan\\n\\n\")\n}\n```\n**Rationale**: Le comportement actuel (header + ligne vide sans contenu) est silencieux et non spécifié.\n\n---\n\n### 12\n\n**Severity**: suggestion\n**Location**: `pkg\u002Fmodel\u002Fskill.go:169-178`\n**Issue**: `WhenToUseFacet.IsEmpty()` ne vérifie que `len == 0` pour trois champs, ce qui est correct, mais la méthode n'est pas testée isolément — elle est uniquement exercée indirectement via les règles du linter et le score.\n**Fix**: Ajouter un test unitaire minimal dans `pkg\u002Fmodel\u002Fskill_test.go` pour `IsEmpty()` avec un cas vide et un cas non-vide.\n**Rationale**: Méthode publique sur un type de modèle central, sans test direct.\n\n---\n\n## Résumé\n\n| Sévérité   | Nombre |\n|------------|--------|\n| Critique   | 0      |\n| Avertissement | 5   |\n| Suggestion | 7      |\n| **Total**  | **12** |\n\nLes tests passent tous (706\u002F706). Les points les plus risqués sont l'étape orpheline en cas de skill non résolu (#1, #11) et le breaking change silencieux sur `depends_on` (#5). Le mapping de modèles Copilot (#2) est fonctionnellement incorrect pour les utilisateurs GitHub Copilot.\n\n## Strategy\nApproach: diff-first\nTools: read_file, grep, search\n\n### Steps\n1. read the git diff\n2. cross-reference with test results\n3. cross-reference with lint results\n4. identify risky changes\n5. write actionable review comments\n\n## Security\n- Filesystem: read-only\n- Network: none\n","# Review Commenter\n\n## Guardrails\n- max_comments: 15\n- timeout: 5min\n- no_approve_without_tests\n\n## Context\nConsumes: git_diff, test_results, lint_results\nProduces: review_comments\nMemory: conversation\n\n## Output Format\n\n### Output: Review_comments\n\nProvide your review as a structured list. For each finding:\n\n1. **Severity**: critical | warning | suggestion\n2. **Location**: `file:line`\n3. **Issue**: One-sentence description\n4. **Fix**: Concrete code suggestion or action\n5. **Rationale**: Why this matters\n\nGroup findings by severity (critical first). End with a summary count.\n\n---\n\n# Review Comments — feat\u002Fscanner-enricher-bench → main\n\nBranch : `feat\u002Fscanner-enricher-bench`\nTests  : 706\u002F706 pass\nLint   : 103 mineurs, 0 critiques\n\n---\n\n## Critiques\n\nAucun problème bloquant identifié.\n\n---\n\n## Avertissements\n\n### 1\n\n**Severity**: warning\n**Location**: `internal\u002Fgenerator\u002Fclaude\u002Fagent.go:66`\n**Issue**: Quand un skill figure dans `agent.Skills` mais pas dans `resolvedSkills`, le générateur émet l'en-tête `### Step N: X` suivi d'une ligne vide sans bloc `Launch a subagent`, produisant une étape vide dans le markdown final.\n**Fix**: Déplacer l'ajout de l'en-tête à l'intérieur de la branche `if s.Skill == skillName` afin qu'il ne soit émis que si le skill est effectivement résolu, ou ajouter un commentaire `(skill not resolved)` explicite.\n\n```go\n\u002F\u002F Avant (ligne 66-88)\nlines = append(lines, fmt.Sprintf(\"### Step %d: %s\", i+1, generator.ToTitle(skillName)))\neffort := model.EffortMedium\nfor _, s := range resolvedSkills {\n    if s.Skill == skillName {\n        lines = append(lines, \"Launch a subagent:\")\n        ...\n        break\n    }\n}\nlines = append(lines, \"\")\n\n\u002F\u002F Après — déplacer l'en-tête à l'intérieur du if\nfor _, s := range resolvedSkills {\n    if s.Skill == skillName {\n        lines = append(lines, fmt.Sprintf(\"### Step %d: %s\", i+1, generator.ToTitle(skillName)))\n        lines = append(lines, \"Launch a subagent:\")\n        ...\n        break\n    }\n}\n```\n**Rationale**: Le même problème existe dans `internal\u002Fgenerator\u002Fcopilot\u002Fagent.go:66`. Un skill non résolu produit du markdown silencieusement cassé sans que le build échoue ni n'avertisse au niveau du générateur.\n\n---\n\n### 2\n\n**Severity**: warning\n**Location**: `internal\u002Fgenerator\u002Fcopilot\u002Feffort.go:6`\n**Issue**: `EffortToModel` du package `copilot` retourne `\"haiku\"`, `\"sonnet\"`, `\"opus\"` — des noms de modèles Anthropic — alors que GitHub Copilot utilise des modèles GPT\u002FOpenAI.\n**Fix**: Mapper vers les identifiants corrects pour Copilot (ex. `\"gpt-4o-mini\"`, `\"gpt-4o\"`, `\"o1\"`) ou documenter explicitement que ces noms sont intentionnellement génériques et laissés à l'utilisateur.\n**Rationale**: Un agent Copilot généré avec `Model: haiku` sera incompréhensible pour un utilisateur GitHub Copilot ; cela peut induire des erreurs de configuration silencieuses.\n\n---\n\n### 3\n\n**Severity**: warning\n**Location**: `internal\u002Fgenerator\u002Fcopilot\u002Feffort.go` (fichier entier)\n**Issue**: `EffortToModel` est dupliqué à l'identique dans `internal\u002Fgenerator\u002Fclaude\u002Feffort.go` et `internal\u002Fgenerator\u002Fcopilot\u002Feffort.go` sans factorisation.\n**Fix**: Extraire la logique commune dans `internal\u002Fgenerator\u002Feffort.go` (package `generator`) et appeler `generator.EffortToModel` depuis chaque package, ou au moins ajouter un commentaire signalant la divergence intentionnelle (noms de modèles différents par cible).\n**Rationale**: Toute évolution future (ajout d'un niveau `EffortUltra`) devra être dupliquée manuellement, avec risque de désynchronisation.\n\n---\n\n### 4\n\n**Severity**: warning\n**Location**: `internal\u002Fgenerator\u002Fclaude\u002Fagent.go:44`\n**Issue**: La section `## Execution` et le texte d'orchestration sont toujours émis, même quand `resolvedSkills` est vide (ex. aucun skill chargé), ce qui produit un agent avec `Execute 2 skills sequentially...` mais aucun step.\n**Fix**: Conditionner l'émission du bloc `## Execution` à `len(resolvedSkills) > 0`, ou au moins à `len(agent.Skills) > 0`.\n**Rationale**: Le test `TestGenerateAgentMd_NoSkills` passe `nil` comme `resolvedSkills` mais `agent.Skills` reste `[\"code-review\",\"security-scan\"]` — l'output contient le texte d'orchestration sans aucune étape concrète.\n\n---\n\n### 5\n\n**Severity**: warning\n**Location**: `pkg\u002Fmodel\u002Fskill.go:143–148` (suppression de `DependencyFacet` \u002F `DependsOn`)\n**Issue**: Le champ `DependsOn DependencyFacet` a été supprimé du modèle `SkillBehavior` sans versioning ni note de migration : les fichiers `.skill.yaml` existants contenant `depends_on:` seront silencieusement ignorés par le loader YAML.\n**Fix**: Ajouter un avertissement dans le loader YAML ou le linter quand un champ `depends_on` est détecté dans le fichier source (strict mode YAML ou vérification explicite).\n**Rationale**: Un breaking change silencieux sur le format YAML de définition des skills peut casser des projets utilisateurs sans message d'erreur.\n\n---\n\n## Suggestions\n\n### 6\n\n**Severity**: suggestion\n**Location**: `internal\u002Fgenerator\u002Fcopilot\u002Fagent.go:35`\n**Issue**: La valeur `tools: [\"task\"]` est un littéral de chaîne Go brut non construit programmatiquement, contrairement à `GenerateCompactCopilotAgentMd` (ligne 138) qui génère le format `tools: [\"read\", ...]` via `fmt.Sprintf`.\n**Fix**: Utiliser une constante ou une construction cohérente :\n\n```go\nconst copilotTaskTool = `tools: [\"task\"]`\nlines = append(lines, copilotTaskTool)\n```\n**Rationale**: Incohérence de style entre les deux fonctions du même fichier ; risque d'erreur typographique lors d'une future modification.\n\n---\n\n### 7\n\n**Severity**: suggestion\n**Location**: `internal\u002Fgenerator\u002Fclaude\u002Fagent.go:41`\n**Issue**: Le corps de l'agent en mode dispatch ne contient plus la `description` de l'agent (remplacée par le template fixe `An orchestrator that coordinates N specialized subagents`), alors que `GenerateCompactAgentMd` (ligne 150) conserve la description dans le corps.\n**Fix**: Soit inclure la description dans les deux modes de façon cohérente :\n\n```go\nlines = append(lines, fmt.Sprintf(\"You are %s. %s Orchestrates %d specialized subagents.\",\n    generator.ToTitle(agent.Agent), agent.Description, len(agent.Skills)))\n```\nsoit documenter que le mode dispatch n'expose pas la description dans le corps.\n**Rationale**: Incohérence comportementale entre `--compact` et mode standard ; peut surprendre les utilisateurs qui personnalisent la description.\n\n---\n\n### 8\n\n**Severity**: suggestion\n**Location**: `internal\u002Fgenerator\u002Fcopilot\u002Feffort.go` (absence de fichier `effort_test.go`)\n**Issue**: `EffortToModel` dans le package `copilot` n'a aucun test unitaire dédié, alors que le package `claude` possède `effort_test.go`.\n**Fix**: Créer `internal\u002Fgenerator\u002Fcopilot\u002Feffort_test.go` avec les quatre cas (`light`, `medium`, `heavy`, `\"\"`).\n**Rationale**: Couverture asymétrique entre les deux packages pour une fonction identique.\n\n---\n\n### 9\n\n**Severity**: suggestion\n**Location**: `internal\u002Fgenerator\u002Fclaude\u002Fagent.go:114`\n**Issue**: `formatIOPointers` est une fonction privée triviale (2 lignes) qui duplique partiellement la logique de formatage d'I\u002FO déjà présente dans `internal\u002Fgenerator\u002Futil.go`.\n**Fix**: Vérifier si `generator.FormatIO` ou une fonction similaire existe dans `util.go` et consolider, ou au moins ajouter un commentaire de justification.\n**Rationale**: Fragmentation des helpers de formatage entre `util.go` (package `generator`) et les sous-packages.\n\n---\n\n### 10\n\n**Severity**: suggestion\n**Location**: `internal\u002Fgenerator\u002Fclaude\u002Fagent_test.go:112`\n**Issue**: `TestGenerateAgentMd_NoSkills` passe `resolvedSkills=nil` mais l'agent a toujours `Skills: [\"code-review\",\"security-scan\"]` — la condition testée est donc `resolvedSkills vide` et non `agent.Skills vide` ; le cas `agent.Skills=[]` + `resolvedSkills=[]` n'est pas couvert.\n**Fix**: Ajouter un test explicite :\n\n```go\nfunc TestGenerateAgentMd_EmptySkillsList(t *testing.T) {\n    agent := testAgent()\n    agent.Skills = nil\n    md := claude.GenerateAgentMd(agent, nil, \".claude\")\n    assert.NotContains(t, md, \"## Execution\")\n}\n```\n**Rationale**: La validation `ValidateAgent` rejette `len(Skills)==0`, mais ce chemin de génération reste non couvert.\n\n---\n\n### 11\n\n**Severity**: suggestion\n**Location**: `internal\u002Fgenerator\u002Fclaude\u002Fagent_test.go` (absence de test d'étape orpheline)\n**Issue**: Aucun test ne vérifie le comportement quand `agent.Skills` référence un skill absent de `resolvedSkills` (ex. skill déclaré mais non chargé).\n**Fix**: Ajouter :\n\n```go\nfunc TestGenerateAgentMd_UnresolvedSkill_NoOrphanBlock(t *testing.T) {\n    agent := testAgent() \u002F\u002F Skills: [\"code-review\", \"security-scan\"]\n    \u002F\u002F Ne passer qu'un seul des deux skills\n    md := claude.GenerateAgentMd(agent, testResolvedSkills()[:1], \".claude\")\n    \u002F\u002F Vérifier que l'étape sans skill résolu ne produit pas de header vide\n    assert.NotContains(t, md, \"### Step 2: Security Scan\\n\\n\")\n}\n```\n**Rationale**: Le comportement actuel (header + ligne vide sans contenu) est silencieux et non spécifié.\n\n---\n\n### 12\n\n**Severity**: suggestion\n**Location**: `pkg\u002Fmodel\u002Fskill.go:169-178`\n**Issue**: `WhenToUseFacet.IsEmpty()` ne vérifie que `len == 0` pour trois champs, ce qui est correct, mais la méthode n'est pas testée isolément — elle est uniquement exercée indirectement via les règles du linter et le score.\n**Fix**: Ajouter un test unitaire minimal dans `pkg\u002Fmodel\u002Fskill_test.go` pour `IsEmpty()` avec un cas vide et un cas non-vide.\n**Rationale**: Méthode publique sur un type de modèle central, sans test direct.\n\n---\n\n## Résumé\n\n| Sévérité   | Nombre |\n|------------|--------|\n| Critique   | 0      |\n| Avertissement | 5   |\n| Suggestion | 7      |\n| **Total**  | **12** |\n\nLes tests passent tous (706\u002F706). Les points les plus risqués sont l'étape orpheline en cas de skill non résolu (#1, #11) et le breaking change silencieux sur `depends_on` (#5). Le mapping de modèles Copilot (#2) est fonctionnellement incorrect pour les utilisateurs GitHub Copilot.\n\n## Strategy\nApproach: diff-first\nTools: read_file, grep, search\n\n### Steps\n1. read the git diff\n2. cross-reference with test results\n3. cross-reference with lint results\n4. identify risky changes\n5. write actionable review comments\n\n## Security\n- Filesystem: read-only\n- Network: none\n",{"name":5,"description":7},"main","4d9b9a91bfce5fb950004c0d9a4e000cc0deea83",".github\u002Fskills\u002Freview-commenter","https:\u002F\u002Fgithub.com\u002Fmirandaguillaume\u002Fforgent","https:\u002F\u002Fgithub.com\u002Fmirandaguillaume","2026-11-02 23:00:19",{"source":8,"skillName":5,"commands":39,"prompt":49,"zipAvailable":50},[40,43,46],{"tool":41,"command":42},"npx","npx skills add mirandaguillaume\u002Fforgent --skill review-commenter",{"tool":44,"command":45},"bunx","bunx skills add mirandaguillaume\u002Fforgent --skill review-commenter",{"tool":47,"command":48},"pnpm","pnpm dlx skills add mirandaguillaume\u002Fforgent --skill review-commenter","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fmirandaguillaume\u002Fforgent\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Freview-commenter\u002FSKILL.md",true,[],[53,64,81,93,104,113],{"skillNo":54,"name":55,"nameZh":6,"description":56,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":57,"categories":58,"keywords":59,"publishedAt":27},"01M6DHNP7G7M2C35EZJSZMTX1M","identify-root-cause","Identify the root cause of the issue through detailed code examination-based skill consuming code_analysis, issue_description to produce root_cause","https:\u002F\u002Fgithub.com\u002Fmirandaguillaume\u002Fforgent\u002Fblob\u002Fmain\u002Finternal\u002Fbench\u002Ffixtures\u002Fswebench-imported\u002Fsweagent-v1\u002Foutput-standard\u002Fskills\u002Fidentify-root-cause\u002FSKILL.md",[],[60,61,23,62,63],"identify root cause","code","analysis","issue",{"skillNo":65,"name":66,"nameZh":6,"description":67,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":68,"categories":69,"keywords":75,"publishedAt":27},"01M6DHNKSARH3N7Z6QR0KXR23F","generate-code-fix","Generate minimal code changes that resolve the identified issue-based skill consuming root_cause, issue_description to produce code_patch","https:\u002F\u002Fgithub.com\u002Fmirandaguillaume\u002Fforgent\u002Fblob\u002Fmain\u002Finternal\u002Fbench\u002Ffixtures\u002Fswebench-imported\u002Fsweagent-v1\u002Foutput-standard\u002Fskills\u002Fgenerate-code-fix\u002FSKILL.md",[70],{"categoryNo":71,"categoryName":72,"taxonomyType":73,"taxonomyTypeDesc":74},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[76,77,61,78,79,23,80,63],"generate code fix","fix","generate","minimal","patch",{"skillNo":82,"name":83,"nameZh":6,"description":84,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":85,"categories":86,"keywords":88,"publishedAt":27},"01M6DHNHAWKHCWGN9TJ5439DXE","analyze-code-repository","Systematically analyze repository structure to locate code relevant to the issue-based skill consuming repository, issue_description to produce code_analysis","https:\u002F\u002Fgithub.com\u002Fmirandaguillaume\u002Fforgent\u002Fblob\u002Fmain\u002Finternal\u002Fbench\u002Ffixtures\u002Fswebench-imported\u002Fsweagent-v1\u002Foutput-standard\u002Fskills\u002Fanalyze-code-repository\u002FSKILL.md",[87],{"categoryNo":71,"categoryName":72,"taxonomyType":73,"taxonomyTypeDesc":74},[89,90,91,61,63,92,62,23],"analyze code repository","analyze","repository","structure",{"skillNo":94,"name":95,"nameZh":6,"description":96,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":97,"categories":98,"keywords":100,"publishedAt":27},"01M6DHNEZ69TY2H6BF5QBVAKKK","generate-code-patch","minimal code changes to resolve identified issue-based skill consuming bug_analysis, code_context to produce unified_diff_patch","https:\u002F\u002Fgithub.com\u002Fmirandaguillaume\u002Fforgent\u002Fblob\u002Fmain\u002Finternal\u002Fbench\u002Ffixtures\u002Fswebench-imported\u002Fautocoderover-patch\u002Foutput-standard\u002Fskills\u002Fgenerate-code-patch\u002FSKILL.md",[99],{"categoryNo":71,"categoryName":72,"taxonomyType":73,"taxonomyTypeDesc":74},[80,101,61,78,23,62,102,26,79,103,63],"generate code patch","context","bug",{"skillNo":105,"name":106,"nameZh":6,"description":107,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":108,"categories":109,"keywords":110,"publishedAt":27},"01M6DHNBWX1RF21ZJX3EXNSF4K","analyze-bug-root-cause","systematic code analysis to identify root cause-based skill consuming issue_description, code_context to produce bug_analysis","https:\u002F\u002Fgithub.com\u002Fmirandaguillaume\u002Fforgent\u002Fblob\u002Fmain\u002Finternal\u002Fbench\u002Ffixtures\u002Fswebench-imported\u002Fautocoderover-patch\u002Foutput-standard\u002Fskills\u002Fanalyze-bug-root-cause\u002FSKILL.md",[],[111,103,90,63,112,62,23,61,102],"analyze bug root cause","systematic",{"skillNo":114,"name":115,"nameZh":6,"description":116,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":117,"categories":118,"keywords":119,"publishedAt":27},"01M6DHN9FX2CDT3WBS03T45AGC","unified-diff-patch-generator","Generate minimal unified diff patches for Python bug fixes-based skill consuming issue_analysis, python_codebase to produce unified_diff_patch","https:\u002F\u002Fgithub.com\u002Fmirandaguillaume\u002Fforgent\u002Fblob\u002Fmain\u002Finternal\u002Fbench\u002Ffixtures\u002Fswebench-imported\u002Faider-editblock\u002Foutput-standard\u002Fskills\u002Funified-diff-patch-generator\u002FSKILL.md",[],[120,80,26,79,62,23,78,121,103,63],"unified diff patch generator","python"]