stablyai/orca · error
Targeted update changed the non-targeted control skill
Error message
Targeted update changed the non-targeted control skill
What it means
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.
Source
Thrown at config/scripts/verify-skill-update-roundtrip.mjs:238
if (
shape === 'copy' &&
targetProviderAfter !== targetProviderBefore &&
targetProviderAfter !== currentSkill(targetName).packageDigest
) {
throw new Error('Independent provider copy changed to an unexpected package identity')
}
if (shape === 'copy') {
// Why: hosted 1.5.17 replaces copies with aliases while equivalent local runs
// retain the copy. Both prove this input topology must remain ineligible.
const outcome = targetProviderStat.isSymbolicLink()
? 'converged to an alias'
: targetProviderAfter === targetProviderBefore
? 'remained a historical copy'
: 'converged as a copy'
console.log(`[skill-update-roundtrip] independent copy ${outcome}`)
}
if ((await packageDigestAt(controlCanonical)) !== controlBefore) {
throw new Error('Targeted update changed the non-targeted control skill')
}
if ((await packageDigestAt(await realpath(controlProvider))) !== controlProviderBefore) {
throw new Error('Targeted update changed the non-targeted control provider placement')
}
const controlProviderStat = await lstat(controlProvider)
if (shape === 'symlink' && !controlProviderStat.isSymbolicLink()) {
throw new Error('Targeted update changed the non-targeted control topology')
}
} finally {
await rm(sandbox, { recursive: true, force: true })
}
View on GitHub (pinned to 1136503c6a)
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.
Defensive patterns
Strategy: validation
Validate before calling
// Before the targeted update, confirm the lock pins the control skill so it's not eligible:
const lock = JSON.parse(await readFile(lockPath, 'utf8'))
const controlPinned = lock.skills?.some((s) => s.name === controlName && s.releaseRevision === currentSkill(controlName).releaseRevision)
if (!controlPinned) {
throw new Error(`Lock does not pin control skill ${controlName}; targeted update may leak into it`)
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Targeted update changed the non-targeted control provider pl
- ${targetName} provider alias was replaced with an independen
- Targeted update changed the non-targeted control topology
- Usage: verify-skill-update-roundtrip.mjs --cli=<version> --a
- ${name} canonical placement did not update to the PR content
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/1f8c9dc28a1922c0.
Report an issue: GitHub.