{"record":{"id":"6e7ae303b6fb54dc","repo":"garrytan/gstack","slug":"cannot-rollback-normalized-has-fewer-than-2-ve","errorCode":null,"errorMessage":"Cannot rollback: ${normalized} has fewer than 2 versions in ${scope} scope.\nCause: no prior version to roll back to.\nAction: $B domain-skill rm to delete instead, or wait for a future revision to roll back from.","messagePattern":"Cannot rollback: (.+?) has fewer than 2 versions in (.+?) scope\\.\nCause: no prior version to roll back to\\.\nAction: \\$B domain-skill rm to delete instead, or wait for a future revision to roll back from\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"browse/src/domain-skills.ts","lineNumber":385,"sourceCode":"    use_count: 0,\n    flag_count: 0,\n    updated_ts: now,\n  };\n  await appendRow(globalFile(), globalRow);\n  return globalRow;\n}\n\n/**\n * Rollback to a prior version (by sha256 OR previous version number).\n * Re-emits the prior row as the latest, preserving the version counter monotonicity.\n */\nexport async function rollbackSkill(host: string, projectSlug: string, scope: SkillScope = 'project'): Promise<DomainSkillRow> {\n  const normalized = normalizeHost(host);\n  const file = scope === 'project' ? projectFile(projectSlug) : globalFile();\n  const rows = await readRows(file);\n  const matching = rows.filter((r) => r.host === normalized && r.scope === scope && !r.tombstone);\n  if (matching.length < 2) {\n    throw new Error(\n      `Cannot rollback: ${normalized} has fewer than 2 versions in ${scope} scope.\\n` +\n        'Cause: no prior version to roll back to.\\n' +\n        'Action: $B domain-skill rm to delete instead, or wait for a future revision to roll back from.'\n    );\n  }\n  // Sort by version desc; take second-latest as the rollback target.\n  matching.sort((a, b) => b.version - a.version);\n  const target = matching[1]!;\n  const newVersion = matching[0]!.version + 1;\n  const restored: DomainSkillRow = {\n    ...target,\n    version: newVersion,\n    updated_ts: new Date().toISOString(),\n  };\n  await appendRow(file, restored);\n  return restored;\n}\n","sourceCodeStart":367,"sourceCodeEnd":403,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/browse/src/domain-skills.ts#L367-L403","documentation":"Thrown by rollbackSkill() when fewer than 2 non-tombstoned version rows exist for the host in the given scope. Rollback re-emits the second-latest version as a new latest row, so it requires at least one prior version to target. With 0 or 1 versions there is nothing to roll back to.","triggerScenarios":"Calling rollbackSkill(host, projectSlug, scope) where rows.filter(host === normalized && scope === scope && !tombstone).length < 2.","commonSituations":"The skill was only written once (single version), or all prior versions were tombstoned or compacted away. The user expects an undo that has no target.","solutions":["Use '$B domain-skill rm' to delete the skill instead if you want to remove it","Wait for a future writeSkill() revision to create a second version, then rollback to the first","Check version history with '$B domain-skill list' to confirm how many versions exist"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Check version count before rolling back\nconst rows = await readRows(scope === 'project' ? projectFile(projectSlug) : globalFile());\nconst matching = rows.filter(r => r.host === normalizeHost(host) && r.scope === scope && !r.tombstone);\nif (matching.length < 2) {\n  console.error('Not enough versions to roll back. Use delete instead.');\n  return;\n}\nawait rollbackSkill(host, projectSlug, scope);","typeGuard":"function hasRollbackTarget(rows: DomainSkillRow[], host: string, scope: SkillScope): boolean {\n  return rows.filter(r => r.host === normalizeHost(host) && r.scope === scope && !r.tombstone).length >= 2;\n}","tryCatchPattern":null,"preventionTips":["Track version count after each writeSkill call","Only offer rollback in the UI when >= 2 versions exist","Use deleteSkill for single-version skills instead of rollback"],"tags":["domain-skills","rollback","versioning"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}