feat(beehive): add the Harnesses tracer

Signed-off-by: Logan Johnson <loganj@squareup.com>
This commit is contained in:
Logan Johnson
2026-09-21 10:47:15 -04:00
parent e765229a16
commit d1c4c930b9
9 changed files with 508 additions and 18 deletions

View File

@@ -7,7 +7,7 @@ import { spawnOwned } from './owned.ts';
/** Public capabilities, never login evidence. Non-Buzz adapters remain visible,
* but cannot consume this catalog's OS-backed provider contract without an adapter. */
export type DetectedHarness = { id: string; label: string; executable?: string; cli?: string; state: 'available' | 'not-installed' | 'cli-missing' | 'incompatible'; providers: string[]; reason: string };
export type DetectedHarness = { id: string; label: string; executable?: string; cli?: string; version?: string; state: 'available' | 'not-installed' | 'cli-missing' | 'incompatible'; providers: string[]; reason: string };
const executable = (path: string) => { try { const p = realpathSync(path); accessSync(p, constants.X_OK); return statSync(p).isFile() ? p : undefined; } catch { return undefined; } };
const version = (s: string) => { const last = s.trim().split(/\s+/).at(-1) ?? ''; return /^\d+\.\d+\.\d+$/.test(last) ? last.split('.').map(BigInt) : undefined; };
/** Desktop's strict adapter floor: unknown/prerelease/partial versions fail closed. */
@@ -76,11 +76,17 @@ export async function discoverHarnesses(signal: AbortSignal, options: { home?: s
for (const command of row.commands) { binary = await resolve(command); if (binary) break; }
const cli = row.underlyingCli ? await resolve(row.underlyingCli) : undefined;
let state: DetectedHarness['state'] = !binary ? 'not-installed' : row.underlyingCli && !cli ? 'cli-missing' : 'available';
if (state === 'available' && row.id === 'codex' && !compatibleCodex(await probe(binary!, ['--version'], [...managed, path].join(delimiter), home, signal) ?? '')) state = 'incompatible';
let detectedVersion: string | undefined;
if (state === 'available') {
const output = await probe(binary!, ['--version'], [...managed, path].join(delimiter), home, signal);
detectedVersion = output?.trim().split(/\s+/).at(-1);
if (!detectedVersion || !version(detectedVersion)) detectedVersion = undefined;
if (row.id === 'codex' && !compatibleCodex(output ?? '')) state = 'incompatible';
}
signal.throwIfAborted();
const providers = state === 'available' && ['buzz-agent','pi'].includes(row.id) ? row.id === 'buzz-agent' ? ['openai', 'anthropic', 'openai-compat', 'openrouter', 'databricks_v2'] : ['openai', 'databricks_v2'] : state === 'available' && row.id === 'codex' ? ['openai'] : [];
const reason = state === 'not-installed' ? 'Not installed' : state === 'cli-missing' ? `Underlying ${row.underlyingCli} CLI not installed` : state === 'incompatible' ? 'Codex ACP 1.10.0 or newer required; version unknown/outdated' : providers.length ? 'Detected; provider/model access unverified' : 'Detected; this catalog’s OS-backed providers are not supported by this adapter. Use existing local setup for native authentication.';
result.push({ id: row.id, label: row.label!, executable: binary, cli, state, providers, reason });
result.push({ id: row.id, label: row.label!, executable: binary, cli, ...(detectedVersion ? { version: detectedVersion } : {}), state, providers, reason });
}
return result;
}

View File

@@ -0,0 +1,100 @@
import { homedir } from 'node:os';
import { join } from 'node:path';
import { discoverHarnesses, type DetectedHarness } from './harness-discovery.ts';
import { readSettings, saveSettings, type Settings } from './settings.ts';
export type HarnessInventoryPhase = 'idle' | 'refreshing' | 'error';
export type HarnessInventorySnapshot = Readonly<{
harnesses: readonly DetectedHarness[];
savedConfigurations: Readonly<Record<string, number>>;
phase: HarnessInventoryPhase;
message: string;
generation: number;
}>;
export type HarnessInventoryStore = {
read(): Settings;
save(candidate: Settings, expectedRevision: number): Settings;
};
export type HarnessInventoryDiscovery = (signal: AbortSignal) => Promise<DetectedHarness[]>;
const counts = (settings: Settings) => Object.freeze(settings.runtimes.reduce<Record<string, number>>((result, runtime) => {
result[runtime.harness] = (result[runtime.harness] ?? 0) + 1;
return result;
}, {}));
/** Harness-only controller. It owns durable inventory refresh without constructing
* owner, relay, Host, provider, credential, or agent state. */
export class HarnessInventoryController {
private current: HarnessInventorySnapshot;
private active?: AbortController;
private serial = 0;
private disposed = false;
private listener?: (snapshot: HarnessInventorySnapshot) => void;
constructor(private readonly store: HarnessInventoryStore, private readonly discover: HarnessInventoryDiscovery) {
try {
const settings = store.read();
this.current = Object.freeze({ harnesses: Object.freeze([...(settings.harnesses ?? [])]), savedConfigurations: counts(settings), phase: 'idle', message: settings.harnesses ? 'Saved local harness inventory.' : 'No saved harness inventory. Run Refresh harnesses.', generation: 0 });
} catch {
this.current = Object.freeze({ harnesses: Object.freeze([]), savedConfigurations: Object.freeze({}), phase: 'error', message: 'Saved harness inventory could not be read. Repair the local settings file, then try again.', generation: 0 });
}
}
snapshot() { return this.current; }
subscribe(listener: (snapshot: HarnessInventorySnapshot) => void) { this.listener = listener; listener(this.current); return () => { if (this.listener === listener) this.listener = undefined; }; }
private publish(next: Omit<HarnessInventorySnapshot, 'generation'>) {
if (this.disposed) return;
this.current = Object.freeze({ ...next, harnesses: Object.freeze([...next.harnesses]), savedConfigurations: Object.freeze({ ...next.savedConfigurations }), generation: this.serial });
this.listener?.(this.current);
}
/** One bounded refresh at a time. Completion is fenced before durable commit
* and again before presentation, so leaving the screen or disposal wins. */
async refresh(): Promise<boolean> {
if (this.disposed || this.active) return false;
const abort = new AbortController();
this.active = abort;
const generation = ++this.serial;
this.publish({ ...this.current, phase: 'refreshing', message: 'Refreshing local harness inventory…' });
try {
const harnesses = await this.discover(abort.signal);
if (this.disposed || abort.signal.aborted || generation !== this.serial) return false;
const previous = this.store.read();
if (this.disposed || abort.signal.aborted || generation !== this.serial) return false;
const committed = this.store.save({ ...previous, harnesses: [...harnesses] }, previous.revision);
if (this.disposed || abort.signal.aborted || generation !== this.serial) return false;
this.publish({ harnesses, savedConfigurations: counts(committed), phase: 'idle', message: `Harness inventory refreshed. ${harnesses.length} harnesses checked.` });
return true;
} catch (error) {
if (this.disposed || abort.signal.aborted || generation !== this.serial) return false;
const changed = error instanceof Error && error.message === 'Settings changed. Open the form again.';
this.publish({ ...this.current, phase: 'error', message: changed ? 'Local settings changed during refresh. Existing inventory was kept; try Refresh harnesses again.' : 'Harness refresh failed. Existing inventory was kept; check local executable access and try again.' });
return false;
} finally {
if (this.active === abort) this.active = undefined;
}
}
cancel() {
if (!this.active) return;
this.serial++;
this.active.abort();
this.active = undefined;
this.publish({ ...this.current, phase: 'idle', message: 'Harness refresh stopped. Existing inventory was kept.' });
}
dispose() { if (this.disposed) return; this.disposed = true; this.serial++; this.active?.abort(); this.active = undefined; this.listener = undefined; }
}
/** Production adapter. BEEHIVE_HOME and the isolated switch are explicit seams
* for product-shaped tests; normal launch uses the ordinary local HOME/PATH. */
export function localHarnessInventory(environment: NodeJS.ProcessEnv = process.env) {
const isolated = environment.BEEHIVE_HARNESS_ISOLATED === '1';
if (isolated && (!environment.BEEHIVE_HOME || environment.BEEHIVE_HARNESS_PATH === undefined)) throw Error('Isolated harness discovery requires an explicit BEEHIVE_HOME and BEEHIVE_HARNESS_PATH');
const home = environment.BEEHIVE_HOME || homedir();
const path = environment.BEEHIVE_HARNESS_PATH ?? environment.PATH ?? '';
const directory = join(home, '.beehive', 'host');
return new HarnessInventoryController(
{ read: () => readSettings(directory), save: (candidate, revision) => saveSettings(directory, candidate, revision) },
signal => discoverHarnesses(signal, { home, path, ...(isolated ? { bundled: [], common: [], loginShells: [] } : {}) }),
);
}

