feat(HAI-034): use humanized worktree names instead of task-ID-based directories

- Add worktree-names module with adjective-animal name generator (generateWorktreeName)
- Update executor to assign humanized random worktree names and support dependency worktree reuse
- Remove task-ID fallback paths from merger and store; add shared worktree cleanup via findWorktreeUser
- Remove worktree pool module and related scheduler/integration tests
- Update dashboard worktree label tests and add JSDoc documentation for worktree naming
This commit is contained in:
Dustin Byrne
2026-03-25 23:09:25 -04:00
parent 74379bdbce
commit b9d7c89905
7 changed files with 233 additions and 36 deletions

View File

@@ -0,0 +1,76 @@
import { readdirSync } from "node:fs";
import { join } from "node:path";
import { existsSync } from "node:fs";
const ADJECTIVES = [
"amber", "azure", "bold", "brave", "bright",
"calm", "clear", "cool", "coral", "crisp",
"deft", "dusky", "eager", "early", "faint",
"fast", "fleet", "fresh", "gentle", "gilt",
"glad", "grand", "green", "happy", "hazy",
"ivory", "jade", "keen", "lemon", "light",
"lunar", "maple", "merry", "misty", "noble",
"opal", "pale", "pearl", "plush", "proud",
"quiet", "rapid", "rosy", "rusty", "sandy",
"sharp", "sleek", "solar", "swift", "vivid",
];
const NOUNS = [
"badger", "breeze", "brook", "cedar", "cliff",
"crane", "daisy", "delta", "dune", "eagle",
"ember", "falcon", "fern", "finch", "flame",
"frost", "grove", "hawk", "heron", "iris",
"lark", "lotus", "maple", "marsh", "mesa",
"moss", "oak", "olive", "orbit", "otter",
"panda", "peach", "petal", "pine", "plume",
"quail", "raven", "reef", "ridge", "robin",
"sage", "shore", "spark", "stone", "swift",
"thorn", "tiger", "trail", "trout", "wren",
];
/**
* Generate a random, human-friendly worktree directory name.
*
* Names follow an `adjective-noun` pattern (e.g., `swirly-monkey`,
* `quiet-falcon`, `bright-orchid`) drawn from embedded word lists of
* ~50 adjectives × ~50 nouns, producing ~2,500 unique combinations.
*
* **Collision avoidance:** The function checks existing subdirectories
* under `<rootDir>/.worktrees/`. If the randomly chosen name already
* exists, a numeric suffix is appended (e.g., `swift-falcon-2`,
* `swift-falcon-3`) until a unique name is found.
*
* @param rootDir - The project root directory (parent of `.worktrees/`)
* @returns A unique worktree directory name (not a full path)
*/
export function generateWorktreeName(rootDir: string): string {
const adjective = ADJECTIVES[Math.floor(Math.random() * ADJECTIVES.length)];
const noun = NOUNS[Math.floor(Math.random() * NOUNS.length)];
const baseName = `${adjective}-${noun}`;
const worktreesDir = join(rootDir, ".worktrees");
const existing = getExistingWorktreeNames(worktreesDir);
if (!existing.has(baseName)) {
return baseName;
}
// Collision — append numeric suffix
let suffix = 2;
while (existing.has(`${baseName}-${suffix}`)) {
suffix++;
}
return `${baseName}-${suffix}`;
}
function getExistingWorktreeNames(worktreesDir: string): Set<string> {
if (!existsSync(worktreesDir)) {
return new Set();
}
try {
const entries = readdirSync(worktreesDir, { withFileTypes: true });
return new Set(entries.filter((e) => e.isDirectory()).map((e) => e.name));
} catch {
return new Set();
}
}