{"record":{"id":"496edaa397136d5c","repo":"garrytan/gstack","slug":"cannot-promote-no-skill-for-normalized-in-proj","errorCode":null,"errorMessage":"Cannot promote: no skill for ${normalized} in project ${projectSlug}.\nCause: skill does not exist or is tombstoned.\nAction: $B domain-skill list to see what exists in this project.","messagePattern":"Cannot promote: no skill for (.+?) in project (.+?)\\.\nCause: skill does not exist or is tombstoned\\.\nAction: \\$B domain-skill list to see what exists in this project\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"browse/src/domain-skills.ts","lineNumber":348,"sourceCode":"    flag_count: flagCount,\n    version: current.version + 1,\n    updated_ts: new Date().toISOString(),\n  };\n  await appendRow(projectFile(projectSlug), updated);\n  return updated;\n}\n\n/**\n * Promote an active per-project skill to global. Explicit operator call only —\n * never auto-promoted across project boundaries (T4).\n */\nexport async function promoteToGlobal(host: string, projectSlug: string): Promise<DomainSkillRow> {\n  const normalized = normalizeHost(host);\n  const rows = await readRows(projectFile(projectSlug));\n  const latest = resolveLatest(rows);\n  const current = latest.get(`project::${normalized}`);\n  if (!current) {\n    throw new Error(\n      `Cannot promote: no skill for ${normalized} in project ${projectSlug}.\\n` +\n        'Cause: skill does not exist or is tombstoned.\\n' +\n        'Action: $B domain-skill list to see what exists in this project.'\n    );\n  }\n  if (current.state !== 'active') {\n    throw new Error(\n      `Cannot promote: skill for ${normalized} is in state \"${current.state}\", expected \"active\".\\n` +\n        `Cause: skill must be active in this project (used ${PROMOTE_THRESHOLD}+ times without flag) before global promotion.\\n` +\n        'Action: use the skill in this project until it auto-promotes to active.'\n    );\n  }\n  const now = new Date().toISOString();\n  const globalRow: DomainSkillRow = {\n    ...current,\n    scope: 'global',\n    state: 'global',\n    version: 1, // global file has its own version line","sourceCodeStart":330,"sourceCodeEnd":366,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/browse/src/domain-skills.ts#L330-L366","documentation":"Thrown by promoteToGlobal() when resolveLatest(rows) returns no entry for the key 'project::<normalized_host>'. This means no active or quarantined skill row exists for the given host in the specified project — the skill was never created, was tombstoned by a prior delete, or the host name does not match after normalization.","triggerScenarios":"Calling promoteToGlobal(host, projectSlug) where readRows(projectFile(projectSlug)) contains no non-tombstoned row for the normalized host.","commonSituations":"Typo or casing mismatch in the host name, skill was deleted before promotion attempt, skill was saved to a different project slug, or the skill exists only in global scope.","solutions":["Run '$B domain-skill list' to verify which skills exist in the current project","Check the host spelling — normalizeHost may transform the name (e.g., lowercasing, stripping protocol)","If the skill was never created, call writeSkill() first, then use it until active before promoting"],"exampleFix":"// before — promoting a non-existent skill\nawait promoteToGlobal('exampl.com', 'my-proj'); // typo\n\n// after — verify then promote\nconst skills = await listSkills('my-proj');\nif (skills.has('example.com')) {\n  await promoteToGlobal('example.com', 'my-proj');\n}","handlingStrategy":"validation","validationCode":"// Before promoting, check the skill exists in project scope\nconst rows = await readRows(projectFile(projectSlug));\nconst latest = resolveLatest(rows);\nconst key = `project::${normalizeHost(host)}`;\nif (!latest.has(key)) {\n  console.error(`No skill for ${host} in ${projectSlug}. Run '$B domain-skill list'.`);\n  return;\n}\nawait promoteToGlobal(host, projectSlug);","typeGuard":"function skillExistsInProject(latest: Map<string, DomainSkillRow>, host: string): boolean {\n  return latest.has(`project::${normalizeHost(host)}`);\n}","tryCatchPattern":null,"preventionTips":["Call '$B domain-skill list' before attempting promotion to verify the skill exists","Normalize the host name the same way the library does before looking it up","Confirm the correct projectSlug — skills are scoped per project"],"tags":["domain-skills","promote","not-found"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}