Articles

Linter des spécifications en CI avec un linter de spécifications

Un linter de spécifications est un programme qui vérifie une spécification à la recherche d’erreurs de structure et se termine par un code d’échec lorsqu’il en trouve. L’intégration continue (CI, continuous integration) est un contrôle automatique à chaque changement proposé. L’intégration continue et la livraison continue (CI/CD, continuous integration and continuous delivery) est le pipeline qui exécute ce contrôle et garde livrable un changement qui passe. Le développement piloté par les spécifications (SDD, spec-driven development) est la pratique d’écrire la spécification avant le code et de traiter la spécification comme la source de vérité. Un document d’exigences produit (PRD, product requirements document) est la prose qui énonce le problème et le périmètre. Dans SpecScore, une exigence (REQ) est une règle nommée dans une fonctionnalité, et un critère d’acceptation (AC, acceptance criterion) est une condition abstraite facultative qui pointe vers une ou plusieurs de ces règles. Le contrôle est une commande de l’interface en ligne de commande (CLI, command-line interface), specscore spec lint, de la CLI dans specscore/specscore-cli (Apache-2.0). Easy Approach to Requirements Syntax (EARS) est un patron de phrase pour une exigence. Ce linter vérifie les titres, les sections obligatoires et les références. Il ne note pas l’anglais.

Dernière mise à jour : 4 novembre 2026

Ce que signifie linter une spécification

Linter une spécification est le même type de contrôle que linter du code. Un programme déterministe lit les fichiers et signale les violations de règles. Il ne décide pas si le produit est une bonne idée, et il ne réécrit pas l’exigence en une meilleure prose. La page des validateurs sépare ce contrôle structurel d’une revue manuelle par IA. specscore spec lint est le contrôle structurel.

La description de la commande, dans internal/cli/spec.go à l’étiquette v0.55.1 de la CLI et à l’étiquette v0.39.1 qu’installe le workflow de ce dépôt, dit qu’elle valide l’arbre de spécifications pour des violations de conventions structurelles. Une exécution propre affiche 0 violations found et se termine avec 0. Une exécution qui trouve des violations affiche une ligne par constat et se termine avec 1. Ce code de sortie est ce qui fait échouer un job de CI. Les arguments invalides se terminent avec 2. Une specscore.yaml absente se termine avec 3, et le message indique d’exécuter specscore init.

Ce que vérifie le linter de spécifications

Les règles sont celles de la CLI, pas d’un service cloud séparé. Le catalogue généré docs/lint-rules.md à l’étiquette v0.55.1 liste 183 règles. Le workflow de ce site installe v0.39.1, une version plus ancienne, donc une règle qui n’existe que dans une étiquette ultérieure n’est pas ce que ce job exécute. Les règles nommées dans cette section sont dans les deux étiquettes. Le --severity par défaut est error. Un avertissement n’est affiché que si vous passez --severity warning ou info, et la commande par défaut n’échoue pas sur un avertissement. Le catalogue marque dogfood-version-bump comme avertissement, donc la commande par défaut n’échoue pas lorsque l’épingle du workflow est en retard sur le binaire.

Fichiers et sections obligatoires

La règle readme-exists (readme_exists.go) exige un README.md dans chaque répertoire de spécification. La règle oq-section (oq_section.go) exige que le README d’une fonctionnalité contienne une section ## Open Questions. Un titre hérité ## Outstanding Questions est un constat distinct, et --fix réécrit ce titre sur place. Le message de l’exemple ci-dessous, «Open Questions section not found», est le constat d’un README de fonctionnalité dont la section a été retirée.

Lignes d’index et répertoires enfants

La règle index-entries (index_entries.go) compare un index de fonctionnalités aux répertoires enfants sur le disque. Un enfant qui n’est pas listé est une violation, et une ligne dont le lien pointe vers un enfant qui n’est pas là est une violation. --fix peut supprimer la ligne fantôme et insérer celle qui manque. C’est l’intégrité du répertoire et de l’index. Ce n’est pas un parcours de chaque URL dans la prose.

Forme du titre de critère d’acceptation

La règle ac-heading-format (ac_heading.go) s’applique au README d’une fonctionnalité, un fichier dont le titre est # Feature:. Une ligne qui ressemble à un titre de critère d’acceptation doit être exactement ### AC: <id> avec un espace après le deux-points et un id en kebab-case. Un titre de la forme ### AC: <id> (verifies ...) est aussi accepté. --fix ne corrige que l’espacement. Un id avec des majuscules ou des tirets bas est signalé et laissé à une personne. La règle n’exige pas la ligne **Requirements:**, ne note pas des mots comme «graceful» ou «fast», et n’avertit pas lorsque la prose contient Given, When ou Then. Comment écrire le critère lui-même est dans comment écrire des critères d’acceptation.

