{"record":{"id":"6324b909e52167a6","repo":"garrytan/gstack","slug":"cannot-promote-skill-for-normalized-is-in-stat","errorCode":null,"errorMessage":"Cannot promote: skill for ${normalized} is in state \"${current.state}\", expected \"active\".\nCause: skill must be active in this project (used ${PROMOTE_THRESHOLD}+ times without flag) before global promotion.\nAction: use the skill in this project until it auto-promotes to active.","messagePattern":"Cannot promote: skill for (.+?) is in state \"(.+?)\", expected \"active\"\\.\nCause: skill must be active in this project \\(used (.+?)\\+ times without flag\\) before global promotion\\.\nAction: use the skill in this project until it auto-promotes to active\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"browse/src/domain-skills.ts","lineNumber":355,"sourceCode":"\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\n    use_count: 0,\n    flag_count: 0,\n    updated_ts: now,\n  };\n  await appendRow(globalFile(), globalRow);\n  return globalRow;\n}","sourceCodeStart":337,"sourceCodeEnd":373,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/browse/src/domain-skills.ts#L337-L373","documentation":"Thrown by promoteToGlobal() when the skill exists but current.state !== 'active'. Skills start in 'quarantined' state and must earn PROMOTE_THRESHOLD or more uses without being flagged before they become 'active' and are eligible for global promotion. This enforces a trust gradient — only proven skills cross the project-to-global boundary.","triggerScenarios":"Calling promoteToGlobal() on a skill whose latest row has state 'quarantined' (or any state other than 'active').","commonSituations":"A newly created skill that has not yet accumulated enough clean uses. The user tries to promote it to global scope prematurely, before the auto-promotion threshold is reached.","solutions":["Use the skill in the project repeatedly (PROMOTE_THRESHOLD+ times) without it being flagged — it will auto-promote to 'active'","Check current state with '$B domain-skill list' to see if it is still 'quarantined'","Ensure no flags are being raised against the skill during use, as flags reset or block promotion"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Check state before promoting\nconst rows = await readRows(projectFile(projectSlug));\nconst latest = resolveLatest(rows);\nconst current = latest.get(`project::${normalizeHost(host)}`);\nif (!current || current.state !== 'active') {\n  console.error(`Skill is in state '${current?.state ?? 'missing'}'. Use it more to auto-promote.`);\n  return;\n}\nawait promoteToGlobal(host, projectSlug);","typeGuard":"function isSkillActive(row: DomainSkillRow | undefined): row is DomainSkillRow {\n  return row !== undefined && row.state === 'active';\n}","tryCatchPattern":null,"preventionTips":["Track skill usage count and state in your application before attempting promotion","Do not attempt promotion on freshly created (quarantined) skills — let them accumulate clean uses first","Monitor for flags against the skill, as flags block the auto-promotion to active"],"tags":["domain-skills","promote","state-machine"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}