mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 09:07:40 +00:00
* Layout groundwork (switched off): main derives agent tab titles from its own hook status With the layout-owner switch on, main gives agent tabs their generated titles from the hook-server status it owns, on the desktop, headless serve and orcad: one renameTabs per status batch, so one write and one frame. It keeps the window's rules (setting, custom and quick-command titles, first title wins, dispatch labels). The window stops sending generated titles for tabs main holds; a paired server's tab, or one main has no tab-bar entry for, is still titled by the window as today. The title rules move to src/shared (agent-row-primary-text, and the new agent-generated-tab-title-rules) so both sides share them; switch-off behaviour is unchanged. * feat(layout): main applies a phone's or paired client's tab move itself, and a tab move names the tab it follows so a stale index can't misplace it (layout-owner build, L193) * fix(layout): a move in a group showing a tab main lacks goes the window's way, a twin's agent tab keeps its window title, and orcad subscribes titles through the store attach (layout-owner build) * chore(layout): main's tab move reaches the old-client translation through its compat entry * chore(layout): mark the window-only-row move fallback as bridge code (ledger L280) * test(layout): attaching the hook store follows its rows for main's tab titles once * chore(layout): the old-client move translation is a compat entry of its own, with no cyclic re-export * test(config): the boundary message test names the old-client folder's configured entries
79 lines
3.2 KiB
JavaScript
79 lines
3.2 KiB
JavaScript
import { afterEach, expect, it, vi } from 'vitest'
|
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
|
import { tmpdir } from 'node:os'
|
|
import path from 'node:path'
|
|
import { COMPAT_FOLDERS } from './check-compat-boundary-imports.mjs'
|
|
import { main } from './check-import-boundaries.mjs'
|
|
import { collectModuleSpecifiers } from './static-module-specifiers.mjs'
|
|
|
|
const roots = []
|
|
afterEach(() => {
|
|
vi.restoreAllMocks()
|
|
for (const root of roots.splice(0)) {
|
|
rmSync(root, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
function fixture(files = {}) {
|
|
const root = mkdtempSync(path.join(tmpdir(), 'orca-import-boundaries-'))
|
|
roots.push(root)
|
|
for (const [file, contents] of Object.entries({
|
|
'src/packages/process-host/package.json': JSON.stringify({ exports: { '.': './index.ts' } }),
|
|
'src/packages/process-host/src/index.ts': 'export {}',
|
|
'config/process-host-direct-import-baseline.txt': '',
|
|
...files
|
|
})) {
|
|
const target = path.join(root, file)
|
|
mkdirSync(path.dirname(target), { recursive: true })
|
|
writeFileSync(target, contents)
|
|
}
|
|
return root
|
|
}
|
|
|
|
it('parses candidate files once and reports violations from both guards', () => {
|
|
vi.spyOn(console, 'log').mockImplementation(() => {})
|
|
const error = vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
const parseSpecifiers = vi.fn(collectModuleSpecifiers)
|
|
const root = fixture({
|
|
'src/main/consumer.ts': `import '@orca/process-host/private'
|
|
const entry = '../shared/workspace-layout/old-client/inner'
|
|
require(entry)`
|
|
})
|
|
expect(main(root, { parseSpecifiers })).toBe(1)
|
|
expect(
|
|
parseSpecifiers.mock.calls.filter(([file]) => file === 'src/main/consumer.ts')
|
|
).toHaveLength(1)
|
|
expect(error).toHaveBeenCalledWith(expect.stringContaining('not a public process-host export'))
|
|
const oldClient = COMPAT_FOLDERS.find(({ folder }) => folder.endsWith('/old-client'))
|
|
// The message names the folder's entry modules, whichever they are.
|
|
expect(error).toHaveBeenCalledWith(
|
|
expect.stringContaining(`through ${oldClient.entries.join(', ')}, not`)
|
|
)
|
|
})
|
|
|
|
it('uses fresh inputs on every invocation, including newly added files', () => {
|
|
vi.spyOn(console, 'log').mockImplementation(() => {})
|
|
vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
const root = fixture()
|
|
expect(main(root)).toBe(0)
|
|
mkdirSync(path.join(root, 'src/main'), { recursive: true })
|
|
writeFileSync(path.join(root, 'src/main/consumer.ts'), "import 'node:child_process'")
|
|
expect(main(root)).toBe(1)
|
|
})
|
|
|
|
it('does not cache a file across an edit at the same path', () => {
|
|
vi.spyOn(console, 'log').mockImplementation(() => {})
|
|
vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
const root = fixture({ 'src/main/consumer.ts': "import '@orca/process-host'" })
|
|
expect(main(root)).toBe(0)
|
|
writeFileSync(path.join(root, 'src/main/consumer.ts'), "import '@orca/process-host/private'")
|
|
expect(main(root)).toBe(1)
|
|
})
|
|
|
|
it('rejects malformed source and missing package implementations', () => {
|
|
expect(() => main(fixture({ 'src/main/broken.ts': 'import {' }))).toThrow('Cannot parse')
|
|
const root = fixture()
|
|
rmSync(path.join(root, 'src/packages/process-host/src'), { recursive: true })
|
|
expect(() => main(root)).toThrow('Missing process-host implementation')
|
|
})
|