Références du plan vers ces identifiants

Dans un plan qui nomme une fonctionnalité source, plan_rules.go lit les titres ### AC: du README de cette fonctionnalité. La règle P-002 signale une valeur **Verifies:** de tâche qui ne se résout pas en <feature-slug>#ac:<ac-slug> pour l’un de ces titres, une ligne **Verifies:** vide, une ligne de source absente ou non reconnue, ou une fonctionnalité source qui ne se résout pas en un README de fonctionnalité. La règle P-001 signale un titre qu’aucune tâche ne couvre et qui n’est pas listé sous ## Deferred AC Coverage. Les plans dont la source est une idée (**Source:** idea:<slug>) et les plans avec **Source:** none sont exemptés, de même qu’un plan dans un état de disposition terminal. Le texte de catalogue sur une ligne pour P-002 dit «valid dependency references» ; la vérification dans ce fichier est le contrôle des références AC qui vient d’être décrit.

Ce qu’un lint réussi n’est pas

La même commande ne décide pas si une exigence résout le problème énoncé, si un nom a du sens, ou si deux fonctionnalités se contredisent dans le sens. La page des validateurs les place en dehors du linter de la CLI. Elle ne vérifie pas non plus les mots-clés des étapes de scénario. specscore rehearse exécute les fichiers de scénario. Le workflow de ce dépôt exécute specscore spec lint et n’appelle pas rehearse. Le catalogue nomme d’autres familles — idées, décisions, entités, propriétés, leçons, issues et le reste des règles de plan — et chaque règle s’applique aux fichiers qu’elle possède. Le catalogue est la liste. Cette page ne répète pas les 183 descriptions.

Exécuter specscore spec lint en CI

La commande cherche vers le haut depuis le répertoire de travail un specscore.yaml. Sans ce fichier, elle se termine avec 3 et nomme specscore init. Un job de CI a donc besoin du fichier de configuration dans le checkout. scripts/install.sh lit SPECSCORE_VERSION et prend latest par défaut lorsque la variable n’est pas définie. Fixez une étiquette si vous voulez les mêmes règles à chaque exécution.

Le contrôle de ce dépôt est dogfood.yml. Il installe la CLI avec SPECSCORE_VERSION fixé à v0.39.1 puis exécute specscore spec lint sans options supplémentaires, sur les pull requests et sur les push vers main. GitHub Actions fait échouer l’étape lorsque ce processus se termine avec un code non nul. L’extrait ci-dessous conserve ces étapes d’installation et de lint. Le fichier définit aussi permissions: contents: read, un groupe de concurrence, et un commentaire sur la raison pour laquelle l’épingle n’est pas latest.

Étapes d’installation et de lint de dogfood.yml
name: Dogfood lint

on:
  push:
    branches: [main]
  pull_request:

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
      - name: Install specscore CLI (pinned)
        env:
          SPECSCORE_VERSION: v0.39.1
        run: |
          curl -fsSL https://specscore.md/install/get-cli | \
            SPECSCORE_INSTALL_DIR="$HOME/.local/bin" sh
          echo "$HOME/.local/bin" >> "$GITHUB_PATH"
          specscore --version
      - name: Lint spec tree
        run: specscore spec lint
Rapport d’échec (specscore 0.55.0, même forme de ligne en v0.39.1)
features/apply-discount/README.md:0 [error] oq-section: Open Questions section not found
features/apply-discount/README.md:0 [error] index-entries: Child directory not listed in index: refunds
features/apply-discount/refunds:0 [error] readme-exists: README.md not found

3 violations found (3 errors)
Rapport propre
0 violations found

Chaque constat est file:line [severity] rule: message. Le résumé est N violations found plus un compte entre parenthèses, comme (3 errors). Le code de sortie est le statut du processus, pas une ligne du rapport. Les trois lignes ci-dessus ont été capturées avec specscore 0.55.0 sur une fonctionnalité dont la section Open Questions avait été retirée et dont l’index omettait un répertoire enfant vide. C’est le même rapport que montre la page de la CLI. --format accepte text (la valeur par défaut), json ou yaml. --rules et --ignore sélectionnent des règles et ne peuvent pas être combinés. --fix réécrit les constats qui ont un correcteur, y compris le titre Outstanding Questions. Le job dogfood ne passe pas --fix.

