Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9a5af9e
feat(e2e): add isolated Claude Migrator adapter
tobyhede Nov 11, 2026
fbba666
merge(e2e): integrate PostgreSQL harness into Claude adapter
tobyhede Nov 11, 2026
73c5e7a
chore(e2e): integrate reviewed snapshot failure classification
tobyhede Nov 11, 2026
fab51cf
docs(e2e): keep real agent runs outside CI
tobyhede Nov 11, 2026
bbe2262
docs(e2e): record acceptance of the migration harness design
tobyhede Nov 11, 2026
32e72f9
chore(e2e): integrate PostgreSQL Oracle review fixes
tobyhede Nov 11, 2026
ad4e5a3
chore(e2e): merge the strace tracee attribution fix
tobyhede Nov 11, 2026
ad98b38
chore(e2e): merge the snapshot classification review fixes
tobyhede Nov 11, 2026
60db099
feat(e2e): let the Claude adapter authenticate with a subscription token
tobyhede Nov 11, 2026
29e7d11
chore(e2e): merge the npm 12 Stack resolution fix
tobyhede Nov 11, 2026
ef5a84d
chore(e2e): merge the isolation Git ownership fix
tobyhede Nov 11, 2026
a182a53
chore(e2e): merge the held-descriptor observer fix
tobyhede Nov 11, 2026
d6feaca
chore(e2e): merge the PostgreSQL migrate entry point
tobyhede Nov 11, 2026
88e34ee
fix(e2e): retain private broker response evidence
tobyhede Nov 11, 2026
c9eb076
chore(e2e): integrate installed-copy validation into agent runs
tobyhede Nov 11, 2026
70b64d8
chore(e2e): integrate the completed migration audit stack
tobyhede Nov 11, 2026
6a11ab1
chore(e2e): integrate supervisor frame compatibility
tobyhede Nov 11, 2026
42560b0
chore(e2e): integrate tracee lifetime repair into agent runs
tobyhede Nov 12, 2026
e84bfaf
chore(e2e): integrate socket error classification into agent runs
tobyhede Nov 12, 2026
45facd5
chore(e2e): integrate friction schema into agent runs
tobyhede Nov 12, 2026
5261d3a
fix(e2e): direct migrators to the staged friction schema
tobyhede Nov 12, 2026
e8e7f61
fix(e2e): address review findings on the Claude migration adapter
tobyhede Nov 12, 2026
5afa803
chore(e2e): integrate explicit process tracing fix
tobyhede Nov 12, 2026
7e12eaa
chore(e2e): integrate traced actor identity repair
tobyhede Nov 12, 2026
1d6a18a
Merge remote-tracking branch 'origin/feat/migration-harness-agent' in…
tobyhede Nov 12, 2026
18287cb
chore(e2e): integrate reviewed Oracle fixes into the agent adapter
tobyhede Nov 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 160 additions & 0 deletions e2e/migration-harness/__tests__/broker-evidence.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import { mkdtemp, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, expect, test } from 'vitest'
import { requiredOracleChecks } from '../run/migration-checks.mjs'
import { runMigrator } from '../run/migrator.mjs'
import { run } from '../run/run.mjs'
import type { Manifest } from '../types.js'

const directories: string[] = []
afterEach(async () => {
for (const directory of directories.splice(0))
await rm(directory, { recursive: true, force: true })
})

async function invoke(
command: string[],
response: { exitCode: number; stdout: string; stderr: string },
maxEvidenceBytes?: number,
) {
const directory = await mkdtemp(join(tmpdir(), 'mh-evidence-test-'))
directories.push(directory)
const adapter = join(directory, 'adapter.mjs')
await writeFile(
adapter,
`
console.log(JSON.stringify(${JSON.stringify({ id: 'request-1', method: 'exec', command })}));
process.stdin.once('data', () => process.exit(0));
`,
)
return runMigrator({
command: `node '${adapter}'`,
execute: async () => response,
stop: async () => {},
timeoutMs: 2000,
maxEvidenceBytes,
})
}

test('broker retains responses with the command and request identity for private evidence', async () => {
const command = ['npm', 'view', '@cipherstash/stack', 'readme']
const response = {
exitCode: 0,
stdout: 'DOCUMENTATION-RESPONSE-CANARY',
stderr: 'DIAGNOSTIC-RESPONSE-CANARY',
}
const result = await invoke(command, response)
expect(result.exitCode).toBe(0)
expect(result).toMatchObject({
brokerEvidence: [{ id: 'request-1', command, result: response }],
})
// Raw command responses must not silently become inline Editor evidence.
expect(result.toolCalls).toEqual([{ id: 'request-1', exitCode: 0 }])
})

test('explicit documentation retrieval is flagged for evidence review', async () => {
const result = await invoke(['npm', 'view', '@cipherstash/stack', 'readme'], {
exitCode: 0,
stdout: 'package instructions',
stderr: '',
})
expect(result).toMatchObject({
documentationExposure: { status: 'observed' },
})
})

// `npm view readme version` asks for the `version` field of a package named
// `readme`. Only a requested readme field, answered with content, is exposure.
test.each([
[['npm', 'view', 'readme', 'version'], 'stack-docs-1.0.0', 'unknown'],
[['npm', 'view', 'readme'], '{"name":"readme"}', 'unknown'],
[['npm', 'view', '@cipherstash/stack', 'readme'], '', 'unknown'],
[
['npm', 'info', '--json', '@cipherstash/stack', 'readme'],
'docs',
'observed',
],
])(
'documentation exposure from %j answering %j is %s',
async (command, stdout, status) => {
const result = await invoke(command, { exitCode: 0, stdout, stderr: '' })
expect(result.documentationExposure?.status).toBe(status)
},
)

test('an arbitrary shell command cannot establish the absence of supplementary documentation', async () => {
const result = await invoke(['sh', '-c', 'node inspect.mjs'], {
exitCode: 0,
stdout: 'application output',
stderr: '',
})
expect(result).toMatchObject({ documentationExposure: { status: 'unknown' } })
})

test('evidence retention has a byte budget and never labels incomplete records complete', async () => {
const result = await invoke(
['true'],
{ exitCode: 0, stdout: 'x'.repeat(100), stderr: '' },
64,
)
expect(result).toMatchObject({
brokerEvidence: [],
documentationExposure: {
status: 'unknown',
evidenceComplete: false,
reviewRequired: true,
},
})
})

test.each(['openat', 'stat'])(
'documentation exposure distinguishes a successful %s from metadata inspection',
async (operation) => {
const manifest: Manifest = {
guideHash: 'guide',
documentationExposure: {
status: 'unknown',
reviewRequired: true,
evidenceComplete: true,
},
}
let sent = false
const report = await run({
id: 'docs',
manifest,
driver: {
provision: async () => {},
installUnmigrated: async () => {},
initializeLegacyData: async () => {},
snapshot: async () => {},
migrate: async () => ({ exitCode: 0 }),
friction: async () => [],
installAppHost: async () => {},
behaviour: async () =>
requiredOracleChecks.map((id) => ({ id, passed: true })),
},
supervision: {
stop: async () => {},
drain: () => {
if (sent) return []
sent = true
return [
{
actor: 'migrator',
stage: 'command',
operation,
resource: '/workspace/node_modules/example/README.md',
decision: 'allowed',
result: 'success',
},
]
},
},
})
expect(report.manifest.documentationExposure?.status).toBe(
operation === 'openat' ? 'possible' : 'unknown',
)
expect(report.manifest.documentationExposure?.reviewRequired).toBe(true)
},
)
Loading
Loading