{"record":{"id":"b09e852df11610bb","repo":"tinyhumansai/openhuman","slug":"provider-chain-row-p-order-is-missing-a-compone","errorCode":null,"errorMessage":"provider-chain row ${p.order} is missing a component name","messagePattern":"provider-chain row (.+?) is missing a component name","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/generate-architecture-docs.mjs","lineNumber":76,"sourceCode":"  const rowRe = /^\\s*\\*?\\s*(\\d+)\\.\\s+(.+?)\\s+—\\s+(.+?)\\s*$/;\n  const providers = [];\n  for (const line of region.split('\\n')) {\n    const m = rowRe.exec(line);\n    if (!m) continue;\n    providers.push({ order: Number(m[1]), name: m[2].trim(), role: m[3].trim() });\n  }\n  if (providers.length === 0) {\n    throw new Error('provider-chain source marker contained no `N. Component — role` rows');\n  }\n  // Guard against drift in the marker itself: orders must be 1..N, in order,\n  // names non-empty, and roles free of the `|` that would break the table.\n  providers.forEach((p, i) => {\n    if (p.order !== i + 1) {\n      throw new Error(\n        `provider-chain rows must be numbered 1..N in order; row ${i + 1} is numbered ${p.order}`\n      );\n    }\n    if (!p.name) throw new Error(`provider-chain row ${p.order} is missing a component name`);\n    if (!p.role) throw new Error(`provider-chain row ${p.order} is missing a role`);\n    if (p.name.includes('|') || p.role.includes('|')) {\n      throw new Error(`provider-chain row ${p.order} must not contain a \"|\" character`);\n    }\n  });\n  return providers;\n}\n\n/**\n * Render the markdown body that lives between the BEGIN/END markers.\n * Returned as an array of lines (no trailing join) so splicing stays\n * deterministic and line-oriented.\n *\n * @param {{ order: number, name: string, role: string }[]} providers\n * @returns {string[]}\n */\nexport function renderProviderChainBody(providers) {\n  return [","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/scripts/generate-architecture-docs.mjs#L58-L94","documentation":"Defensive guard in parseProviderChain(): a row matched the regex but its captured component name trims to the empty string. Because the regex requires at least one character before the em dash, this only fires when that character(s) is pure whitespace — i.e. the row has a role but the component-name slot was left blank.","triggerScenarios":"A marker row like `* 3.  — runs the boot gate` where the author deleted the component name but kept numbering and the em dash. Any single stray space before the dash satisfies `(.+?)` while trimming empty.","commonSituations":"Hand-editing a row to swap in a new component and accidentally deleting the name; placeholder rows (`3. TBD — role`) don't hit this (TBD is non-empty) but fully blanked names do.","solutions":["Fill in the missing component identifier in the flagged row (the message names the row number)","Keep names as real exported symbol names (e.g. `CoreStateProvider`) so the doc stays greppable","Re-run the generator to confirm the row parses"],"exampleFix":"// before:\n * 3.  — provides auth snapshot via fetchCoreAppSnapshot()\n\n// after:\n * 3. CoreStateProvider — provides auth snapshot via fetchCoreAppSnapshot()","handlingStrategy":"validation","validationCode":"for (const m of rows.map(l => ROW_RE.exec(l))) {\n  if (!m[2].trim()) {\n    console.error(`Row ${m[1]} has an empty component name`);\n    process.exit(1);\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use real exported component names (CoreStateProvider, SocketProvider, …) in the marker rows so empty names are obvious in review","Diff the marker block whenever App.tsx provider code changes; the name column should mirror the code"],"tags":["docs","codegen","validation"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}