Un rapport affiche quatre lignes alors que son fichier de configuration indique limit: 6, et on vous demande pourquoi. Vous découvrez ce dépôt. Il contient une CLI, un chargeur de configuration, quelques services et un dossier de tests. Tout lire finirait par vous donner la réponse. Suivre la valeur qui détermine le nombre de lignes est bien plus rapide.

Préciser la question

« Comprendre le module de rapports » n’a pas de limite claire. Une question précise, si :

La configuration indique limit: 6 et l’environnement contient REPORT_LIMIT=4. Le rapport affiche quatre lignes. Quelle valeur a pris le dessus, et que se passe-t-il si REPORT_LIMIT est une chaîne vide ?

Notez la commande exécutée, le répertoire et le commit. Vérifiez aussi que l’entrée contient plus de quatre enregistrements. Si la source n’a que quatre lignes, en obtenir quatre ne prouve rien sur la limite.

Trouver un premier fichier à partir de votre indice

Le premier fichier utile peut être un lanceur, un formateur ou un test, sans être la fonction qui choisit la limite. Partez de l’indice que vous pouvez reproduire pour atteindre cette fonction, et notez les liens réellement vérifiés.

Téléchargez le laboratoire sur le point d’entrée du rapport, décompressez-le et entrez dans report-entry-lab. Ce petit projet a été créé pour ce tutoriel. Il ajoute une vraie commande, une table de dispatch et des chargeurs de fixtures locales aux règles de priorité présentées ci-dessous. Il faut Node.js 22 ou ultérieur, npm et ripgrep (rg), sans installation de dépendances ni compte de modèle. Les commandes shell fonctionnent sous macOS et Linux.

REPORT_LIMIT=4 npm run --silent report
Report rows: 4
Aster
Birch
Clover
Dahlia

La fixture de configuration contient limit: 6 ; celle des données a huit lignes. La première ligne de sortie est un en-tête, suivi de quatre lignes de données. L’écart est donc réel et ne vient pas d’un fichier d’entrée trop court. Le programme lit uniquement les fixtures locales incluses et écrit dans le terminal ; ses tests lancent des processus Node locaux, sans requête réseau ni écriture de fichier.

Votre indice Point de départ Ce qui reste à vérifier
Une commande reproduit le comportement Le script du paquet ou l’exécutable appelé Quel argument sélectionne quel gestionnaire
Un texte de sortie ou d’erreur distinctif Le code qui le construit Si le résultat est un producteur, une fixture ou une assertion, et qui appelle le producteur
Un test identifié Son import et l’appel dans le test S’il lance la vraie commande ou fournit des dépendances de remplacement
Une clé de configuration ou une variable d’environnement La lecture et la conversion de cette valeur Quel appelant y mène et quel chargeur fournit la configuration

Lire le même laboratoire dans Yenpo

Utilisez le dossier report-entry-lab téléchargé pour répondre à « Quel point d’entrée a produit ces quatre lignes ? ». La lecture doit aboutir à une courte note indiquant la commande de lancement, le gestionnaire choisi, les fichiers d’entrée et l’appel de sortie.

  1. Ouvrez le dossier extrait report-entry-lab dans Yenpo en choisissant la racine contenant package.json. Utilisez Command-P (Aller au fichier…) pour ouvrir package.json, puis src/cli.mjs et src/commands.mjs. Vérifiez le script de lancement et le gestionnaire enregistré pour report.
  2. Dans la barre latérale, utilisez Rechercher dans tous les fichiers pour chercher Report rows: en désactivant Regex. Lisez src/format-report.mjs avec les résultats dans les tests : l’un construit le texte, les autres vérifient un résultat attendu. Suivez l’import et l’appel du formateur dans src/report.mjs avant de le rattacher à cette commande.
  3. Gardez src/report.mjs, src/limits.mjs et le test concerné ouverts dans des onglets. Notez le fichier et la ligne qui étayent chaque étape, puis utilisez les vérifications Terminal ci-dessous pour distinguer un test direct de fonction d’une exécution par la vraie CLI.

La navigation dans les fichiers et la recherche textuelle ne nécessitent ni connexion à un modèle ni serveur de langage JavaScript. Pour essayer, téléchargez Yenpo pour Mac et ouvrez le même dossier extrait ; l’aide à la lecture du code explique les deux outils de recherche. Le ZIP contient des sources, sans historique Git : cet exercice porte donc sur les fichiers, pas sur une comparaison de commits figée.

