Un informe imprime cuatro filas, pero su archivo de configuración indica limit: 6, y te han pedido que averigües por qué. Nunca habías visto este repositorio. Tiene una CLI, un cargador de configuración, varios servicios y una carpeta de pruebas. Leerlo todo te llevaría a la respuesta con el tiempo. Seguir el valor que determina el número de filas es mucho más rápido.

Concreta la pregunta

«Entender el módulo de informes» no tiene un punto final. Una pregunta con una respuesta concreta sí:

La configuración indica limit: 6 y el entorno tiene REPORT_LIMIT=4. El informe imprime cuatro filas. ¿Qué valor prevaleció y qué ocurre si REPORT_LIMIT es una cadena vacía?

Anota el comando que ejecutaste, el directorio y el commit. Comprueba también que la entrada tenga más de cuatro registros. Si los datos de origen solo tienen cuatro filas, recibir cuatro filas no demuestra nada sobre el límite.

Encuentra el archivo inicial a partir de la pista disponible

El primer archivo útil puede ser un lanzador, un formateador o una prueba, en lugar de la función que elige el límite. Usa una pista reproducible para llegar a esa función y registra qué conexiones has comprobado realmente.

Descarga el laboratorio de entrada del informe, descomprímelo y entra en report-entry-lab. Este pequeño proyecto se creó para este tutorial. Añade un comando real, una tabla de despacho y cargadores de datos de prueba locales a las mismas reglas de prioridad que se usan más adelante. Necesitas Node.js 22 o posterior, npm y ripgrep (rg); no hace falta instalar dependencias ni tener una cuenta de modelo. Los comandos de shell funcionan en macOS y Linux.

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

El archivo de configuración de prueba contiene limit: 6; el archivo de datos tiene ocho filas. La primera línea es una cabecera y le siguen cuatro filas de datos. Así, la discrepancia es real y no una consecuencia de una entrada demasiado corta. El programa solo lee los archivos locales incluidos y escribe en el terminal; sus pruebas inician procesos locales de Node, sin solicitudes de red ni escrituras en archivos.

Pista disponible Por dónde empezar Qué falta comprobar
Un comando que reproduce el comportamiento El script del paquete o el ejecutable que invoca Qué argumento selecciona cada manejador
Una cadena distintiva de salida o error El código que la construye Si la coincidencia produce el texto, contiene datos de prueba o es una aserción, y quién llama al productor
Una prueba con nombre Su importación y la llamada dentro de la prueba Si inicia el comando real o proporciona dependencias de sustitución
Una clave de configuración o variable de entorno La lectura y conversión del valor Qué llamador llega hasta ahí y qué cargador proporciona la configuración

Lee el mismo laboratorio en Yenpo

Usa el report-entry-lab descargado cuando tu pregunta sea «¿Qué entrada produjo estas cuatro filas?». El resultado de la lectura debe ser un registro breve del comando de inicio, el manejador seleccionado, los archivos de entrada y la llamada de salida.

  1. Abre la carpeta extraída report-entry-lab en Yenpo y elige la raíz que contiene package.json. Usa Command-P (Go to File) para abrir package.json, después src/cli.mjs y src/commands.mjs. Comprueba el script de inicio y el manejador registrado para report.
  2. Usa Search All Files en la barra lateral para buscar Report rows: con Regex desactivado. Lee src/format-report.mjs junto a las coincidencias de las pruebas: uno construye el texto y las otras verifican el resultado esperado. Sigue la importación y la llamada del formateador en src/report.mjs antes de considerarlo parte de este comando.
  3. Mantén src/report.mjs, src/limits.mjs y la prueba correspondiente abiertos en pestañas. Anota el archivo y la línea que respaldan cada paso; después usa las comprobaciones de Terminal de abajo para distinguir una prueba directa de la función de una ejecución mediante la CLI real.

La navegación de archivos y la búsqueda de texto no necesitan conexión a un modelo ni un servidor de lenguaje de JavaScript. Para probar este flujo, descarga Yenpo para Mac y abre la misma carpeta extraída; la ayuda de lectura explica las dos herramientas de búsqueda. El ZIP contiene archivos fuente, sin historial Git, por lo que este ejercicio lee esos archivos en lugar de una comparación fijada entre commits.

Tienes el comando del terminal

Inspecciona el script antes de deducir la entrada a partir del nombre de una carpeta:

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

El primer comando imprime "node src/cli.mjs report". En cli.mjs, process.argv.slice(2) convierte report en el nombre del comando; los argumentos restantes se pasan al manejador seleccionado. commands[command] es una consulta, así que encontrar el lanzador solo es la mitad del trabajo. Abre commands.mjs: su propiedad report llama a runReport con cargadores para report-config.json y rows.json. La propiedad vecina help devuelve texto de ayuda.

Ahora introduce un cambio controlado mediante el mismo lanzador:

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

