{"record":{"id":"12e9683a9391727e","repo":"tinyhumansai/openhuman","slug":"provider-chain-source-marker-contained-no-n-comp","errorCode":null,"errorMessage":"provider-chain source marker contained no `N. Component — role` rows","messagePattern":"provider-chain source marker contained no `N\\. Component — role` rows","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/generate-architecture-docs.mjs","lineNumber":66,"sourceCode":"export function parseProviderChain(appSource) {\n  const begin = appSource.indexOf('@generated-source:provider-chain');\n  const end = appSource.indexOf('@end-source:provider-chain');\n  if (begin === -1 || end === -1 || end < begin) {\n    throw new Error(\n      'provider-chain source marker not found in App.tsx; expected ' +\n        '`@generated-source:provider-chain` … `@end-source:provider-chain`'\n    );\n  }\n  const region = appSource.slice(begin, end);\n  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","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/scripts/generate-architecture-docs.mjs#L48-L84","documentation":"The marker block in App.tsx was found, but not a single line inside the region matched the row regex /^\\s*\\*?\\s*(\\d+)\\.\\s+(.+?)\\s+—\\s+(.+?)\\s*$/. Rows MUST be `N. Component — role`, numbered, and joined to the role by a single em dash (—, U+2014). An empty parse means the block is prose-only or the dash/numbering format drifted, so the generator aborts instead of writing an empty provider table.","triggerScenarios":"Rows rewritten with a hyphen `-` or en dash `–` instead of an em dash `—`; rows converted from `1. Foo — bar` to markdown bullets `- Foo — bar`; rows commented out or replaced with free text while the outer markers survived; a two-space or tab variation is fine, but any row without `<digits>. <text> — <text>` is skipped.","commonSituations":"Auto-correct/IME replacing em dash with a similar glyph; copy-pasting the chain from rendered markdown that normalized the dash; a teammate 'simplifying' the numbered list. Note the repo itself bans em dash U+2014 in i18n strings, so editors with a lint that rewrites dashes hit exactly this.","solutions":["Rewrite each row inside the marker as `N. Component — role` using a real em dash (U+2014) between name and role","Keep the `N.` numbering prefix on every row — unnumbered lines never match","Re-run `node scripts/generate-architecture-docs.mjs` and confirm the rendered table is non-empty"],"exampleFix":"// before (rows no longer match the regex):\n * @generated-source:provider-chain\n * Sentry ErrorBoundary - global crash boundary\n * Redux Provider - app-wide store\n * @end-source:provider-chain\n\n// after:\n * @generated-source:provider-chain\n * 1. Sentry ErrorBoundary — global crash boundary\n * 2. Redux Provider — app-wide store\n * @end-source:provider-chain","handlingStrategy":"validation","validationCode":"const ROW_RE = /^\\s*\\*?\\s*(\\d+)\\.\\s+(.+?)\\s+—\\s+(.+?)\\s*$/;\nconst begin = src.indexOf('@generated-source:provider-chain');\nconst end = src.indexOf('@end-source:provider-chain');\nconst rows = src.slice(begin, end).split('\\n').filter(l => ROW_RE.test(l));\nif (rows.length === 0) {\n  console.error('Marker block has no `N. Component — role` rows — check the em dash (U+2014) survived editing');\n  process.exit(1);\n}","typeGuard":"/** True when a marker line is a parseable `N. Component — role` row (em dash U+2014). */\nconst ROW_RE = /^\\s*\\*?\\s*(\\d+)\\.\\s+(.+?)\\s+—\\s+(.+?)\\s*$/;\nfunction isProviderChainRow(line) {\n  return ROW_RE.test(line);\n}","tryCatchPattern":"try {\n  parseProviderChain(appSource);\n} catch (err) {\n  if (err.message.includes('no `N. Component — role` rows')) {\n    console.error('Rows unreadable — most likely the em dash was replaced with a hyphen/en dash');\n  }\n  throw err;\n}","preventionTips":["Type the em dash (U+2014) deliberately when editing the marker block; verify with a regex search for `—` in App.tsx","Paste chain edits as plain text with auto-correct disabled — smart-dash substitution is the usual culprit","Run `node scripts/generate-architecture-docs.mjs` locally right after editing the block; it parses the rows before touching any doc"],"tags":["docs","codegen","unicode","formatting"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}