mirror of
https://github.com/stablyai/orca.git
synced 2026-09-24 16:02:41 +00:00
* fix(terminal): retire explicitly closed pending split connections * test(memory): keep pending split proof compatible with formatted source * fix(terminal): confirm pending split retirement before stopping work * fix(terminal): restore the pending split-close gates CI checks Three CI gates were red on this branch and all three were this branch's own. The hook-order parity snapshot did not count the `confirmedCloseRef` this branch adds to `use-terminal-pane-close-actions.ts`. Dumping the flattened order against clean `main` shows exactly one added `useRef` at position 148 and no reordering, so the count moves 211 -> 212 and the digest with it. `pending-split-close-test-fixture.ts` is Vitest support code, but it sits outside the `*.test` / `*.spec` / `tests` globs that already switch `anti-slop/no-module-mocking` off, so the gate failed on all twelve of its `vi.mock` calls. It carries a file-scoped disable with the reason, matching `work-item-search-test-harness.ts`. `fix.patch` still described the pre-confirmation shape of the close hook, so `reproduce.mjs` aborted with `Source changed` and the cited ablation could not run at this head. Regenerated against the committed sources; the harness again reports 10 pass / 14 fail before and 24 pass / 0 fail after. Merges `main` rather than rebasing: #21005 is stacked on this branch. --------- Co-authored-by: m4air <m4air@Mac.localdomain> Co-authored-by: Neil <neil@stably.ai>
153 lines
4.9 KiB
JavaScript
153 lines
4.9 KiB
JavaScript
import { createHash } from 'node:crypto'
|
|
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
|
|
import { createRequire } from 'node:module'
|
|
import { tmpdir } from 'node:os'
|
|
import { join, resolve } from 'node:path'
|
|
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
import { applyPatch, parsePatch, reversePatch } from 'diff'
|
|
import { build } from 'esbuild'
|
|
|
|
if (process.env.ORCA_BACKGROUND_LAUNCH !== '1') {
|
|
throw new Error('Run with ORCA_BACKGROUND_LAUNCH=1.')
|
|
}
|
|
|
|
const root = fileURLToPath(new URL('../../../', import.meta.url))
|
|
const patch = await readFile(new URL('./fix.patch', import.meta.url), 'utf8')
|
|
const beforeSources = {}
|
|
const sourceHashes = {}
|
|
for (const parsed of parsePatch(patch)) {
|
|
const path = parsed.newFileName.replace(/^b\//, '')
|
|
const absolute = resolve(root, path)
|
|
const current = await readFile(absolute, 'utf8')
|
|
const before = applyPatch(current, reversePatch(parsed))
|
|
if (before === false) {
|
|
throw new Error(`Source changed; review the proof patch: ${path}`)
|
|
}
|
|
beforeSources[absolute.replaceAll('\\', '/')] = before
|
|
sourceHashes[path] = {
|
|
before: createHash('sha256').update(before).digest('hex'),
|
|
after: createHash('sha256').update(current).digest('hex')
|
|
}
|
|
}
|
|
|
|
for (const path of [
|
|
'src/renderer/src/components/terminal-pane/retire-unbound-ipc-terminal-pane.ts',
|
|
'src/renderer/src/components/terminal-pane/pending-split-close-test-fixture.ts',
|
|
'src/renderer/src/components/terminal-pane/pending-split-close.test.ts',
|
|
'docs/audits/pending-split-close/daemon-proof.test.ts'
|
|
]) {
|
|
sourceHashes[path] = {
|
|
current: createHash('sha256')
|
|
.update(await readFile(resolve(root, path)))
|
|
.digest('hex')
|
|
}
|
|
}
|
|
|
|
const scratch = await mkdtemp(join(tmpdir(), 'orca-pending-split-close-'))
|
|
const require = createRequire(import.meta.url)
|
|
let runnerModuleId
|
|
try {
|
|
const runnerPath = join(scratch, 'run-process.cjs')
|
|
await build({
|
|
absWorkingDir: root,
|
|
entryPoints: [resolve(root, 'src/shared/child-process/run-process.ts')],
|
|
outfile: runnerPath,
|
|
bundle: true,
|
|
platform: 'node',
|
|
format: 'cjs',
|
|
logLevel: 'silent'
|
|
})
|
|
runnerModuleId = require.resolve(runnerPath)
|
|
const { runProcess } = require(runnerModuleId)
|
|
const baselineConfig = join(scratch, 'before.config.mjs')
|
|
const fixedConfig = join(scratch, 'after.config.mjs')
|
|
const includes = [
|
|
'src/renderer/src/components/terminal-pane/pending-split-close.test.ts',
|
|
'docs/audits/pending-split-close/daemon-proof.test.ts'
|
|
]
|
|
const configImport = JSON.stringify(pathToFileURL(resolve(root, 'config/vitest.config.ts')).href)
|
|
await writeFile(
|
|
baselineConfig,
|
|
`import base from ${configImport};
|
|
const beforeSources = ${JSON.stringify(beforeSources)};
|
|
export default {...base, test: {...base.test, include: ${JSON.stringify(includes)}}, plugins: [{
|
|
name: 'pending-split-close-before-fix', enforce: 'pre',
|
|
transform(_code, id) {
|
|
const before = beforeSources[id.replaceAll('\\\\', '/').split('?')[0]];
|
|
return before === undefined ? null : {code: before, map: null};
|
|
}
|
|
}]};\n`
|
|
)
|
|
|
|
await writeFile(
|
|
fixedConfig,
|
|
`import base from ${configImport};\nexport default {...base, test: {...base.test, include: ${JSON.stringify(includes)}}};\n`
|
|
)
|
|
|
|
async function run(label, config) {
|
|
const report = join(scratch, `${label}.json`)
|
|
const result = await runProcess({
|
|
program: process.execPath,
|
|
args: [
|
|
resolve(root, 'node_modules/vitest/vitest.mjs'),
|
|
'run',
|
|
'--config',
|
|
config,
|
|
'--reporter=json',
|
|
`--outputFile=${report}`
|
|
],
|
|
cwd: root,
|
|
env: process.env,
|
|
timeoutMs: 90_000,
|
|
maxOutputBytes: 4 * 1024 * 1024
|
|
})
|
|
let parsed
|
|
try {
|
|
parsed = JSON.parse(await readFile(report, 'utf8'))
|
|
} catch (error) {
|
|
throw new Error(`${label} runner failed: ${result.stderr || result.stdout}`, { cause: error })
|
|
}
|
|
return {
|
|
exitCode: result.code,
|
|
passed: parsed.numPassedTests,
|
|
failed: parsed.numFailedTests,
|
|
failedCases: parsed.testResults.flatMap((suite) =>
|
|
suite.assertionResults
|
|
.filter((test) => test.status === 'failed')
|
|
.map((test) => test.fullName)
|
|
)
|
|
}
|
|
}
|
|
|
|
const before = await run('before', baselineConfig)
|
|
const after = await run('after', fixedConfig)
|
|
const passed =
|
|
before.failed === 14 &&
|
|
before.passed === 10 &&
|
|
before.passed + before.failed === 24 &&
|
|
after.passed === 24 &&
|
|
after.failed === 0
|
|
console.log(
|
|
JSON.stringify(
|
|
{
|
|
comparison:
|
|
'Actual split close/IPC transport and temporary daemon socket tests; before reverses only fix.patch in a temporary Vite transform',
|
|
sourceHashes,
|
|
before,
|
|
after,
|
|
passed
|
|
},
|
|
null,
|
|
2
|
|
)
|
|
)
|
|
if (!passed) {
|
|
process.exitCode = 1
|
|
}
|
|
} finally {
|
|
if (runnerModuleId) {
|
|
delete require.cache[runnerModuleId]
|
|
}
|
|
await rm(scratch, { recursive: true, force: true })
|
|
}
|