Outils qui ne lintent pas ce format en CI

Un contrôle de qualité des exigences, dans cette CLI, est le contrôle structurel ci-dessus. Ce n’est pas une note de la prose. D’autres outils de développement piloté par les spécifications publient des contrôles différents. Les paragraphes ci-dessous n’utilisent que ce que disent les pages de ces projets. La revue des outils enregistre les mêmes pages comme lues le 30 octobre 2026, sauf l’accueil de la documentation de Spec Kit, qui imprime le 28 septembre 2026, et la page de spécifications de fonctionnalité de Kiro, qui imprime le 4 août 2026.

GitHub Spec Kit est un workflow d’agent qui écrit des artefacts Markdown. La référence de SDD agentique décrit /speckit.analyze comme un rapport d’incohérences entre la spécification, le plan et les tâches, sans modifier les fichiers. On l’exécute dans l’agent. L’accueil de la documentation nomme aussi des extensions de la communauté, dont une appelée CI Guard, qui ajoutent des portes de conformité. Ce n’est pas une commande qui linte un README de fonctionnalité SpecScore.

OpenSpec publie bien un contrôle structurel. openspec validate, documenté dans la référence de la CLI, vérifie la structure de changement et de spécification d’OpenSpec, se termine avec un code non nul lorsque la validation échoue, et la référence inclut un extrait shell qu’un job de CI peut exécuter. Ce contrôle est la forme d’OpenSpec, pas les titres ### AC: ni un index SpecScore.

La page de spécifications de fonctionnalité de Kiro décrit Analyze Requirements comme un contrôle à l’intérieur de Kiro, avant la conception, qui cherche des incohérences, de l’ambiguïté, des contraintes en conflit et des lacunes. Ce n’est pas la commande de dogfood.yml. Les patrons de phrase que Kiro écrit sont dans la syntaxe des exigences EARS.

Le README de BMAD Method décrit les artefacts de la méthode et les workflows d’agents. Il ne documente pas une commande qui linte un schéma de spécification fixe en CI. Le README de Task Master décrit la transformation d’un PRD en tâches avec task-master parse-prd. Il ne décrit pas un linter pour ce PRD. Un PRD peut toujours être l’entrée d’une fonctionnalité SpecScore, et specscore spec lint est ce qui vérifie alors le fichier de la fonctionnalité.

Questions fréquentes

Qu’est-ce qu’un linter de spécifications ?

Un linter de spécifications lit des fichiers de spécification et signale des violations structurelles. specscore spec lint est cette commande pour un arbre SpecScore. Il se termine avec 0 lorsqu’il ne trouve rien à la sévérité choisie, et avec 1 lorsqu’il trouve des violations.

Le linter de spécifications juge-t-il la qualité des exigences dans la formulation ?

Non. ac-heading-format vérifie le titre ### AC:. Il ne note pas des mots comme «graceful» ou «fast», et il n’avertit pas lorsqu’un critère contient Given, When ou Then. La formulation est un travail d’écriture. La page des validateurs sépare ce lint déterministe de la revue manuelle par IA.

Comment linter des spécifications en CI ?

Installez la CLI avec SPECSCORE_VERSION fixé à une étiquette de version, puis exécutez specscore spec lint. Le checkout a besoin d’un specscore.yaml. dogfood.yml est un exemple GitHub Actions qui fonctionne. Il épingle v0.39.1.

Qu’affiche une exécution qui échoue ?

Une ligne par constat, file:line [severity] rule: message, puis un résumé tel que 3 violations found (3 errors). Le processus se termine avec 1. Un arbre propre affiche 0 violations found et se termine avec 0.

D’autres outils de développement piloté par les spécifications valident-ils les spécifications en CI/CD ?

Le openspec validate d’OpenSpec peut faire échouer un job de CI, et il vérifie la structure de changement propre à OpenSpec. Le /speckit.analyze de Spec Kit s’exécute dans la session de l’agent. Analyze Requirements de Kiro s’exécute à l’intérieur de Kiro. Les README de BMAD Method et de Task Master ne documentent pas un linter de format pour la CI. Le workflow dogfood de SpecScore exécute specscore spec lint sur l’arbre SpecScore.

Lintez la spécification en CI.

Have an idea how to improve this page?