{"record":{"id":"34901276574aa82a","repo":"affaan-m/ECC","slug":"pattern-source-is-missing-the-pattern-categor","errorCode":null,"errorMessage":"${pattern.source} is missing the ${pattern.category} row","messagePattern":"(.+?) is missing the (.+?) row","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/ci/catalog.js","lineNumber":125,"sourceCode":"  }\n\n  expectations.push({\n    category: 'agents',\n    mode: 'exact',\n    expected: Number(projectTreeAgentsMatch[1]),\n    source: 'README.md project tree (agents)'\n  });\n\n  const tablePatterns = [\n    { category: 'agents', regex: /\\|\\s*(?:\\*\\*)?Agents(?:\\*\\*)?\\s*\\|\\s*(?:(?:PASS:|\\u2705)\\s*)?(\\d+)\\s+agents\\s*\\|/i, source: 'README.md comparison table' },\n    { category: 'commands', regex: /\\|\\s*(?:\\*\\*)?Commands(?:\\*\\*)?\\s*\\|\\s*(?:(?:PASS:|\\u2705)\\s*)?(\\d+)\\s+commands(?:\\s*\\([^)]*\\))?\\s*\\|/i, source: 'README.md comparison table' },\n    { category: 'skills', regex: /\\|\\s*(?:\\*\\*)?Skills(?:\\*\\*)?\\s*\\|\\s*(?:(?:PASS:|\\u2705)\\s*)?(\\d+)\\s+skills\\s*\\|/i, source: 'README.md comparison table' }\n  ];\n\n  for (const pattern of tablePatterns) {\n    const match = readmeContent.match(pattern.regex);\n    if (!match) {\n      throw new Error(`${pattern.source} is missing the ${pattern.category} row`);\n    }\n\n    expectations.push({\n      category: pattern.category,\n      mode: 'exact',\n      expected: Number(match[1]),\n      source: `${pattern.source} (${pattern.category})`\n    });\n  }\n\n  return expectations;\n}\n\nfunction parseZhRootReadmeExpectations(readmeContent) {\n  const match = readmeContent.match(/你现在可以使用\\s+(\\d+)\\s+个代理、\\s*(\\d+)\\s*个技能和\\s*(\\d+)\\s*个命令/i);\n  if (!match) {\n    throw new Error('README.zh-CN.md is missing the quick-start catalog summary');\n  }","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/ci/catalog.js#L107-L143","documentation":"This throw occurs inside the loop over `tablePatterns` in `parseReadmeExpectations`. The script scans README.md's comparison table for three markdown rows — Agents, Commands, Skills — each expected in the form `| Agents | 68 agents |`. If any single row's regex fails to match, the error names which source (`README.md comparison table`) and which category is missing. All three counts feed the cross-document consistency check.","triggerScenarios":"Triggered by `node scripts/ci/catalog.js` when the comparison-table section of README.md is missing the Agents, Commands, or Skills row, or the row's wording deviates (e.g. `| **Agents** | 68 |` without the trailing `agents` unit word, or `68` spelled out, or a `PASS:`/emoji prefix in an unexpected position). The check fires per-row, so only the first missing row is reported.","commonSituations":"The comparison table is reformatted to remove unit words (`agents`, `commands`, `skills`). A row is deleted during a table consolidation. Counts are updated by hand but the unit suffix is dropped. The table is moved to a different doc without updating README.","solutions":["Open README.md and restore the missing row to the exact form `| Agents | N agents |` (or `| Commands | N commands |` / `| Skills | N skills |`), matching the regex which tolerates optional `**` bold and optional `PASS:`/`✅` prefixes.","Run `node scripts/ci/catalog.js --write` to let the sync functions rewrite all documented counts from the live catalog.","Confirm the row lives in a markdown table (pipe-delimited) and the unit word follows the number with a single space.","Re-run `node scripts/ci/catalog.js` to confirm all three rows now match."],"exampleFix":"// before (README.md comparison table, skills row dropped)\n| **Agents** | 68 agents |\n| **Commands** | 94 commands |\n\n// after\n| **Agents** | 68 agents |\n| **Commands** | 94 commands |\n| **Skills** | 284 skills |","handlingStrategy":"validation","validationCode":"const fs = require('fs');\nconst readme = fs.readFileSync('README.md', 'utf8');\nconst rows = [\n  { cat: 'agents', re: /\\|\\s*(?:\\*\\*)?Agents(?:\\*\\*)?\\s*\\|\\s*(?:(?:PASS:|\\u2705)\\s*)?(\\d+)\\s+agents\\s*\\|/i },\n  { cat: 'commands', re: /\\|\\s*(?:\\*\\*)?Commands(?:\\*\\*)?\\s*\\|\\s*(?:(?:PASS:|\\u2705)\\s*)?(\\d+)\\s+commands(?:\\s*\\([^)]*\\))?\\s*\\|/i },\n  { cat: 'skills', re: /\\|\\s*(?:\\*\\*)?Skills(?:\\*\\*)?\\s*\\|\\s*(?:(?:PASS:|\\u2705)\\s*)?(\\d+)\\s+skills\\s*\\|/i },\n];\nfor (const { cat, re } of rows) {\n  if (!re.test(readme)) {\n    console.error(`README.md comparison table missing ${cat} row`);\n    process.exit(1);\n  }\n}","typeGuard":"function hasComparisonTableRow(readmeContent, category) {\n  const map = {\n    agents: /\\|\\s*(?:\\*\\*)?Agents(?:\\*\\*)?\\s*\\|\\s*(?:(?:PASS:|\\u2705)\\s*)?(\\d+)\\s+agents\\s*\\|/i,\n    commands: /\\|\\s*(?:\\*\\*)?Commands(?:\\*\\*)?\\s*\\|\\s*(?:(?:PASS:|\\u2705)\\s*)?(\\d+)\\s+commands(?:\\s*\\([^)]*\\))?\\s*\\|/i,\n    skills: /\\|\\s*(?:\\*\\*)?Skills(?:\\*\\*)?\\s*\\|\\s*(?:(?:PASS:|\\u2705)\\s*)?(\\d+)\\s+skills\\s*\\|/i,\n  };\n  const m = readmeContent.match(map[category]);\n  return m !== null && Number.isInteger(Number(m[1]));\n}","tryCatchPattern":"try {\n  runCatalogCheck();\n} catch (error) {\n  if (/comparison table is missing the .* row/i.test(error.message)) {\n    console.error('Restore the markdown table row with the exact unit word (agents/commands/skills) after the count.');\n    console.error('Run: node scripts/ci/catalog.js --write');\n  }\n  throw error;\n}","preventionTips":["Keep the unit word (agents/commands/skills) immediately after the count in every comparison-table row.","Do not strip the `PASS:`/emoji prefix or the bold markers without re-testing the regex.","Run `--write` instead of hand-editing counts.","After any table reformat, run the catalog check before committing."],"tags":["catalog","ci","regex","readme","markdown-table","documentation-drift"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}