Vous avez la commande du terminal

Inspectez le script avant de déduire le point d’entrée du nom d’un dossier :

npm pkg get scripts.report
rg -n 'process.argv|commands\[command\]' src/cli.mjs
rg -n 'report:|runReport|report-config.json' src/commands.mjs

La première commande affiche "node src/cli.mjs report". Dans cli.mjs, process.argv.slice(2) fait de report le nom de commande ; les arguments restants vont au gestionnaire choisi. commands[command] est une recherche dans une table : trouver le lanceur ne suffit donc pas. Ouvrez commands.mjs : sa propriété report appelle runReport avec des chargeurs pour report-config.json et rows.json. La propriété voisine help renvoie plutôt le texte d’aide.

Effectuez maintenant une modification contrôlée via le même lanceur :

REPORT_LIMIT=4 npm run --silent report -- --limit 2

Cela affiche Report rows: 2, puis Aster et Birch. npm transmet au script les arguments placés après le séparateur à deux tirets. Lisez la recherche dans la table et le gestionnaire enregistré pour établir le chemin ; la sortie modifiée vérifie ensuite que cette commande atteint le comportement de limitation. La sortie seule ne permettrait pas d’identifier tous les appels intermédiaires.

Vous n’avez qu’un texte de sortie

rg -n -F 'Report rows:' src tests
rg -n 'renderReport|runReport' src

La première recherche trouve format-report.mjs et des assertions de tests. Le formateur construit l’en-tête ; les tests comparent le texte attendu. Aucun de ces résultats n’est automatiquement un point d’entrée de commande. Dans report.mjs, vérifiez l’import de renderReport et l’appel qui lui transmet les noms choisis. Remontez ensuite de runReport à l’enregistrement report dans commands.mjs, puis du gestionnaire choisi à l’écriture sur stdout dans cli.mjs.

Vous obtenez ainsi un parcours à vérifier avec la commande ci-dessus. Si la sortie réelle est assemblée en plusieurs morceaux, cherchez un fragment stable puis examinez la construction de l’expression autour. Si un formateur partagé a plusieurs appelants, gardez-les comme candidats jusqu’à ce que les arguments de lancement ou un test ciblé en désignent un ; ne les fusionnez pas en un seul chemin d’exécution.

Vous avez un nom de test

rg -n -F 'environment wins over config' tests
node --test --test-name-pattern='environment wins over config' tests/report.test.mjs

Le test trouvé importe directement runReport et fournit son propre environnement ainsi que ses chargeurs de configuration et de lignes. Il confirme que quatre l’emporte sur six dans cette fonction, mais ne lance jamais cli.mjs et ne lit aucun fichier de configuration. Il ne dit pas quel fichier une vraie commande charge.

Suivez l’import pour trouver le comportement, puis inspectez la liaison réelle commands.report. Ce test distinct franchit la frontière manquante :

node --test --test-name-pattern='CLI uses REPORT_LIMIT' tests/cli.test.mjs

Il lance la vraie CLI dans un processus enfant avec REPORT_LIMIT=4 et vérifie son code de sortie et stdout. Les deux tests sélectionnés passent. Conservez cette distinction dans vos notes : l’un choisit ses dépendances, l’autre exerce le lancement et les adaptateurs de fichiers. La réussite d’un test direct de fonction ne remplace pas le second.

Vous avez le nom de la configuration

rg -n -F 'REPORT_LIMIT' src tests
rg -n 'resolveLimit|loadConfig|report-config.json' src

La lecture de la valeur se trouve dans limits.mjs ; les autres résultats préparent des entrées de test. Suivez l’appel de resolveLimit dans report.mjs, puis vérifiez la fonction loadConfig fournie par commands.report. Son new URL(..., import.meta.url) choisit la fixture incluse relativement au module. Le nom du fichier à lui seul ne prouve pas que ce gestionnaire l’utilise.

Vous avez maintenant le point de départ nécessaire pour suivre la valeur ci-dessous. Laissez de côté help et les tests sans rapport : vous avez vérifié la commande, le gestionnaire choisi, les vrais fichiers d’entrée et le point de sortie. Si la commande réelle utilise un autre checkout, un wrapper ou une autre source d’environnement, résolvez cet écart avant d’élargir la recherche. Sinon, la prochaine question porte sur la priorité de la valeur, pas sur le reste du dépôt.

