Enregistrer une démonstration
Enregistre une démonstration qui ressemble à une personne utilisant l'application - chemins de curseur courbes, saisie par frappe clavier, pauses intentionnelles - plutôt qu'une machine téléportant à travers l'interface.
Les règles ci-dessous s'appliquent à chaque enregistrement, sur chaque surface. Elles sont ici plutôt que dans les références de plateforme parce que chacune a été enfreinte une fois et a coûté une prise, et parce qu'une règle conservée à côté d'un seul mécanisme est une règle que la personne suivante ne lira pas.
Choisir la surface
- L'application web - reference/web.md. Chromium headless sous Playwright avec un curseur DOM dessiné dans la page. Rien n'atteint l'écran de l'opérateur, donc privilégiez cette méthode chaque fois que la modification est visible dans un navigateur.
- L'application de bureau - reference/desktop.md. Nécessaire lorsque la démonstration nécessite le shell Electron réel : menus natifs, dialogues de fichiers, plateau (tray), fenêtres multiples, ou tout ce qui se trouve en dehors du rendu. Linux dispose d'un exécuteur intégré ; macOS enregistre une seule fenêtre par ID et n'a pas encore d'exécuteur.
Si la modification est visible sur les deux, enregistrez l'application web.
Entrées
Établissez-les avant la première prise. Lisez-les depuis le projet là où vous le pouvez (son README, les scripts package.json, le processus principal Electron) ; demandez à l'utilisateur tout ce que vous ne pouvez pas trouver plutôt que de deviner.
- Web : l'URL de l'application en cours d'exécution, passée à l'enregistreur sous
PR_RECORDER_BASE_URL(par exemplehttp://localhost:3000). L'application doit déjà être en cours d'exécution à partir du répertoire de travail qui contient la modification, y compris les modifications non validées - une nouvelle extraction de la même branche ne les aura pas, et git refuse d'extraire deux fois la même branche. Elle doit également être dans l'état requis par la démonstration. Si le flux nécessite un utilisateur connecté ou des données préremplies, c'est au projet de les fournir, pas à l'enregistreur - mais confirmez-le avant le début de la capture, sinon la prise que vous obtenez est celle d'un écran de connexion. - Bureau :
DEMO_APP_DIR- le répertoire dans lequel la commande de lancement de l'application s'exécute.DEMO_WINDOW_TITLE- le titre exact de la fenêtre principale, utilisé pour trouver à la fois la fenêtre X11 et la page de rendu.DEMO_LAUNCH_CMD- optionnel. Une commande shell qui démarre l'application avec le débogage distant sur{port}. La commande par défaut,pnpm dev --remoteDebuggingPort {port} --noSandbox, convient à electron-vite.
Où se trouve la compétence
<skill-dir> dans cette documentation signifie le répertoire à partir duquel ce SKILL.md a été chargé. Dans Claude Code, c'est ${CLAUDE_SKILL_DIR}, ce qui fonctionne aussi bien pour les installations de projet, personnelles et de plugins. Ailleurs, utilisez le chemin à partir duquel la compétence a été chargée - pour Codex, un dossier .agents/skills/record-demo dans le dépôt ou ~/.agents/skills/record-demo. Résolvez-le une fois et utilisez le chemin absolu ; les scripts ne supposent jamais où la compétence est installée.
Répéter avant la prise qui compte
Exécutez l'intégralité du flux du début à la fin avant d'enregistrer celui que vous allez livrer. Une démonstration se découvre, elle ne s'écrit pas : les noms des contrôles ne sont pas ceux que vous attendez, un bouton fait autre chose que ce qu'il indique, et une surface met plus de temps à se stabiliser que vous ne l'aviez prévu. Découvrir cela devant la caméra signifie que le spectateur vous regarde le découvrir.
La répétition est peu coûteuse et le ré-enregistrement est peu coûteux. Livrer une prise avec un mauvais virage, ou demander au relecteur d'ignorer les huit secondes du milieu, ne l'est pas.
Sonder les noms réels des contrôles d'abord
Ne jamais scripter contre un contrôle dont vous n'avez pas confirmé l'existence et qu'il fait ce que son nom suggère. Chargez l'application une fois, videz les noms accessibles et prenez une capture d'écran :
const names = await page
.getByRole("button")
.evaluateAll((els) =>
els.map((e) => (e.getAttribute("aria-label") || e.textContent || "").trim()).filter(Boolean),
);
console.log(JSON.stringify(names));
await page.screenshot({ path: "/tmp/probe.png" });
Ensuite, lisez la capture d'écran. Le vidage vous dit qu'un nom existe ; seule l'image vous dit ce qu'est le contrôle. Quatre façons dont cela pose problème :
- Un nom qui n'est pas celui que vous auriez deviné. Le nom accessible d'un sélecteur était celui de l'élément actuellement sélectionné, pas "Sélectionner le projet".
- Un nom qui ne correspond pas à ce qu'il semble. Un bouton
Fichiersdans une barre latérale était un basculement de section, donc cliquer dessus a replié la section au lieu de naviguer ailleurs. Cela a été livré dans une prise. - Un nom qui fait partie d'un autre nom.
getByLabel("Premier message")a également correspond à une option proche dont l'étiquette contenait "premier message", donc la saisie n'a jamais atteint le champ et rien n'a échoué. Passezexact: true, ou faites correspondre également par rôle. - Quelque chose qui ressemble à du texte mais n'en est pas. Un titre de sélecteur lu sur une capture d'écran était un espace réservé d'une entrée, donc
getByTextn'a jamais pu correspondre. Confirmez qu'un localisateur se résout avant de scripter contre lui.
Lorsque plusieurs éléments partagent un nom - un élément apparaît dans la barre latérale, l'en-tête de la page et le fil d'Ariane - limitez avec .first().
Pilotage comme le ferait une personne
Trouvez les contrôles par rôle visible ou texte (getByRole, getByText, getByLabel, getByPlaceholder). Jamais de sélecteurs CSS, jamais d'attributs réservés aux tests, et n'ajoutez jamais de crochet à l'application pour qu'un enregistrement puisse trouver quelque chose. Les scripts de démonstration sont jetables et par PR ; gardez-les en dehors du dépôt.
Ne modifiez jamais l'application, la compétence ou le code de production pour une démonstration. Si la démonstration nécessite que l'application soit différente, la démonstration est erronée.
N'attendez jamais sur l'horloge
waitForTimeout(9000) pour "laisser l'éditeur se charger" est la seule plus grande cause de démonstrations regardables mais ennuyeuses. Cela paie toujours le pire cas, et chacune de ces secondes est dans le fichier. Attendez la chose que vous attendez réellement - le premier contrôle avec lequel vous allez interagir - afin que la démonstration continue dès que l'application est prête et échoue de manière bruyante, en nommant le localisateur, au lieu de piloter une page à moitié construite.
Les pauses fixes sont uniquement pour le rythme : un temps mort après une action pour qu'un spectateur puisse assimiler ce qui a changé. Des secondes, pas des dizaines de secondes, et jamais en remplacement d'une attente.
Un fichier écrit n'est pas une preuve
Les enregistreurs vident un fichier partiel lorsque le script lève une exception, et certains outils de capture écrivent à la sortie quoi qu'il se soit passé. Donc le fait que le fichier existe ne signifie rien en soi. Avant de croire une prise :
- Vérifiez le statut de sortie et stderr, et lisez l'erreur s'il y en a une - elle nomme généralement le localisateur qui n'a rien trouvé.
- Vérifiez la présence d'une capture obsolète occupant déjà le chemin de sortie. Une capture laissée en cours d'exécution depuis une tentative antérieure possède toujours le nom de fichier et produira silencieusement un enregistrement de la mauvaise chose.
- Vérifiez les images. Extrayez une planche contact, la dernière image, et toute transition dont vous n'êtes pas sûr, et regardez-les.
Vérifiez la démonstration, pas le script
Une sortie propre signifie que le script s'est exécuté, pas que la fonctionnalité a fonctionné. Confirmez les deux :
- Visuel - la modification est à l'écran dans les images, dans l'état demandé par la requête.
- Fonctionnel - l'état persistant a effectivement changé, lu depuis la base de données ou l'API plutôt qu'inféré depuis l'interface.
Lorsque la revendication concerne la mise en page - deux volets côte à côte, un panneau ancré à un bord - affirmez-la à partir des boîtes englobantes. Le fait que des éléments existent ne dit rien sur l'endroit où ils se trouvent.
Maintenez l'état final pendant environ trois secondes pour que le spectateur s'y pose, puis vérifiez le codec, les dimensions et la durée du fichier de sortie avec ffprobe avant de le remettre.
Conservez les preuves
Conservez les journaux d'échec, les prises partielles et les captures d'écran jusqu'à ce qu'une prise ait réussi. Supprimez l'état jetable seulement après avoir obtenu quelque chose que vous pouvez livrer. Un intermédiaire écarté est souvent le seul enregistrement de la raison pour laquelle la prise a échoué.
Ne dérangez pas la machine sur laquelle vous enregistrez
Un enregistrement local s'exécute sur l'ordinateur réel d'une personne pendant qu'elle l'utilise.
- Donnez à l'application son propre profil jetable et ne touchez jamais au profil réel de l'opérateur.
- Préparez tout ce dont la démonstration a besoin avant le premier lancement, sinon les écrans de première utilisation apparaîtront devant eux.
- Possédez chaque processus que vous démarrez et arrêtez-le lorsque vous avez terminé - en particulier les captures, qui continuent de s'exécuter après le retour du script.
- Démarrez les processus de longue durée en tant que tâches d'arrière-plan dont vous détenez un gestionnaire, pas avec un
&en fin de ligne à l'intérieur d'un wrapper de premier plan : lorsque le wrapper se termine, il emporte le serveur avec lui. - Arrêtez un processus comme il s'attend à être arrêté. Un serveur de développement tué avec SIGKILL a sauté son piège de nettoyage et a laissé derrière lui un fichier d'environnement réécrit et un fichier de port obsolète. Signalez le processus que vous avez démarré et laissez-le se nettoyer. Ne tuez jamais par motif (
pkill -f next) : sur une machine partagée, cela tue aussi les serveurs des autres. - Vérifiez avec quel backend l'application sous démonstration communique. Un lanceur de développement par défaut utilisait une API de production sauf indication contraire, donc les clics d'une démonstration auraient écrit des données réelles. Pointez-le vers un backend local ou de développement avant la première prise.
- Vérifiez tout ce dont la démonstration dépend - un outil CLI qui doit être connecté, un service qui doit être opérationnel - avant de prendre un affichage ou de démarrer la capture, afin qu'une dépendance manquante échoue en une seconde plutôt qu'en milieu de prise. N'effectuez jamais une action avec effet de bord, comme envoyer un message, juste pour prouver qu'un outil a été lancé.
- Si une prise nécessite leur écran, leur gestionnaire de fenêtres ou leur entrée, dites-le et demandez, plutôt que de le prendre.
Mettre le fichier sur une PR
gh télécharge la vidéo elle-même, depuis la v2.99.0 :
gh pr comment <number> --body-file ./note.md --attach ./demo.mp4
--attach fonctionne aussi sur gh pr create, gh pr edit, et les équivalents gh issue. Il est répétable, jusqu'à 50 fichiers par commande, et télécharge vers le CDN user-attachments pour que GitHub affiche la vidéo en ligne.
Quatre choses à savoir :
- Le texte alternatif est réservé aux images.
'./shot.png#L'état d'erreur'définit le texte alternatif sur une image ; la même syntaxe sur une vidéo est refusée aveccannot set alt text on video. Attachez les vidéos avec un chemin nu. - Un corps qui référence déjà le chemin local est réécrit. Écrivez
là où vous le souhaitez etghremplace l'URL téléchargée ; sinon, la pièce jointe est ajoutée à la fin. gh pr edit --attachsans indicateur de corps conserve le corps existant et ajoute à la fin, de sorte qu'une démonstration peut être ajoutée à la description d'une PR après coup.- Vérifiez la version avant de vous en remettre à elle. Sur
ghantérieur à 2.99.0, il n'y a pas--attach, et le CDN n'est accessible d'aucune autre manière via l'API - donc repliez-vous en donnant à la personne le chemin du fichier à glisser-déposer dans la PR elle-même.