View File

@@ -1,5 +1,6 @@
import { createCliRenderer } from '@opentui/core';
import { OpenTuiShell } from './opentui-shell.ts';
import { localHarnessInventory } from './harness-inventory.ts';
const renderer = await createCliRenderer({
exitOnCtrlC: false,
@@ -7,7 +8,7 @@ const renderer = await createCliRenderer({
consoleMode: 'disabled',
openConsoleOnError: false,
});
const shell = new OpenTuiShell(renderer);
const shell = new OpenTuiShell(renderer, localHarnessInventory());
const stop = () => shell.close();
process.once('SIGINT', stop);
process.once('SIGTERM', stop);

View File

@@ -1,5 +1,6 @@
import { BoxRenderable, ScrollBoxRenderable, TextRenderable, type CliRenderer, type KeyEvent } from '@opentui/core';
import { brand, destinations, listWidth, ownerLabel, ShellState, type FocusRegion } from './shell-state.ts';
import { HarnessInventoryController, type HarnessInventorySnapshot } from './harness-inventory.ts';
export const palette = {
surface: '#0d100e', text: '#dce1d8', muted: '#7d867c', divider: '#4a5149',
@@ -61,6 +62,14 @@ export class OpenTuiShell {
private readonly detailPane: BoxRenderable;
private readonly divider: TextRenderable;
private readonly focusPerimeter: BoxRenderable;
private readonly listTitle: TextRenderable;
private readonly detailTitle: TextRenderable;
private readonly detailText: TextRenderable;
private readonly detailAction: TextRenderable;
private readonly listRows: TextRenderable[] = [];
private inventorySnapshot: HarnessInventorySnapshot;
private unsubscribeInventory: () => void;
private screenGeneration = 0;
private readonly footerRule: TextRenderable;
private readonly footer: TextRenderable;
private helpScrim?: BoxRenderable;
@@ -70,7 +79,8 @@ export class OpenTuiShell {
private helpTitle?: TextRenderable;
private helpActions?: TextRenderable;
constructor(readonly renderer: CliRenderer, private readonly helpContent = helpBody) {
constructor(readonly renderer: CliRenderer, private readonly inventory: HarnessInventoryController, private readonly helpContent = helpBody) {
this.inventorySnapshot = inventory.snapshot();
this.done = new Promise(resolve => { this.finish = resolve; });
this.root = new BoxRenderable(renderer, { width: '100%', height: '100%', flexDirection: 'column', backgroundColor: palette.surface });
renderer.root.add(this.root);
@@ -95,8 +105,29 @@ export class OpenTuiShell {
this.root.add(this.body);
this.listPane = this.pane('list'); this.detailPane = this.pane('detail');
this.divider = new TextRenderable(renderer, { width: 1, height: '100%', fg: palette.divider, content: '│' });
this.focusPerimeter = new BoxRenderable(renderer, { position: 'absolute', visible: false, border: true, borderColor: palette.focus, backgroundColor: 'transparent' });
this.focusPerimeter = new BoxRenderable(renderer, { position: 'absolute', visible: false, border: true, borderColor: palette.focus, backgroundColor: 'transparent',
onMouseDown: event => {
if (this.state.focus === 'list') this.clickListSlot(Math.floor((event.y - 6) / 2));
else if (this.state.focus === 'detail' && event.y === 15) this.activateSelectedRow();
},
onMouseScroll: event => {
if (this.state.focus !== 'list') return;
if (event.scroll?.direction === 'up') this.state.key('up');
else if (event.scroll?.direction === 'down') this.state.key('down');
this.paint();
} });
this.body.add(this.listPane); this.body.add(this.divider); this.body.add(this.detailPane); this.body.add(this.focusPerimeter);
this.listTitle = new TextRenderable(renderer, { position: 'absolute', left: 2, top: 1, height: 1, fg: palette.muted, content: 'LIST' });
this.detailTitle = new TextRenderable(renderer, { position: 'absolute', left: 2, top: 1, height: 1, fg: palette.muted, content: 'DETAILS' });
this.detailText = new TextRenderable(renderer, { position: 'absolute', left: 2, top: 3, width: '90%', height: '80%', fg: palette.text, wrapMode: 'word', content: '' });
this.detailAction = new TextRenderable(renderer, { position: 'absolute', left: 2, top: 12, height: 1, fg: palette.selectedText, bg: palette.selected, content: ' Refresh harnesses ', visible: false,
onMouseDown: () => this.activateSelectedRow() });
this.listPane.add(this.listTitle); this.detailPane.add(this.detailTitle); this.detailPane.add(this.detailText); this.detailPane.add(this.detailAction);
for (let slot = 0; slot < 32; slot++) {
const row = new TextRenderable(renderer, { position: 'absolute', left: 2, top: slot * 2 + 3, height: 2, fg: palette.text, content: '', visible: false,
onMouseDown: () => this.clickListSlot(slot) });
this.listPane.add(row); this.listRows.push(row);
}
this.footerRule = new TextRenderable(renderer, { height: 1, fg: palette.divider }); this.root.add(this.footerRule);
this.footer = new TextRenderable(renderer, { height: 1, fg: palette.muted, content: wideFooter }); this.root.add(this.footer);
@@ -104,11 +135,22 @@ export class OpenTuiShell {
renderer.keyInput.on('keypress', key => this.key(key));
renderer.on('resize', () => this.resize());
renderer.on('destroy', () => this.close(false));
this.unsubscribeInventory = inventory.subscribe(snapshot => {
this.inventorySnapshot = snapshot;
this.state.setHarnessRows([...snapshot.harnesses.map(harness => ({ id: `harness:${harness.id}`, kind: 'harness' as const })), { id: 'command:refresh', kind: 'command' }]);
this.paint();
});
this.resize();
}
private pane(region: Exclude<FocusRegion, 'header'>) {
return new BoxRenderable(this.renderer, { height: '100%', backgroundColor: palette.surface,
onMouseScroll: event => {
if (region !== 'list' || this.state.activeSection !== 2 || !this.visible(region)) return;
if (event.scroll?.direction === 'up') this.state.key('up');
else if (event.scroll?.direction === 'down') this.state.key('down');
this.paint();
},
onMouseDown: () => { if (!this.visible(region)) return; this.state.focus = region; this.paint(); } });
}
@@ -118,15 +160,38 @@ export class OpenTuiShell {
}
private key(key: KeyEvent) {
const wasHarnesses = this.state.mode === 'section' && this.state.activeSection === 2;
const effect = this.state.key(key.name, { ctrl: key.ctrl, shift: key.shift });
if (wasHarnesses && !(this.state.mode === 'section' && this.state.activeSection === 2)) { this.screenGeneration++; this.inventory.cancel(); }
if (effect !== 'none') key.preventDefault();
if (effect === 'quit') this.close();
else if (effect === 'activate') this.activateSelectedRow();
else if (effect === 'render') this.paint();
}
private activate(index: number) {
if (this.state.belowMinimum || this.state.helpOpen) return;
this.state.activateHeader(index); this.paint();
const wasHarnesses = this.state.mode === 'section' && this.state.activeSection === 2;
this.state.activateHeader(index);
if (wasHarnesses && !(this.state.mode === 'section' && this.state.activeSection === 2)) { this.screenGeneration++; this.inventory.cancel(); }
this.paint();
}
private clickListSlot(slot: number) {
if (this.closed || this.state.helpOpen || this.state.activeSection !== 2 || !this.visible('list')) return;
const generation = this.screenGeneration;
const row = this.state.harnessRows[this.state.listOffset + slot];
if (!row || generation !== this.screenGeneration || !this.state.selectHarness(row.id)) return;
this.state.focus = 'list';
this.paint();
}
private activateSelectedRow() {
const row = this.state.selectedHarnessRow;
if (this.state.activeSection !== 2 || row?.id !== 'command:refresh') { this.paint(); return; }
void this.inventory.refresh();
if (!this.state.splitPane) { this.state.focus = 'detail'; this.state.narrowPane = 'detail'; }
this.paint();
}
private resize() {
@@ -161,6 +226,7 @@ export class OpenTuiShell {
} else {
this.listPane.width = '100%'; this.detailPane.width = '100%'; this.detailPane.flexGrow = 1; this.divider.visible = false;
}
this.paintHarnesses();
const focusedPane = this.state.focus === 'list' || this.state.focus === 'detail' ? this.state.focus : undefined;
this.focusPerimeter.visible = Boolean(focusedPane);
if (focusedPane) {
@@ -172,6 +238,41 @@ export class OpenTuiShell {
this.syncHelp();
}
private paintHarnesses() {
const shown = this.state.mode === 'section' && this.state.activeSection === 2;
this.listTitle.visible = shown; this.detailTitle.visible = shown; this.detailText.visible = shown; this.detailAction.visible = false;
for (const row of this.listRows) row.visible = false;
if (!shown) { this.detailText.content = ''; return; }
const bodyHeight = Math.max(1, this.renderer.height - 5);
const capacity = Math.max(1, Math.min(this.listRows.length, Math.floor((bodyHeight - 4) / 2)));
this.state.setListCapacity(capacity);
for (let slot = 0; slot < capacity; slot++) {
const descriptor = this.state.harnessRows[this.state.listOffset + slot];
const renderable = this.listRows[slot]!;
if (!descriptor) continue;
const selected = descriptor.id === this.state.harnessSelection;
const harness = descriptor.kind === 'harness' ? this.inventorySnapshot.harnesses.find(value => `harness:${value.id}` === descriptor.id) : undefined;
const status = harness ? harness.state === 'available' ? 'Available' : harness.state === 'not-installed' ? 'Not installed' : harness.state === 'cli-missing' ? 'CLI missing' : 'Incompatible' : this.inventorySnapshot.phase === 'refreshing' ? 'Refreshing…' : 'Refresh harnesses';
renderable.content = descriptor.kind === 'command' ? `↻ ${status}\n Re-read and save local facts` : `${harness?.label ?? descriptor.id} · ${status}\n ${harness?.reason ?? 'Reason unavailable'}`;
renderable.width = Math.max(1, (this.state.splitPane ? listWidth(this.renderer.width) : this.renderer.width) - 4);
renderable.bg = selected ? palette.selected : palette.surface;
renderable.fg = selected ? palette.selectedText : palette.text;
renderable.visible = true;
}
const selected = this.state.selectedHarnessRow;
if (selected?.kind === 'harness') {
const harness = this.inventorySnapshot.harnesses.find(value => `harness:${value.id}` === selected.id);
if (harness) {
const saved = this.inventorySnapshot.savedConfigurations[harness.id] ?? 0;
this.detailText.content = `${harness.label}\n\nState: ${harness.state}\nReason: ${harness.reason}\nVersion: ${harness.version ?? 'Not established'}\nExecutable: ${harness.executable ? 'Detected' : 'Not established'}\nUnderlying CLI: ${harness.cli ? 'Detected' : 'Not established'}\nProvider integrations: ${harness.providers.join(', ') || 'None'}\nSaved configurations: ${saved}\n\nThis inventory is local to this computer.`;
} else this.detailText.content = 'This harness is no longer in the current inventory.';
} else {
this.detailText.content = `Refresh harnesses\n\nRe-read bounded local executable and version evidence, then save the complete harness inventory.\n\nStatus: ${this.inventorySnapshot.message}\n\nNo owner sign-in, relay, Host, agent, provider, browser, or credential operation is used.`;
this.detailAction.content = this.inventorySnapshot.phase === 'refreshing' ? ' Refresh in progress ' : ' Refresh harnesses ';
this.detailAction.visible = this.state.focus === 'detail' && this.visible('detail');
}
}
private syncHelp() {
if (!this.state.helpOpen) {
if (this.helpScrim) { this.help?.destroyRecursively(); this.helpScrim.destroyRecursively(); this.helpScrim = undefined; this.help = undefined; this.helpBody = undefined; this.helpBodyScrollable = false; this.helpTitle = undefined; this.helpActions = undefined; }
@@ -214,6 +315,8 @@ export class OpenTuiShell {
close(destroy = true) {
if (this.closed) return;
this.closed = true;
this.unsubscribeInventory();
this.inventory.dispose();
if (destroy) this.renderer.destroy();
this.finish();
}

View File

@@ -42,13 +42,14 @@ export function validateSettings(value: Settings): Settings {
if (r.harness === 'codex' ? !r.cli || !isAbsolute(r.cli) || r.effort !== undefined || value.providers.find(p => p.id === r.providerId)?.type !== 'openai' : r.harness === 'pi' ? !['openai','databricks_v2'].includes(value.providers.find(p => p.id === r.providerId)!.type) || !r.cli || !isAbsolute(r.cli) || r.effort !== undefined && !piEfforts(value.providers.find(p => p.id === r.providerId)!.type, r.model).includes(r.effort) : r.cli !== undefined) throw Error('Unsupported runtime provider/CLI/effort combination');
}
for (const r of value.runtimes) validateRuntimeEffort(value.providers.find(p => p.id === r.providerId)!.type, r.model, r.effort);
if (value.harnesses !== undefined && (!Array.isArray(value.harnesses) || value.harnesses.length > 32 || value.harnesses.some(h => typeof h.id !== 'string' || typeof h.label !== 'string' || typeof h.reason !== 'string' || !['available','not-installed','cli-missing','incompatible'].includes(h.state) || !Array.isArray(h.providers) || h.providers.some(p => typeof p !== 'string') || (h.executable !== undefined && !isAbsolute(h.executable)) || (h.cli !== undefined && !isAbsolute(h.cli))))) throw Error('Invalid harness inventory');
if (value.harnesses !== undefined && (!Array.isArray(value.harnesses) || value.harnesses.length > 32 || value.harnesses.some(h => typeof h.id !== 'string' || typeof h.label !== 'string' || typeof h.reason !== 'string' || (h.version !== undefined && (typeof h.version !== 'string' || !/^\d+\.\d+\.\d+$/.test(h.version))) || !['available','not-installed','cli-missing','incompatible'].includes(h.state) || !Array.isArray(h.providers) || h.providers.some(p => typeof p !== 'string') || (h.executable !== undefined && !isAbsolute(h.executable)) || (h.cli !== undefined && !isAbsolute(h.cli))))) throw Error('Invalid harness inventory');
// Reject unknown fields, including accidental secret-bearing input.
const fields = (o: object, allowed: string[]) => { if (Object.keys(o).some(k => !allowed.includes(k))) throw Error('Unexpected settings field'); };
fields(value, ['version','revision','agents','providers','runtimes','harnesses']);
for (const a of value.agents) { fields(a, ['publicKey','key','profile','profileState','runtimeId']); if (a.profile) fields(a.profile, ['relay','name','picture','about']); }
for (const p of value.providers) { fields(p, ['id','name','type','endpoint','key','wire']); fields(p.key, ['service','account']); }
for (const r of value.runtimes) fields(r, ['id','name','harness','executable','providerId','model','effort','cli','environment']);
for (const h of value.harnesses ?? []) fields(h, ['id','label','executable','cli','version','state','providers','reason']);
return structuredClone(value);
}
/** Absent settings means an empty catalog, never reconstructed credentials. */

View File

@@ -5,7 +5,8 @@ export const brand = '⬢ BEEHIVE';
export type FocusRegion = 'header' | 'list' | 'detail';
export type ShellMode = 'section' | 'owner';
export type ShellEffect = 'render' | 'quit' | 'none';
export type ShellEffect = 'render' | 'activate' | 'quit' | 'none';
export type HarnessListRow = Readonly<{ id: string; kind: 'harness' | 'command' }>;
/** Framework-independent state for the never-signed-in shell tracer. */
export class ShellState {
@@ -17,6 +18,10 @@ export class ShellState {
width = 120;
height = 40;
narrowPane: 'list' | 'detail' = 'list';
harnessRows: readonly HarnessListRow[] = [{ id: 'command:refresh', kind: 'command' }];
harnessSelection = 'command:refresh';
listOffset = 0;
listCapacity = 1;
get belowMinimum() { return this.width < 50 || this.height < 20; }
get protectedSection() { return this.activeSection < 2; }
@@ -32,6 +37,43 @@ export class ShellState {
if (this.belowMinimum) this.helpOpen = false;
}
setHarnessRows(rows: readonly HarnessListRow[]) {
const previous = this.harnessSelection;
const previousHarnesses = this.harnessRows.filter(row => row.kind === 'harness');
const previousHarnessIndex = previousHarnesses.findIndex(row => row.id === previous);
const initialCommandOnly = this.harnessRows.length === 1 && this.harnessRows[0]?.id === 'command:refresh';
this.harnessRows = rows.length ? rows : [{ id: 'command:refresh', kind: 'command' }];
if (initialCommandOnly && this.harnessRows[0]?.kind === 'harness') this.harnessSelection = this.harnessRows[0].id;
else if (this.harnessRows.some(row => row.id === previous)) this.harnessSelection = previous;
else {
const harnesses = this.harnessRows.filter(row => row.kind === 'harness');
this.harnessSelection = harnesses[previousHarnessIndex]?.id
?? harnesses[Math.min(previousHarnessIndex - 1, harnesses.length - 1)]?.id
?? this.harnessRows.find(row => row.kind === 'command')?.id
?? this.harnessRows[0]!.id;
}
this.revealHarnessSelection();
}
setListCapacity(capacity: number) { this.listCapacity = Math.max(1, capacity); this.revealHarnessSelection(); }
private revealHarnessSelection() {
const index = Math.max(0, this.harnessRows.findIndex(row => row.id === this.harnessSelection));
if (index < this.listOffset) this.listOffset = index;
if (index >= this.listOffset + this.listCapacity) this.listOffset = index - this.listCapacity + 1;
this.listOffset = Math.max(0, Math.min(this.listOffset, Math.max(0, this.harnessRows.length - this.listCapacity)));
}
selectHarness(id: string) {
if (!this.harnessRows.some(row => row.id === id)) return false;
this.harnessSelection = id; this.revealHarnessSelection(); return true;
}
private moveHarness(delta: number) {
const index = Math.max(0, this.harnessRows.findIndex(row => row.id === this.harnessSelection));
const next = Math.max(0, Math.min(this.harnessRows.length - 1, index + delta));
if (next === index) return false;
this.harnessSelection = this.harnessRows[next]!.id; this.revealHarnessSelection(); return true;
}
get selectedHarnessRow() { return this.harnessRows.find(row => row.id === this.harnessSelection); }
key(name: string, options: { ctrl?: boolean; shift?: boolean } = {}): ShellEffect {
if (options.ctrl && name === 'q') return 'quit';
if (this.belowMinimum) return 'none';
@@ -66,7 +108,9 @@ export class ShellState {
// active destination remain separate: only header activation changes the
// destination, while these transitions only move the focus perimeter.
if (this.collection && this.focus === 'list') {
if (this.activeSection === 2 && name === 'down') return this.moveHarness(1) ? 'render' : 'none';
if (name === 'up') {
if (this.activeSection === 2 && this.moveHarness(-1)) return 'render';
this.focus = 'header'; this.headerIndex = this.activeSection; return 'render';
}
if (name === 'right' || name === 'return') {
@@ -76,6 +120,7 @@ export class ShellState {
}
}
if (this.collection && this.focus === 'detail') {
if (name === 'return' && this.activeSection === 2 && this.selectedHarnessRow?.kind === 'command') return 'activate';
if (name === 'up') {
this.focus = 'header'; this.headerIndex = this.activeSection; return 'render';
}
@@ -86,8 +131,9 @@ export class ShellState {
}
}
if (name === 'tab') {
const harnessAction = this.activeSection === 2 && this.selectedHarnessRow?.kind === 'command';
const regions: FocusRegion[] = this.collection
? this.splitPane ? ['header', 'list', 'detail'] : ['header', this.narrowPane]
? this.splitPane ? harnessAction ? ['header', 'list', 'detail'] : ['header', 'list'] : this.narrowPane === 'list' ? ['header', 'list'] : harnessAction ? ['header', 'detail'] : ['header']
: ['header', 'detail'];
const current = Math.max(0, regions.indexOf(this.focus));
this.focus = regions[(current + (options.shift ? regions.length - 1 : 1)) % regions.length]!;

View File

@@ -0,0 +1,123 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { mkdtempSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync, chmodSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { HarnessInventoryController, localHarnessInventory } from '../src/harness-inventory.ts';
import type { DetectedHarness } from '../src/harness-discovery.ts';
import type { Settings } from '../src/settings.ts';
const row = (id: string, state: DetectedHarness['state'] = 'available'): DetectedHarness => ({ id, label: id.toUpperCase(), state, providers: state === 'available' ? ['openai'] : [], reason: state === 'available' ? 'Detected; authorization unverified' : 'Not installed', ...(state === 'available' ? { executable: `/fixture/${id}` } : {}) });
const fixture = (initial: DetectedHarness[] = []) => {
let settings: Settings = { version: 1, revision: 4, agents: [], providers: [], runtimes: [], harnesses: initial };
let saves = 0;
const store = {
read: () => structuredClone(settings),
save: (candidate: Settings, expected: number) => { assert.equal(expected, settings.revision); saves++; settings = { ...structuredClone(candidate), revision: expected + 1 }; return structuredClone(settings); },
};
return { store, settings: () => settings, saves: () => saves };
};
const deferred = <T>() => { let resolve!: (value: T) => void; let reject!: (error: unknown) => void; const promise = new Promise<T>((yes, no) => { resolve = yes; reject = no; }); return { promise, resolve, reject }; };
test('loads durable local harness facts and saved configuration counts without probing', () => {
const data = fixture([row('codex'), row('pi', 'not-installed')]);
const settings = data.settings();
settings.providers = [{ id: 'provider', name: 'Provider', type: 'openai', endpoint: 'https://api.openai.com/v1', key: { service: 'beehive', account: 'provider' } }];
settings.runtimes = [{ id: 'runtime', name: 'Saved', harness: 'codex', executable: '/fixture/codex', cli: '/fixture/codex-cli', providerId: 'provider', model: 'model' }];
let probes = 0;
const controller = new HarnessInventoryController(data.store, async () => { probes++; return []; });
assert.equal(probes, 0);
assert.deepEqual(controller.snapshot().harnesses.map(value => [value.id, value.state]), [['codex', 'available'], ['pi', 'not-installed']]);
assert.equal(controller.snapshot().savedConfigurations.codex, 1);
controller.dispose();
});
test('refresh commits one complete inventory and rejects a duplicate in-flight request', async () => {
const data = fixture([row('old')]);
const work = deferred<DetectedHarness[]>();
const controller = new HarnessInventoryController(data.store, () => work.promise);
const states: string[] = [];
controller.subscribe(snapshot => states.push(snapshot.phase));
const first = controller.refresh();
assert.equal(await controller.refresh(), false);
assert.equal(controller.snapshot().phase, 'refreshing');
work.resolve([row('codex'), row('pi', 'not-installed')]);
assert.equal(await first, true);
assert.equal(data.saves(), 1);
assert.deepEqual(data.settings().harnesses?.map(value => value.id), ['codex', 'pi']);
assert.deepEqual(states, ['idle', 'refreshing', 'idle']);
controller.dispose();
});
test('refresh failure keeps durable rows and exposes a retryable plain reason', async () => {
const data = fixture([row('retained')]);
const controller = new HarnessInventoryController(data.store, async () => { throw Error('/private/path/token=secret'); });
assert.equal(await controller.refresh(), false);
assert.equal(data.saves(), 0);
assert.equal(controller.snapshot().harnesses[0]?.id, 'retained');
assert.equal(controller.snapshot().phase, 'error');
assert.doesNotMatch(controller.snapshot().message, /private|token|secret/);
assert.match(controller.snapshot().message, /try again/i);
controller.dispose();
});
test('production adapter discovers and durably saves only an explicit isolated HOME/PATH fixture', async () => {
const home = mkdtempSync(join(tmpdir(), 'beehive-harness-inventory-'));
try {
const bin = join(home, 'bin'); mkdirSync(bin);
const executable = join(bin, 'buzz-agent'); writeFileSync(executable, '#!/bin/sh\necho "buzz-agent 2.3.4"\n'); chmodSync(executable, 0o700);
const controller = localHarnessInventory({ BEEHIVE_HOME: home, BEEHIVE_HARNESS_PATH: bin, BEEHIVE_HARNESS_ISOLATED: '1', PATH: bin });
assert.equal(await controller.refresh(), true);
const buzz = controller.snapshot().harnesses.find(value => value.id === 'buzz-agent');
assert.deepEqual({ state: buzz?.state, version: buzz?.version, executable: buzz?.executable, providers: buzz?.providers }, { state: 'available', version: '2.3.4', executable: realpathSync(executable), providers: ['openai', 'anthropic', 'openai-compat', 'openrouter', 'databricks_v2'] });
assert.equal(controller.snapshot().harnesses.find(value => value.id === 'codex')?.state, 'not-installed');
const persisted = JSON.parse(readFileSync(join(home, '.beehive', 'host', 'settings.json'), 'utf8'));
assert.equal(persisted.harnesses.find((value: DetectedHarness) => value.id === 'buzz-agent').executable, realpathSync(executable));
controller.dispose();
rmSync(executable);
const relaunched = localHarnessInventory({ BEEHIVE_HOME: home, BEEHIVE_HARNESS_PATH: bin, BEEHIVE_HARNESS_ISOLATED: '1', PATH: '/ordinary/path-must-not-be-used' });
assert.deepEqual({ state: relaunched.snapshot().harnesses.find(value => value.id === 'buzz-agent')?.state, version: relaunched.snapshot().harnesses.find(value => value.id === 'buzz-agent')?.version }, { state: 'available', version: '2.3.4' }, 'relaunch reads durable inventory without another probe');
relaunched.dispose();
} finally { rmSync(home, { recursive: true, force: true }); }
});
test('isolated production adapter fails closed unless both fixture root and executable path are explicit', () => {
assert.throws(() => localHarnessInventory({ BEEHIVE_HARNESS_ISOLATED: '1', BEEHIVE_HARNESS_PATH: '/fixture/bin' }), /explicit BEEHIVE_HOME/);
assert.throws(() => localHarnessInventory({ BEEHIVE_HARNESS_ISOLATED: '1', BEEHIVE_HOME: '/fixture/home' }), /explicit BEEHIVE_HOME/);
});
test('a failed refresh and cancellation preserve the last trustworthy inventory across reconstruction', async () => {
const data = fixture([row('trusted')]);
const failed = new HarnessInventoryController(data.store, async () => { throw Error('probe failed'); });
assert.equal(await failed.refresh(), false);
failed.dispose();
const reconstructed = new HarnessInventoryController(data.store, async () => [row('must-not-probe')]);
assert.equal(reconstructed.snapshot().harnesses[0]?.id, 'trusted');
const work = deferred<DetectedHarness[]>();
const refresh = new HarnessInventoryController(data.store, () => work.promise);
const pending = refresh.refresh(); refresh.cancel(); work.resolve([row('late')]);
assert.equal(await pending, false);
assert.equal(new HarnessInventoryController(data.store, async () => []).snapshot().harnesses[0]?.id, 'trusted');
reconstructed.dispose(); refresh.dispose();
});
test('cancel and disposal fence late discovery before persistence or presentation', async () => {
for (const mode of ['cancel', 'dispose'] as const) {
const data = fixture([row('retained')]);
const work = deferred<DetectedHarness[]>();
const controller = new HarnessInventoryController(data.store, () => work.promise);
let notifications = 0;
controller.subscribe(() => notifications++);
const refresh = controller.refresh();
if (mode === 'cancel') controller.cancel(); else controller.dispose();
const beforeLate = notifications;
work.resolve([row('late')]);
assert.equal(await refresh, false);
assert.equal(data.saves(), 0, `${mode} must fence durable commit`);
assert.equal(notifications, beforeLate, `${mode} must fence late presentation`);
assert.equal(data.settings().harnesses?.[0]?.id, 'retained');
controller.dispose();
}
});

View File

@@ -2,7 +2,14 @@ import { test } from 'node:test';
import assert from 'node:assert/strict';
import { createTestRenderer } from '@opentui/core/testing';
import { OpenTuiShell, footerGuides, helpDialogGeometry, palette } from '../src/opentui-shell.ts';
import { HarnessInventoryController } from '../src/harness-inventory.ts';
import type { Settings } from '../src/settings.ts';
const inventoryWith = (harnesses: NonNullable<Settings['harnesses']>, discover: (signal: AbortSignal) => Promise<NonNullable<Settings['harnesses']>> = async () => []) => {
let settings: Settings = { version: 1, revision: 0, agents: [], providers: [], runtimes: [], harnesses };
return new HarnessInventoryController({ read: () => structuredClone(settings), save: (candidate, expected) => settings = { ...structuredClone(candidate), revision: expected + 1 } }, discover);
};
const inventory = () => inventoryWith([{ id: 'fixture', label: 'Fixture Harness', state: 'available', providers: [], reason: 'Synthetic test executable detected', executable: '/fixture/bin/harness' }]);
const text = (frame: string) => frame.endsWith('\n') ? frame.slice(0, -1).split('\n') : frame.split('\n');
const waitForEscape = () => new Promise(resolve => setTimeout(resolve, 50));
const cell = (ui: Awaited<ReturnType<typeof createTestRenderer>>, x: number, y: number) => {
@@ -17,7 +24,7 @@ const rgb = (value: any) => [value.buffer['0'], value.buffer['1'], value.buffer[
test('memory renderer draws the exact wide empty shell and focus perimeter', async () => {
const ui = await createTestRenderer({ width: 120, height: 40, exitOnCtrlC: false });
const shell = new OpenTuiShell(ui.renderer);
const shell = new OpenTuiShell(ui.renderer, inventory());
try {
await ui.renderOnce(); let frame = ui.captureCharFrame();
assert.ok(frame.includes('⬢ BEEHIVE')); assert.ok(frame.includes('HOST AGENTS HARNESSES PROVIDERS')); assert.ok(frame.includes('SIGNED OUT'));
@@ -29,7 +36,8 @@ test('memory renderer draws the exact wide empty shell and focus perimeter', asy
assert.equal(shell.state.headerIndex, 1); assert.equal(shell.state.activeSection, 0);
ui.mockInput.pressArrow('right'); ui.mockInput.pressEnter(); await ui.renderOnce(); frame = ui.captureCharFrame();
assert.equal(shell.state.activeSection, 2); assert.equal(shell.state.focus, 'list'); assert.ok(frame.includes('│'));
assert.equal(text(frame).slice(3, 38).join('').replace(/[│─┌┐└┘ ]/g, ''), '');
assert.ok(frame.includes('Fixture Harness · Available'));
assert.ok(frame.includes('State: available'));
const spans = JSON.stringify(ui.captureSpans());
assert.ok(spans.includes('"0":255,"1":211,"2":78'), `focus color missing from ${spans.slice(0, 300)}`);
} finally { shell.close(); }
@@ -37,7 +45,7 @@ test('memory renderer draws the exact wide empty shell and focus perimeter', asy
test('memory renderer owns split geometry, flat hierarchy, and focus inset without width shifts', async () => {
const ui = await createTestRenderer({ width: 120, height: 40, exitOnCtrlC: false });
const shell = new OpenTuiShell(ui.renderer);
const shell = new OpenTuiShell(ui.renderer, inventory());
try {
ui.mockInput.pressArrow('right'); ui.mockInput.pressArrow('right'); ui.mockInput.pressEnter(); await ui.renderOnce();
let frame = text(ui.captureCharFrame());
@@ -53,7 +61,7 @@ test('memory renderer owns split geometry, flat hierarchy, and focus inset witho
ui.mockInput.pressArrow('left'); await ui.renderOnce();
assert.equal(shell.state.focus, 'list'); assert.equal(rgb(cell(ui, 0, 3).fg), '255,211,78');
ui.mockInput.pressTab(); await ui.renderOnce(); frame = text(ui.captureCharFrame());
assert.equal(shell.state.focus, 'detail'); assert.equal(rgb(cell(ui, 36, 3).fg), '255,211,78');
assert.equal(shell.state.focus, 'header', 'Tab skips informational Details that have no action');
ui.mockInput.pressEscape(); await waitForEscape(); await ui.renderOnce(); frame = text(ui.captureCharFrame());
assert.equal(frame[3]![35], '│'); assert.doesNotMatch(frame.slice(3, 38).join('\n'), /[┌┐└┘]/); // unfocused panes remain flat
} finally { shell.close(); }
@@ -61,7 +69,7 @@ test('memory renderer owns split geometry, flat hierarchy, and focus inset witho
test('memory renderer keeps keyboard, pointer, resize, and Help return focus visible', async () => {
const ui = await createTestRenderer({ width: 120, height: 40, exitOnCtrlC: false });
const shell = new OpenTuiShell(ui.renderer);
const shell = new OpenTuiShell(ui.renderer, inventory());
try {
ui.mockInput.pressArrow('right'); ui.mockInput.pressArrow('right'); ui.mockInput.pressEnter(); await ui.renderOnce();
await ui.mockMouse.click(60, 10); await ui.renderOnce();
@@ -79,7 +87,7 @@ test('memory renderer keeps keyboard, pointer, resize, and Help return focus vis
test('memory renderer layers a full scrim and naturally sized help surface with constrained-body geometry', async () => {
const ui = await createTestRenderer({ width: 120, height: 40, exitOnCtrlC: false });
const shell = new OpenTuiShell(ui.renderer);
const shell = new OpenTuiShell(ui.renderer, inventory());
try {
ui.mockInput.pressKey('?'); await ui.renderOnce();
const geometry = helpDialogGeometry(120, 40);
@@ -97,7 +105,7 @@ test('memory renderer layers a full scrim and naturally sized help surface with
test('memory renderer reserves the overflow scrollbar cell outside help text and chrome', async () => {
const content = Array.from({ length: 12 }, (_, row) => `overflow row ${row} ends here`).join('\n');
const ui = await createTestRenderer({ width: 50, height: 20, exitOnCtrlC: false });
const shell = new OpenTuiShell(ui.renderer, content);
const shell = new OpenTuiShell(ui.renderer, inventory(), content);
try {
ui.mockInput.pressKey('?'); await ui.renderOnce();
const frame = text(ui.captureCharFrame());
@@ -113,9 +121,88 @@ test('memory renderer reserves the overflow scrollbar cell outside help text and
} finally { shell.close(); }
});
test('memory renderer binds refresh to its command row and applies real async results coherently', async () => {
let complete!: (rows: NonNullable<Settings['harnesses']>) => void;
const discovered = new Promise<NonNullable<Settings['harnesses']>>(resolve => { complete = resolve; });
const ui = await createTestRenderer({ width: 120, height: 40, exitOnCtrlC: false });
const controller = inventoryWith([], () => discovered);
const shell = new OpenTuiShell(ui.renderer, controller);
try {
ui.mockInput.pressArrow('right'); ui.mockInput.pressArrow('right'); ui.mockInput.pressEnter(); await ui.renderOnce();
assert.ok(ui.captureCharFrame().includes('Saved local harness inventory'));
ui.mockInput.pressEnter(); await ui.renderOnce();
assert.equal(shell.state.focus, 'detail', 'the command row opens its owned Details action without executing');
assert.ok(ui.captureCharFrame().includes('Refresh harnesses'));
ui.mockInput.pressEnter(); await ui.renderOnce();
assert.ok(ui.captureCharFrame().includes('Refreshing local harness inventory'));
complete([{ id: 'codex', label: 'Codex', state: 'incompatible', providers: [], reason: 'Codex ACP 1.10.0 or newer required; version unknown/outdated' }]);
await new Promise(resolve => setTimeout(resolve, 0)); await ui.renderOnce();
const frame = ui.captureCharFrame();
assert.ok(frame.includes('Codex · Incompatible')); assert.ok(frame.includes('State: incompatible'));
assert.equal(shell.state.harnessSelection, 'harness:codex', 'disappearing command selection uses deterministic first-row fallback');
} finally { shell.close(); }
});
test('renderer shows refresh failure, permits recovery, and fences completion after leaving Harnesses', async () => {
let attempts = 0;
let lateResolve!: (rows: NonNullable<Settings['harnesses']>) => void;
const late = new Promise<NonNullable<Settings['harnesses']>>(resolve => { lateResolve = resolve; });
const controller = inventoryWith([], async () => {
attempts++;
if (attempts === 1) throw Error('/private/diagnostic');
if (attempts === 2) return [{ id: 'buzz-agent', label: 'Buzz Agent', state: 'available', providers: ['openai'], reason: 'Detected; provider/model access unverified', executable: '/fixture/buzz-agent' }];
return late;
});
const ui = await createTestRenderer({ width: 120, height: 40, exitOnCtrlC: false });
const shell = new OpenTuiShell(ui.renderer, controller);
try {
ui.mockInput.pressArrow('right'); ui.mockInput.pressArrow('right'); ui.mockInput.pressEnter(); await ui.renderOnce();
ui.mockInput.pressEnter(); ui.mockInput.pressEnter(); await new Promise(resolve => setTimeout(resolve, 0)); await ui.renderOnce();
assert.ok(ui.captureCharFrame().includes('Harness refresh failed'));
assert.doesNotMatch(ui.captureCharFrame(), /private\/diagnostic/);
ui.mockInput.pressEnter(); await new Promise(resolve => setTimeout(resolve, 0)); await ui.renderOnce();
assert.ok(ui.captureCharFrame().includes('Buzz Agent · Available'));
ui.mockInput.pressArrow('down'); ui.mockInput.pressEnter(); ui.mockInput.pressEnter(); await ui.renderOnce();
ui.mockInput.pressArrow('up'); ui.mockInput.pressArrow('up'); ui.mockInput.pressArrow('right'); ui.mockInput.pressEnter(); await ui.renderOnce();
assert.equal(shell.state.activeSection, 3);
lateResolve([{ id: 'late', label: 'Late', state: 'available', providers: [], reason: 'late', executable: '/fixture/late' }]);
await new Promise(resolve => setTimeout(resolve, 0));
assert.equal(controller.snapshot().harnesses.some(row => row.id === 'late'), false);
} finally { shell.close(); }
});
test('pointer selection does not execute a command and its Details action dispatches once', async () => {
let attempts = 0;
const ui = await createTestRenderer({ width: 120, height: 40, exitOnCtrlC: false });
const shell = new OpenTuiShell(ui.renderer, inventoryWith([], async () => { attempts++; return []; }));
try {
ui.mockInput.pressArrow('right'); ui.mockInput.pressArrow('right'); ui.mockInput.pressEnter(); await ui.renderOnce();
await ui.mockMouse.click(3, 6); await ui.renderOnce();
assert.equal(attempts, 0, 'a List click only selects the command row');
ui.mockInput.pressEnter(); await ui.renderOnce();
await ui.mockMouse.click(40, 15); await new Promise(resolve => setTimeout(resolve, 0)); await ui.renderOnce();
assert.equal(attempts, 1, 'mouse bubbling cannot double-dispatch the Details action');
} finally { shell.close(); }
});
test('virtual list click mapping targets the currently visible row after overflow scroll', async () => {
const harnesses = Array.from({ length: 20 }, (_, index) => ({ id: `fixture-${index}`, label: `Harness ${index}`, state: 'available' as const, providers: [], reason: 'Synthetic test executable detected', executable: `/fixture/${index}` }));
const ui = await createTestRenderer({ width: 50, height: 20, exitOnCtrlC: false });
const shell = new OpenTuiShell(ui.renderer, inventoryWith(harnesses));
try {
ui.mockInput.pressArrow('right'); ui.mockInput.pressArrow('right'); ui.mockInput.pressEnter(); await ui.renderOnce();
for (let index = 0; index < 14; index++) ui.mockInput.pressArrow('down');
await ui.renderOnce();
assert.ok(shell.state.listOffset > 0);
const expected = shell.state.harnessRows[shell.state.listOffset]!.id;
await ui.mockMouse.click(3, 6); await ui.renderOnce();
assert.equal(shell.state.harnessSelection, expected, 'the slot maps through the current virtual offset, never a stale absolute index');
} finally { shell.close(); }
});
test('memory renderer preserves the 50x20 narrow shell, help, keyboard, and pointer activation', async () => {
const ui = await createTestRenderer({ width: 50, height: 20, exitOnCtrlC: false });
const shell = new OpenTuiShell(ui.renderer);
const shell = new OpenTuiShell(ui.renderer, inventory());
try {
await ui.renderOnce(); let frame = ui.captureCharFrame();
assert.equal(text(frame).length, 20); assert.ok(frame.includes(footerGuides.compact)); assert.ok(frame.includes('PROVIDERS'));

View File

@@ -12,6 +12,7 @@ test('never-signed-in shell labels and initial state are exact', () => {
test('wide collection arrows traverse header, List and Details without activating a new destination', () => {
const state = new ShellState();
state.setHarnessRows([{ id: 'harness:fixture', kind: 'harness' }, { id: 'command:refresh', kind: 'command' }]);
assert.equal(state.key('right'), 'render'); assert.equal(state.headerIndex, 1); assert.equal(state.activeSection, 0);
state.key('right'); state.key('return');
assert.deepEqual({ active: state.activeSection, header: state.headerIndex, focus: state.focus, split: state.splitPane }, { active: 2, header: 2, focus: 'list', split: true });
@@ -26,6 +27,7 @@ test('wide collection arrows traverse header, List and Details without activatin
test('narrow pane transitions, resize, and Help preserve a visible valid focus target', () => {
const state = new ShellState();
state.setHarnessRows([{ id: 'harness:fixture', kind: 'harness' }, { id: 'command:refresh', kind: 'command' }]);
state.activateHeader(2); state.key('right');
assert.equal(state.focus, 'detail');
state.resize(50, 20); assert.deepEqual({ focus: state.focus, pane: state.narrowPane, split: state.splitPane }, { focus: 'detail', pane: 'detail', split: false });
@@ -40,6 +42,7 @@ test('narrow pane transitions, resize, and Help preserve a visible valid focus t
test('help, q semantics, minimum guard and narrow return are deterministic', () => {
const state = new ShellState();
state.setHarnessRows([{ id: 'harness:fixture', kind: 'harness' }, { id: 'command:refresh', kind: 'command' }]);
assert.equal(state.key('?'), 'render'); assert.equal(state.helpOpen, true);
assert.equal(state.key('q'), 'none'); assert.equal(state.helpOpen, true);
assert.equal(state.key('escape'), 'render'); assert.equal(state.helpOpen, false);
@@ -49,6 +52,26 @@ test('help, q semantics, minimum guard and narrow return are deterministic', ()
state.key('return'); assert.equal(state.narrowPane, 'detail'); state.key('escape'); assert.equal(state.narrowPane, 'list'); assert.equal(state.focus, 'list');
});
test('harness rows preserve stable identity, fall back deterministically, and virtualize overflow', () => {
const state = new ShellState();
const rows: Array<{ id: string; kind: 'harness' | 'command' }> = [...Array.from({ length: 20 }, (_, index) => ({ id: `harness:${index}`, kind: 'harness' as const })), { id: 'command:refresh', kind: 'command' }];
state.setHarnessRows(rows); state.setListCapacity(5); state.activateHeader(2);
for (let index = 0; index < 9; index++) assert.equal(state.key('down'), 'render');
assert.equal(state.harnessSelection, 'harness:9'); assert.equal(state.listOffset, 5);
state.setHarnessRows(rows.filter(row => row.id !== 'harness:3'));
assert.equal(state.harnessSelection, 'harness:9', 'stable identity survives row reindexing');
state.setHarnessRows([{ id: 'harness:replacement', kind: 'harness' }, { id: 'command:refresh', kind: 'command' }]);
assert.equal(state.harnessSelection, 'harness:replacement', 'disappearing identity falls back to the next surviving harness');
state.setHarnessRows([{ id: 'harness:a', kind: 'harness' }, { id: 'harness:b', kind: 'harness' }, { id: 'harness:c', kind: 'harness' }, { id: 'command:refresh', kind: 'command' }]);
state.selectHarness('harness:b');
state.setHarnessRows([{ id: 'harness:a', kind: 'harness' }, { id: 'harness:c', kind: 'harness' }, { id: 'command:refresh', kind: 'command' }]);
assert.equal(state.harnessSelection, 'harness:c', 'removed row falls forward at the same harness position');
state.setHarnessRows([{ id: 'harness:a', kind: 'harness' }, { id: 'command:refresh', kind: 'command' }]);
assert.equal(state.harnessSelection, 'harness:a', 'then falls back to the previous surviving harness');
state.key('down'); assert.equal(state.key('return'), 'render', 'the command row opens Details without executing');
assert.equal(state.key('return'), 'activate', 'only the explicit command Details action activates');
});
test('prototype breakpoint formulas are exact', () => {
assert.equal(listWidth(120), 35); assert.equal(listWidth(90), 28); assert.equal(listWidth(72), 25); assert.equal(listWidth(50), 50);
});