A commander is a chat by every mechanism it uses, so it turned up in Live beside the conversations somebody is actually having. With one deploy commander that is untidy. With a watcher armed per repo it is a list where the thing you are looking for is outnumbered. Two tabs inside Live, counts always shown. That last part differs from the badge on the Live tab above it, which hides at zero because a standing `0` there says nothing. These two say a lot at zero: `Commanders (0)` is how you find out the watcher is not running, which is usually why you looked. The flag is derived server-side from the cwd, not from a `command_hq_sessions` lookup — every commander shares one directory, so the directory IS the answer, it costs no query, and it is already right during a commander's first turn, before any row exists. It has to be in the payload rather than computed in the panel, and that is the point of the change: the iPhone app swipes between LIVE chats and has no room for tabs. Filtering there needs the server to name them. A deploy commander appearing in that swipe order is precisely what must not happen. A commander row also now opens Command HQ instead of /chat. Same transcript either way, but /chat renders it with nothing that makes it a commander legible — no category, no cost, no watcher state, and no way to disarm it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
38 lines
1.4 KiB
TypeScript
38 lines
1.4 KiB
TypeScript
import { describe, expect, it } from 'bun:test';
|
|
import { resolve } from 'node:path';
|
|
import { isCommandersDir } from './claude-sessions';
|
|
import { getCommandersDir } from '../../data-path';
|
|
|
|
const EMAIL = 'pastilhas@officer.dev';
|
|
|
|
// The live payload's `isCommander` is derived from a cwd read out of a transcript, so it is whatever the
|
|
// process was actually in — not a path this code composed. These are the shapes that arrive.
|
|
describe('isCommandersDir', () => {
|
|
const dir = getCommandersDir(EMAIL);
|
|
|
|
it('recognises the commanders directory', () => {
|
|
expect(isCommandersDir(EMAIL, dir)).toBe(true);
|
|
});
|
|
|
|
it('survives a trailing slash and a .. hop', () => {
|
|
expect(isCommandersDir(EMAIL, `${dir}/`)).toBe(true);
|
|
expect(isCommandersDir(EMAIL, `${dir}/sub/..`)).toBe(true);
|
|
});
|
|
|
|
it('rejects a sibling and a child', () => {
|
|
expect(isCommandersDir(EMAIL, resolve(dir, '..'))).toBe(false);
|
|
expect(isCommandersDir(EMAIL, resolve(dir, 'nested'))).toBe(false);
|
|
});
|
|
|
|
// A commander's cwd belongs to the account that owns it. Reading one user's directory as another's
|
|
// would file their commanders under the wrong person and, in the iPhone swipe order, show them.
|
|
it('is per account', () => {
|
|
expect(isCommandersDir('someone-else@officer.dev', dir)).toBe(false);
|
|
});
|
|
|
|
it('never throws on rubbish', () => {
|
|
expect(isCommandersDir(EMAIL, '')).toBe(false);
|
|
expect(isCommandersDir(EMAIL, '\0')).toBe(false);
|
|
});
|
|
});
|