Suivre le gestionnaire choisi

Le fichier report.mjs du laboratoire analyse les arguments, charge la configuration, choisit une limite, charge les lignes, puis appelle renderReport pour ajouter l’en-tête. La CLI écrit la chaîne renvoyée. L’exemple autonome antérieur ci-dessous ne conserve que les lignes de données, pour examiner la valeur sans le raccordement de la commande. Les chargeurs sont des arguments dans les deux versions, ce qui permet aux tests d’observer l’ordre sans lire de fichiers :

export async function runReport({ args, env, loadConfig, loadRows }) {
  const { values } = parseArgs({
    args,
    options: { limit: { type: 'string' } },
    strict: true,
  });
  const config = await loadConfig();
  const limit = resolveLimit(values, env, config);
  const rows = await loadRows();
  return rows
    .slice(0, limit)
    .map((row) => row.name)
    .join('\n');
}

Avant même d’ouvrir une fonction auxiliaire, celle-ci donne déjà l’ordre : analyser les arguments, charger la configuration, choisir la limite, charger les lignes, afficher. Elle révèle aussi un détail facile à manquer : resolveLimit() ne s’exécute que si loadConfig() réussit. Cela comptera plus loin.

Dans une vraie base de code, suivez les imports. Une fonction nommée loadConfig peut lire un fichier JSON, en fusionner plusieurs ou appeler un service distant. Son nom ne permet pas de trancher.

Comment la limite est choisie

export function resolveLimit(cli, env, config) {
  const raw = cli.limit ?? env.REPORT_LIMIT ?? config.limit ?? 10;
  const limit = Number(raw);
  if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100) {
    throw new Error('limit must be an integer from 1 to 100');
  }
  return limit;
}

Trois opérations ont lieu : choisir une source, convertir en nombre, vérifier la plage. Séparez-les dans vos notes. Écrire seulement « l’environnement prime sur la configuration » peut faire croire qu’une valeur d’environnement invalide déclenche un repli vers le fichier. Ce code ne le fait jamais.

CLI --limit REPORT_LIMIT Configuration limit Résultat
2 4 6 2, depuis la ligne de commande
absent 4 6 4, depuis l’environnement
absent absent 6 6, depuis le fichier
absent absent absent valeur par défaut 10
absent chaîne vide 6 erreur de validation
0 4 6 erreur de validation

?? ignore seulement null et undefined. Une chaîne vide reste une valeur, donc REPORT_LIMIT='' l’emporte. Number('') vaut 0, la vérification de plage lève une erreur et la fonction ne consulte jamais la valeur du fichier de configuration.

Remplacer ?? par || laisserait passer les chaînes vides, mais traiterait aussi 0 comme « non défini ». Cela change les règles ; ce n’est pas un simple nettoyage. Décidez d’abord du comportement voulu : une mauvaise valeur définie volontairement doit-elle produire une erreur ou déclencher discrètement un repli ?

Suivre la valeur jusqu’à son utilisation

Une limite correcte ne garantit pas une sortie correcte. Suivez limit jusqu’à rows.slice(0, limit), puis regardez d’où vient rows.

Ici, loadRows() renvoie toutes les lignes et le gestionnaire garde les premières. La source n’effectue ni tri, ni filtre, ni pagination. La limite plafonne donc les lignes affichées, pas la quantité de données lues ou conservées en mémoire.

Cela compte si le problème signalé est différent. Si l’on dit « le rapport épuise la mémoire même avec --limit 2 », les règles de priorité ne sont pas le bon endroit. Il faut examiner loadRows et vérifier si la limite doit parvenir jusqu’à la source de données. Même gestionnaire, autre question, autre fichier à lire ensuite.

Provoquer une panne volontairement

Essayez maintenant un fichier de configuration invalide avec --limit 2. La ligne de commande a la priorité la plus haute : la commande devrait donc fonctionner quand même, non ?

Non. La configuration est chargée avant le choix de la limite ; si loadConfig() lève une erreur, rien ne s’exécute ensuite. La priorité détermine la valeur utilisée. Elle ne dit rien sur ce qui doit réussir avant ce choix.

Le cas inverse mérite aussi un test : la configuration se charge, mais la limite choisie est invalide. Les lignes ne doivent alors jamais être lues. Comptez les lectures pour le vérifier :