Esto imprime Report rows: 2, después Aster y Birch. npm reenvía al script los argumentos posteriores al separador de doble guion. Lee la consulta de despacho y el manejador registrado para establecer la ruta; el cambio en la salida permite comprobar que este comando alcanza el comportamiento del límite. La salida por sí sola no identifica todas las llamadas intermedias.

Solo tienes una cadena de salida

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

La primera búsqueda encuentra format-report.mjs y aserciones de pruebas. El formateador construye la cabecera; las pruebas comparan el texto esperado. Ninguno de esos tipos de coincidencia es automáticamente la entrada de un comando. En report.mjs, comprueba tanto la importación de renderReport como la llamada que le pasa los nombres seleccionados. Después sigue runReport hasta el registro de report en commands.mjs, y el manejador seleccionado hasta la escritura en stdout de cli.mjs.

Así obtienes una ruta que verificar con el comando anterior. Si tu salida real se compone de varias partes, busca un fragmento estable y examina cómo se construye la expresión que lo rodea. Si un formateador compartido tiene varios llamadores, mantenlos como candidatos hasta que los argumentos de inicio o una prueba específica seleccionen uno; no los combines en una única ruta de ejecución.

Tienes el nombre de una prueba

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

La prueba encontrada importa runReport directamente y proporciona su propio entorno, cargador de configuración y cargador de filas. Confirma que cuatro prevalece sobre seis dentro de esta función, pero nunca inicia cli.mjs ni lee un archivo de configuración. No puede decirte qué archivo carga un comando real.

Sigue la importación para localizar el comportamiento y después inspecciona la vinculación real de commands.report. Esta otra prueba cruza la frontera que faltaba:

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

Inicia la CLI real en un proceso hijo con REPORT_LIMIT=4 y comprueba su estado de salida y stdout. Las dos pruebas seleccionadas pasan. Conserva la distinción en tus notas: una elige sus dependencias; la otra ejercita el inicio y los adaptadores de archivos. Que pase una prueba directa de la función no sustituye a la segunda.

Tienes el nombre de la configuración

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

La lectura está en limits.mjs; las otras coincidencias preparan entradas de pruebas. Sigue la llamada a resolveLimit en report.mjs y comprueba la función loadConfig que proporciona commands.report. Su new URL(..., import.meta.url) selecciona el archivo de prueba incluido con una ruta relativa al módulo. La mera coincidencia del nombre de archivo no demuestra que este manejador lo utilice.

Ya tienes el punto de partida necesario para investigar el valor. Deja a un lado help y las pruebas no relacionadas: has comprobado el comando, el manejador seleccionado, los archivos de entrada reales y la ubicación de la salida. Si el comando real usa otra copia del repositorio, un envoltorio u otra fuente de entorno, resuelve esa diferencia antes de ampliar la búsqueda. En caso contrario, la siguiente pregunta es la prioridad del valor, no el resto del repositorio.

Sigue el manejador seleccionado

El report.mjs del laboratorio analiza argumentos, carga la configuración, selecciona un límite y carga filas; después llama a renderReport para añadir la cabecera. Su CLI escribe la cadena devuelta. El ejemplo independiente anterior que aparece a continuación conserva solo las filas de datos, para examinar el valor sin las conexiones del comando. En ambas versiones los cargadores se pasan como argumentos, lo que permite observar el orden en las pruebas sin leer archivos:

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');
}

Antes de abrir ninguna función auxiliar, esta función ya muestra el orden: analizar argumentos, cargar la configuración, elegir el límite, cargar filas e imprimir. También muestra algo fácil de pasar por alto: resolveLimit() solo se ejecuta si loadConfig() tiene éxito. Eso será relevante más adelante.

En una base de código real, sigue las importaciones. Una función llamada loadConfig podría leer un archivo JSON, combinar varios o llamar a un servicio remoto. El nombre no permite saber cuál de esas cosas hace.

Cómo se elige el límite

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;
}

Aquí ocurren tres cosas: elegir una fuente, convertir el valor en número y comprobar el intervalo. Sepáralas en tus notas. Si solo escribes «el entorno sobrescribe la configuración», es fácil suponer que un valor de entorno incorrecto hace que se use el archivo de configuración. Este código nunca lo hace.

CLI --limit REPORT_LIMIT Configuración limit Resultado
2 4 6 2, de la línea de comandos
ausente 4 6 4, del entorno
ausente ausente 6 6, del archivo
ausente ausente ausente valor predeterminado 10
ausente cadena vacía 6 error de validación
0 4 6 error de validación

?? solo omite null y undefined. Una cadena vacía sigue contando como valor, así que prevalece REPORT_LIMIT=''. Number('') es 0, la comprobación del intervalo lanza una excepción y la función nunca consulta el archivo de configuración.

Cambiar ?? por || dejaría pasar las cadenas vacías a la siguiente opción, pero también trataría 0 como «no definido». Eso cambia las reglas; no es una simple limpieza. Decide primero el comportamiento deseado: ¿un valor incorrecto establecido expresamente debe provocar un error o usar silenciosamente la siguiente opción?

