test(skills): cover placement conflict shapes

This commit is contained in:
Jinwoo-H
2026-08-12 12:48:32 -04:00
parent 4f9b32f58c
commit c75d772e7f
4 changed files with 85 additions and 5 deletions
@@ -1249,8 +1249,8 @@ OS, destination kind, transport, conflicts, placement topology/mechanism, copy f
capability absence, recovered transactions, stale extraction/lock cleanup, and failure categories
use fixed or bounded values; bundle error-category cardinality is capped at 32. The implementation
adds no product telemetry event, RPC field, persisted schema, or remote opcode. The skill-sharing
release suite passes 423 tests with 29 expected platform skips, the focused affected matrix passes
98 tests with one expected skip, typecheck and the full lint pipeline pass, and the real-process
release suite passes 427 tests with 29 expected platform skips, the focused affected matrix passes
102 tests with one expected skip, typecheck and the full lint pipeline pass, and the real-process
macOS recovery matrix passes all 17 crash boundaries. Physical Windows/WSL reruns remain separate
platform gates below.
@@ -1307,8 +1307,14 @@ platform gates below.
safe removal.
- [x] Test modified and unowned canonical and provider conflicts.
- [ ] Test canonical/provider paths as regular files, directories, aliases, junctions, external
links, and broken links.
links, and broken links. Deterministic native coverage now preserves canonical regular-file
name collisions, unowned provider files/directories, external and broken unowned links,
Orca-owned broken-link repair, aliases, and verified copy fallback. Physical Windows
junction coverage remains open.
- [ ] Test concurrent desktop, headless runtime, CLI, SSH, and recovery attempts.
The shared transaction core now serializes two simultaneous installs into one installed and
one unchanged result, one receipt, and no staging residue; cross-entry-point contention
remains open.
- [ ] Test permission and read-only failures, disk exhaustion, cancellation, process termination,
and host disconnect. Simulated `EACCES` and `ENOSPC` preservation plus lost-response retry are
covered; physical failure and termination journeys remain open.
@@ -99,7 +99,7 @@ conflict/topology/mechanism outcomes, copy fallback, capability absence, recover
and bounded error categories without paths, skill names, contents, share URLs, grants, host IDs,
or credentials. Bundle summaries are aggregate and cap error-category cardinality at 32. This is
not product telemetry and changes no RPC, persisted state, or remote opcode, so old clients and
hosts remain compatible. The release suite passes 423 tests, full typecheck/lint pass, and the
hosts remain compatible. The release suite passes 427 tests, full typecheck/lint pass, and the
real-process macOS crash-recovery matrix passes all 17 boundaries; physical platform reruns remain
tracked in the checklist.
@@ -119,6 +119,19 @@ describe('skill install transaction', () => {
expect(await readFile(join(destination, 'SKILL.md'), 'utf8')).toContain('# Cloud')
})
it('preserves a regular file at the canonical destination as a name collision', async () => {
const root = await temporaryDirectory()
const archive = await packageVersion(root, 'version_1', '# Cloud')
const destination = join(root, 'skills', 'test-skill')
await mkdir(join(root, 'skills'), { recursive: true })
await writeFile(destination, 'unowned regular file')
const result = await installLocalSkillPackage(installInput(root, archive))
expect(result).toMatchObject({ status: 'conflict', conflict: { kind: 'name-collision' } })
expect(await readFile(destination, 'utf8')).toBe('unowned regular file')
})
it('rejects a case-insensitive sibling collision before destination mutation', async () => {
const root = await temporaryDirectory()
const archive = await packageVersion(root, 'version_1', '# Cloud')
@@ -272,6 +285,25 @@ describe('skill install transaction', () => {
}
})
it('serializes simultaneous installs without duplicate provenance or staging bytes', async () => {
const root = await temporaryDirectory()
const archive = await packageVersion(root, 'version_1', '# Concurrent')
const results = await Promise.all([
installLocalSkillPackage(installInput(root, archive, { operationId: 'concurrent-a' })),
installLocalSkillPackage(installInput(root, archive, { operationId: 'concurrent-b' }))
])
expect(results.map((result) => result.status).sort()).toEqual(['installed', 'unchanged'])
expect(await readFile(join(root, 'skills', 'test-skill', 'SKILL.md'), 'utf8')).toContain(
'# Concurrent'
)
expect((await readdir(join(root, 'skills'))).filter((name) => name.includes('.orca-'))).toEqual(
[]
)
expect(await readdir(join(root, 'state', 'receipts'))).toHaveLength(1)
})
it('invalidates a plan when destination state changes immediately before commit', async () => {
const root = await temporaryDirectory()
const archive = await packageVersion(root, 'version_1', '# Cloud')
@@ -1,4 +1,4 @@
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
import { lstat, mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
import type * as NodeFsPromises from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
@@ -23,6 +23,48 @@ afterEach(async () => {
})
describe('skill provider placement reconciliation', () => {
it.each(['file', 'directory'] as const)(
'preserves an unowned provider %s destination',
async (kind) => {
const root = await mkdtemp(join(tmpdir(), 'orca-skill-placement-test-'))
temporaryDirectories.push(root)
const canonicalPath = join(root, 'canonical', 'private-skill')
const providerRoot = join(root, 'provider')
const destinationPath = join(providerRoot, 'private-skill')
await mkdir(canonicalPath, { recursive: true })
await writeFile(join(canonicalPath, 'SKILL.md'), 'private skill')
await mkdir(providerRoot)
if (kind === 'file') {
await writeFile(destinationPath, 'unowned file')
} else {
await mkdir(destinationPath)
await writeFile(join(destinationPath, 'SKILL.md'), 'unowned directory')
}
const observed = await nativeSkillInstallFilesystem.observeSkill(canonicalPath)
const result = await reconcileSkillProviderPlacement({
canonicalPath,
skillName: 'private-skill',
destination: { provider: 'claude', rootPath: providerRoot, readsCanonicalRoot: false },
previousReceipt: null,
packageDigest: observed.observedDigest
})
expect(result).toMatchObject({
topology: 'independent-copy',
status: 'skipped',
errorCategory: 'skill-placement-unowned'
})
const destinationStat = await lstat(destinationPath)
expect(kind === 'file' ? destinationStat.isFile() : destinationStat.isDirectory()).toBe(true)
if (kind === 'file') {
expect(await readFile(destinationPath, 'utf8')).toBe('unowned file')
} else {
expect(await readFile(join(destinationPath, 'SKILL.md'), 'utf8')).toBe('unowned directory')
}
}
)
it('creates a verified independent copy when native alias creation is denied', async () => {
const root = await mkdtemp(join(tmpdir(), 'orca-skill-placement-test-'))
temporaryDirectories.push(root)