let reads = 0;
await assert.rejects(
  runReport({
    args: [],
    env: { REPORT_LIMIT: '' },
    loadConfig: async () => ({ limit: 6 }),
    loadRows: async () => {
      reads++;
      return [];
    },
  }),
  /integer/,
);
assert.equal(reads, 0);

Si vous vérifiiez seulement qu’une erreur est levée, une version qui lit toutes les lignes avant de rejeter la limite passerait aussi. C’est reads === 0 qui fixe l’ordre.

Exécuter l’exemple

Téléchargez configuration-flow.mjs et exécutez-le avec Node.js 22 ou ultérieur. Il n’a aucune dépendance :

node --test configuration-flow.mjs

Ce téléchargement original en un seul fichier reste disponible. Ses six tests couvrent l’ordre de priorité, les valeurs vides et invalides, la sortie affichée, l’absence de lecture des lignes après une entrée incorrecte et la configuration invalide. Ils exercent les fonctions isolément ; dans votre projet, il faut encore vérifier comment la vraie CLI charge son environnement et affiche les erreurs.

Le nouveau laboratoire couvre aussi le raccordement de la commande. Depuis son dossier, exécutez :

node --test tests/*.test.mjs
REPORT_LIMIT='' npm run --silent report

Les neuf tests passent. La deuxième commande se termine volontairement avec le code 1, écrit limit must be an integer from 1 to 100 sur stderr et ne produit rien sur stdout. Le test unitaire compte aussi les lectures de lignes : le cas de la valeur vide vérifie donc l’ordre décrit plus haut. Le ZIP contient un manifeste SHA-256 ; l’archive et les tests extraits ont été vérifiés.

Noter ce que vous avez trouvé

Une courte note suffit, et se relira bien plus vite que le code la prochaine fois :

runReport
  parseArgs → loadConfig → resolveLimit → loadRows → slice → output

limit: command line > environment > config > 10
invalid selected value: error; no fallback; rows are not loaded
config read failure: stops even when --limit is present
scope: limit bounds output, not the row loader's work

Dans une vraie base de code, ajoutez le fichier et la ligne à chaque étape et listez les points non vérifiés : quel fichier lit loadConfig, si un processus parent définit REPORT_LIMIT, comment la CLI présente les erreurs. Dès que vous pouvez expliquer les quatre lignes et nommer ces questions ouvertes, vous pouvez vous arrêter. Il n’est pas encore nécessaire de comprendre le reste du projet.

Faire vérifier la transmission par une autre personne

Une note de lecture est plus utile si quelqu’un peut la reproduire sans l’historique de votre conversation. Essayez le ZIP de transmission poster-desk et sa courte fiche de transmission. Ce petit projet hors ligne et ses affirmations ont été créés à des fins pédagogiques ; il ne s’agit ni d’une application réellement générée par IA ni d’une vraie transmission.

La tâche de réception consiste à vérifier quatre promesses sur un instantané fixe des sources : les cartes locales sont lisibles par un nouveau processus, les répétitions séquentielles identiques réutilisent un enregistrement, les reçus ne sont que des aperçus de fixtures et la conservation lors du remplacement d’un déploiement reste inconnue. « Implémenté » couvre les conditions testées ; les répétitions séquentielles ne prouvent pas la sûreté en concurrence, le mock n’envoie rien et le paquet ne fournit aucune preuve sur le stockage du déploiement.

Suivez la fiche, copiez RECEIVER.md hors du paquet extrait, puis renseignez cette copie avec vos commandes, résultats, emplacements dans le code et questions ouvertes. Gardez le modèle livré intact, puis vérifiez à nouveau le paquet original.

SHA-256 du ZIP
c39d19b210c05f4667cd74db4934459a079408d14c5e7678c24cd087e1df5185

Dans Yenpo, Aller au fichier… et la recherche dans le projet permettent de garder le gestionnaire, le résolveur et les tests ouverts côte à côte. Avec le lecteur de code IA, posez une question sur une seule valeur : « Suis REPORT_LIMIT depuis sa lecture jusqu’à son utilisation, avec les références de lignes. » Vous obtiendrez un chemin vérifiable étape par étape plutôt qu’un autre résumé du projet.

Choisissez la prochaine tâche de lecture selon les éléments dont vous disposez :