Sigue el valor hasta donde se utiliza

Un límite correcto no garantiza una salida correcta. Sigue limit hasta rows.slice(0, limit) y después averigua de dónde viene rows.

Aquí, loadRows() devuelve todas las filas y el manejador conserva las primeras. No se ordena, filtra ni pagina nada en el origen. Por tanto, el límite restringe cuántas filas se imprimen, no cuántos datos se leen o mantienen en memoria.

Eso importa si el problema comunicado es otro. Si alguien dice «el informe agota la memoria incluso con --limit 2», las reglas de prioridad no son el lugar adecuado para investigar. Entrarías en loadRows y comprobarías si el límite debe llegar hasta la fuente de datos. Mismo manejador, otra pregunta y otro archivo siguiente.

Provoca un fallo a propósito

Prueba ahora un archivo de configuración roto junto con --limit 2. La línea de comandos tiene la máxima prioridad, así que el comando debería seguir funcionando, ¿verdad?

No funciona. La configuración se carga antes de elegir el límite, así que, cuando loadConfig() lanza una excepción, no se ejecuta nada posterior. La prioridad decide qué valor se usa. No dice nada sobre qué tiene que completarse antes de esa elección.

El caso opuesto también merece una prueba: la configuración se carga, pero el límite elegido no es válido. Entonces nunca deberían leerse las filas. Cuenta las lecturas para comprobarlo:

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 solo verificaras que se lanzó un error, también pasaría una versión que leyera todas las filas antes de rechazar el límite. reads === 0 es lo que fija el orden.

Ejecuta el ejemplo

Descarga configuration-flow.mjs y ejecútalo con Node.js 22 o posterior. No tiene dependencias:

node --test configuration-flow.mjs

La descarga original de un solo archivo sigue disponible. Sus seis pruebas cubren la prioridad, los valores vacíos e incorrectos, la salida impresa, la omisión de la lectura de filas tras una entrada incorrecta y el caso de configuración rota. Ejercitan estas funciones por separado; en tu proyecto aún convendría comprobar cómo carga el entorno y muestra los errores la CLI real.

El nuevo laboratorio de entrada también cubre las conexiones del comando. Desde su carpeta, ejecuta:

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

Las nueve pruebas pasan. El segundo comando termina deliberadamente con estado 1, imprime limit must be an integer from 1 to 100 en stderr y no produce stdout. La prueba unitaria también cuenta las lecturas de filas, así que el caso del valor vacío comprueba el orden descrito. El ZIP incluye un manifiesto SHA-256; se comprobaron tanto el archivo como las pruebas extraídas.

Anota lo que has encontrado

Basta una nota breve, que la próxima vez será mucho más rápida de leer que el código:

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

En una base de código real, añade el archivo y la línea junto a cada paso y enumera lo que aún no has comprobado: qué archivo lee loadConfig, si un proceso padre establece REPORT_LIMIT y cómo muestra los errores la CLI. Cuando puedas explicar las cuatro filas y señalar las preguntas abiertas, puedes detenerte por ahora. Todavía no necesitas entender el resto del proyecto.

Permite que otro responsable compruebe la entrega

Una nota de lectura es más útil cuando otra persona puede reproducirla sin el historial de tu conversación. Prueba el ZIP de entrega de poster-desk y su breve guía de entrega. Este pequeño proyecto sin conexión y sus afirmaciones se crearon con fines didácticos; no proceden de una aplicación generada por IA ni de una entrega real.

La tarea de quien recibe consiste en comprobar cuatro promesas frente a una instantánea fija del código: un proceso nuevo puede leer las tarjetas locales, los reintentos secuenciales idénticos reutilizan un registro, los recibos son solo vistas previas basadas en datos de prueba y se desconoce si los datos se conservan al reemplazar un despliegue. «Implementado» cubre las condiciones probadas; los reintentos secuenciales no demuestran seguridad bajo concurrencia, la simulación no envía nada y el paquete no aporta pruebas del almacenamiento de despliegue.

Sigue la guía, copia RECEIVER.md fuera del paquete extraído y completa esa copia con tus comandos, resultados, ubicaciones del código y preguntas abiertas. Conserva sin cambios la plantilla entregada y vuelve a verificar el paquete original.

SHA-256 del ZIP
c39d19b210c05f4667cd74db4934459a079408d14c5e7678c24cd087e1df5185

En Yenpo, Go to File y la búsqueda del espacio de trabajo permiten mantener el manejador, el resolutor y las pruebas abiertos en paralelo. Cuando preguntes al AI Reader, céntrate en un valor: «Sigue REPORT_LIMIT desde su lectura hasta su uso, con referencias a las líneas». Obtendrás una ruta que comprobar paso a paso, en lugar de otro resumen del proyecto entero.

Elige la siguiente tarea de lectura según las pruebas disponibles: