{"record":{"id":"7e54457a21d62910","repo":"Fission-AI/OpenSpec","slug":"getskilltemplates-returns-unpinned-length-ski","errorCode":null,"errorMessage":"getSkillTemplates() returns ${unpinned.length} skill(s) with no pinned hash in ${sourceLabel}:\n${unpinned.map((key) => `  ${key}`).join('\\n')}\nAdd each to EXPECTED_GENERATED_SKILL_CONTENT_HASHES and GENERATED_SKILL_FACTORIES.\nUntil then the skill ships with no parity coverage, so this run would have reported success while leaving it unguarded.","messagePattern":"getSkillTemplates\\(\\) returns (.+?) skill\\(s\\) with no pinned hash in (.+?):\n(.+?)`\\)\\.join\\('\\\\n'\\)\\}\nAdd each to EXPECTED_GENERATED_SKILL_CONTENT_HASHES and GENERATED_SKILL_FACTORIES\\.\nUntil then the skill ships with no parity coverage, so this run would have reported success while leaving it unguarded\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/parity-hash-shared.mjs","lineNumber":100,"sourceCode":"\n  if (seen !== totalHexLiterals) {\n    throw new Error(\n      `Rewrote ${seen} of ${totalHexLiterals} 64-hex literals in ${sourceLabel}.\\n` +\n        'Every one is assumed to be a pinned hash, so the two counts must agree. Either:\\n' +\n        '  - a pinned hash is formatted in a way the patterns here do not match, and ' +\n        'would have been left stale without warning: widen them; or\\n' +\n        '  - the file gained a 64-hex literal that is not a pin: narrow the count above ' +\n        'so it stops being mistaken for one.'\n    );\n  }\n\n  // The checks above only see pins that exist. A workflow added to the registry\n  // but never pinned is invisible to them AND to the parity test, which compares\n  // only the entries it already lists - so it would ship with no golden hash at\n  // all while this reported success. Compare the other direction too.\n  const unpinned = [...knownContentKeys].filter((key) => !seenContentKeys.has(key));\n  if (unpinned.length > 0) {\n    throw new Error(\n      `getSkillTemplates() returns ${unpinned.length} skill(s) with no pinned hash in ${sourceLabel}:\\n` +\n        unpinned.map((key) => `  ${key}`).join('\\n') +\n        '\\nAdd each to EXPECTED_GENERATED_SKILL_CONTENT_HASHES and GENERATED_SKILL_FACTORIES.\\n' +\n        'Until then the skill ships with no parity coverage, so this run would have ' +\n        'reported success while leaving it unguarded.'\n    );\n  }\n\n  return { source: rewritten, moved };\n}\n\n/**\n * Stable, key-sorted serialisation used to hash a template's payload.\n *\n * Must stay byte-compatible with the copy in\n * `test/core/templates/skill-templates-parity.test.ts`. A divergence cannot pass\n * unnoticed: that test recomputes the hashes independently and compares.\n */","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/Fission-AI/OpenSpec/blob/6926ccb18afa4ff621112813e9968334576ee11a/scripts/parity-hash-shared.mjs#L82-L118","documentation":"Error \"getSkillTemplates() returns ${unpinned.length} skill(s) with no pinned hash in ${sourceLabel}:\n${unpinned.map((key) => `  ${key}`).join('\\n')}\nAdd each to EXPECTED_GENERATED_SKILL_CONTENT_HASHES and GENERATED_SKILL_FACTORIES.\nUntil then the skill ships with no parity coverage, so this run would have reported success while leaving it unguarded.\" thrown in Fission-AI/OpenSpec.","triggerScenarios":"Thrown at scripts/parity-hash-shared.mjs:100 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":[],"exampleFix":null,"handlingStrategy":null,"validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"6926ccb18afa4ff621112813e9968334576ee11a","analyzedAt":"2026-08-25T12:29:45.729Z","schemaVersion":2},"datasetVersion":"2026-08-25T16:17:27.014Z"}