{"record":{"id":"1f8c9dc28a1922c0","repo":"stablyai/orca","slug":"targeted-update-changed-the-non-targeted-control-s","errorCode":null,"errorMessage":"Targeted update changed the non-targeted control skill","messagePattern":"Targeted update changed the non-targeted control skill","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"config/scripts/verify-skill-update-roundtrip.mjs","lineNumber":238,"sourceCode":"  if (\n    shape === 'copy' &&\n    targetProviderAfter !== targetProviderBefore &&\n    targetProviderAfter !== currentSkill(targetName).packageDigest\n  ) {\n    throw new Error('Independent provider copy changed to an unexpected package identity')\n  }\n  if (shape === 'copy') {\n    // Why: hosted 1.5.17 replaces copies with aliases while equivalent local runs\n    // retain the copy. Both prove this input topology must remain ineligible.\n    const outcome = targetProviderStat.isSymbolicLink()\n      ? 'converged to an alias'\n      : targetProviderAfter === targetProviderBefore\n        ? 'remained a historical copy'\n        : 'converged as a copy'\n    console.log(`[skill-update-roundtrip] independent copy ${outcome}`)\n  }\n  if ((await packageDigestAt(controlCanonical)) !== controlBefore) {\n    throw new Error('Targeted update changed the non-targeted control skill')\n  }\n  if ((await packageDigestAt(await realpath(controlProvider))) !== controlProviderBefore) {\n    throw new Error('Targeted update changed the non-targeted control provider placement')\n  }\n  const controlProviderStat = await lstat(controlProvider)\n  if (shape === 'symlink' && !controlProviderStat.isSymbolicLink()) {\n    throw new Error('Targeted update changed the non-targeted control topology')\n  }\n} finally {\n  await rm(sandbox, { recursive: true, force: true })\n}\n","sourceCodeStart":220,"sourceCodeEnd":250,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/config/scripts/verify-skill-update-roundtrip.mjs#L220-L250","documentation":"The round-trip's central invariant: a targeted 'skills update <targetName> --global' must NOT touch the control skill (orchestration). After the update, the script hashes controlCanonical and compares to controlBefore; a mismatch means the targeted update leaked into the non-targeted skill.","triggerScenarios":"The CLI's targeted update doesn't actually scope to the named skill and rewrites all canonical placements; the lock file was set up wrong so the control skill appears eligible; a shared file between skills got rewritten.","commonSituations":"CLI regression that ignores the skill-name argument and updates globally; lock-file fixture in the test missing the control skill's pin so it looks updatable; a refactor that made skills share a directory and a targeted update mutates the shared dir.","solutions":["Inspect the lock fixture written before the update — the control skill must be pinned at controlBefore so it's not eligible.","Diff controlCanonical before/after to see what the update changed.","File a CLI bug: a targeted update by name must not modify other skills' canonical placements.","Pin a CLI version whose targeted update is correctly scoped."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before the targeted update, confirm the lock pins the control skill so it's not eligible:\nconst lock = JSON.parse(await readFile(lockPath, 'utf8'))\nconst controlPinned = lock.skills?.some((s) => s.name === controlName && s.releaseRevision === currentSkill(controlName).releaseRevision)\nif (!controlPinned) {\n  throw new Error(`Lock does not pin control skill ${controlName}; targeted update may leak into it`)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat any change to a non-targeted skill after a targeted update as a CLI bug, not a flaky test.","Pin the control skill in the lock fixture at the current revision so it's ineligible.","Run the targeted-update isolation rail on every CLI release."],"tags":["skill-update","targeted-update","isolation","regression-detection","ci-gate"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}