feat(ui): call lanes "workspaces" in the UI, CLI output and docs

Copy-only rename (workspace redesign, phase 0). The word a user reads
changes — "Lane" → "Workspace", "Add lane" → "Add workspace" — in both
locales, in agenthub CLI prose and help, in the server's error messages
and in the docs. Every identifier stays: /api/lanes, `agenthub lanes`,
--lane, AGENTHUB_LANE_ID, error codes, tmux names, DB tables, file,
component and i18n key names. docs/LANES.md keeps its name, retitled
"Workspaces (lanes)".
This commit is contained in:
2026-10-01 16:03:24 +07:00
parent e01cb26bde
commit 5b3884b309
43 changed files with 928 additions and 891 deletions
+14 -14
View File
@@ -205,7 +205,7 @@ describe("agenthub stage", () => {
it("exits non-zero when no lane owns the cwd", async () => {
const r = await cli(["stage", "review"], os.tmpdir());
assert.notEqual(r.status, 0);
assert.match(`${r.stdout}${r.stderr}`, /no lane/i);
assert.match(`${r.stdout}${r.stderr}`, /no workspace/i);
});
it("refuses a stage name matching no pipeline node", async () => {
@@ -296,7 +296,7 @@ describe("agenthub stage in a directory shared by two lanes", () => {
await post("/api/lanes", { cwd: solo, title: "solo claude" });
const r = await cli(["stage", "plan", "--tool", "codex"], solo, NO_HARNESS);
assert.notEqual(r.status, 0);
assert.match(`${r.stdout}${r.stderr}`, /no codex lane/i);
assert.match(`${r.stdout}${r.stderr}`, /no codex workspace/i);
});
it("--lane flag with explicit ID beats cwd resolution and tool ambiguity", async () => {
@@ -312,7 +312,7 @@ describe("agenthub stage in a directory shared by two lanes", () => {
// Other commands like `agenthub stage` have their own positional argument and don't support it.
const r = await cli(["lanes", "pipeline", String(codexLane.id)], DUAL_DIR, NO_HARNESS);
assert.equal(r.status, 0, r.stderr);
assert.match(r.stdout, new RegExp(`lane #${codexLane.id}`));
assert.match(r.stdout, new RegExp(`workspace #${codexLane.id}`));
});
it("non-numeric AGENTHUB_LANE_ID is ignored", async () => {
@@ -363,7 +363,7 @@ describe("agenthub lanes add", () => {
try {
const r = await cli(["lanes", "add", "--cwd", addDir, "--title", "CLI added"], addDir);
assert.equal(r.status, 0, r.stderr);
assert.match(r.stdout, /Created lane/i);
assert.match(r.stdout, /Created workspace/i);
const list = await cli(["lanes"], addDir);
assert.equal(list.status, 0, list.stderr);
@@ -378,7 +378,7 @@ describe("agenthub lanes add", () => {
try {
const r = await cli(["lanes", "add", "--cwd", addDir, "--title", "Default pipeline"], addDir);
assert.equal(r.status, 0, r.stderr);
assert.match(r.stdout, /Created lane/i);
assert.match(r.stdout, /Created workspace/i);
const details = await get(`/api/lanes`);
const lane = details.body.lanes.find((l) => l.title === "Default pipeline");
@@ -406,7 +406,7 @@ describe("agenthub lanes add", () => {
addDir
);
assert.equal(r.status, 0, r.stderr);
assert.match(r.stdout, /Created lane/i);
assert.match(r.stdout, /Created workspace/i);
const details = await get(`/api/lanes`);
const lane = details.body.lanes.find((l) => l.title === "Custom pipeline");
@@ -452,7 +452,7 @@ describe("agenthub lanes add", () => {
SOURCE_REPO
);
assert.equal(r.status, 0, r.stderr);
assert.match(r.stdout, /Worktree lane #\d+ ready: CLI worktree/);
assert.match(r.stdout, /Worktree workspace #\d+ ready: CLI worktree/);
const list = await get("/api/lanes");
const lane = list.body.lanes.find((l) => l.title === "CLI worktree");
@@ -479,7 +479,7 @@ describe("agenthub lanes add", () => {
emptyRepo
);
assert.notEqual(r.status, 0);
assert.match(`${r.stdout}${r.stderr}`, /Worktree lane #\d+ failed:/);
assert.match(`${r.stdout}${r.stderr}`, /Worktree workspace #\d+ failed:/);
assert.match(`${r.stdout}${r.stderr}`, /fatal:|ambiguous argument|unknown revision/i);
assert.doesNotMatch(r.stdout, /ready/i);
} finally {
@@ -515,7 +515,7 @@ describe("agenthub lanes destructive lifecycle", () => {
const r = await cli(["lanes", "reset", String(lane.id)], SOURCE_REPO);
assert.notEqual(r.status, 0);
assert.match(r.stdout, /Preflight for reset lane #\d+:/);
assert.match(r.stdout, /Preflight for reset workspace #\d+:/);
assert.match(r.stdout, /dirty\s+0/);
assert.match(r.stdout, /untracked\s+1/);
assert.match(`${r.stdout}${r.stderr}`, /without --yes/);
@@ -528,7 +528,7 @@ describe("agenthub lanes destructive lifecycle", () => {
const r = await cli(["lanes", "reset", String(lane.id), "--yes"], SOURCE_REPO);
assert.equal(r.status, 0, r.stderr);
assert.match(r.stdout, new RegExp(`Reset lane #${lane.id}`));
assert.match(r.stdout, new RegExp(`Reset workspace #${lane.id}`));
assert.equal(fs.existsSync(path.join(lane.cwd, "dirty.txt")), false);
const preflight = await get(`/api/lanes/${lane.id}/preflight?action=reset`);
@@ -554,7 +554,7 @@ describe("agenthub lanes destructive lifecycle", () => {
const [winner, loser] = a.status === 0 ? [a, b] : [b, a];
assert.equal(winner.status, 0, winner.stderr);
assert.notEqual(loser.status, 0);
assert.match(`${loser.stdout}${loser.stderr}`, /lane state changed since preflight/);
assert.match(`${loser.stdout}${loser.stderr}`, /workspace state changed since preflight/);
assert.match(loser.stdout, /Expected preflight:/);
assert.match(loser.stdout, /Current preflight:/);
});
@@ -564,7 +564,7 @@ describe("agenthub lanes destructive lifecycle", () => {
assert.equal(fs.existsSync(lane.cwd), true);
const r = await cli(["lanes", "remove", String(lane.id), "--yes"], SOURCE_REPO);
assert.equal(r.status, 0, r.stderr);
assert.match(r.stdout, new RegExp(`Removed lane #${lane.id}`));
assert.match(r.stdout, new RegExp(`Removed workspace #${lane.id}`));
assert.match(r.stdout, /left for you to delete yourself/);
assert.equal(fs.existsSync(lane.cwd), true);
assert.equal((await get(`/api/lanes/${lane.id}`)).status, 404);
@@ -590,7 +590,7 @@ describe("agenthub lanes destructive lifecycle", () => {
// unreachable from the terminal.
const removed = await cli(["lanes", "remove", String(id), "--yes"], adoptedDir);
assert.equal(removed.status, 0, removed.stderr);
assert.match(removed.stdout, new RegExp(`Removed lane #${id}`));
assert.match(removed.stdout, new RegExp(`Removed workspace #${id}`));
assert.equal((await get(`/api/lanes/${id}`)).status, 404);
assert.equal(fs.existsSync(adoptedDir), true);
assert.equal(fs.readFileSync(preserved, "utf8"), "do not delete\n");
@@ -610,7 +610,7 @@ describe("agenthub lanes destructive lifecycle", () => {
assert.match(
r.stdout,
new RegExp(
`Purged lane #${lane.id}: ${sessions} sessions, ${events} events, ${tokenRows} token rows`
`Purged workspace #${lane.id}: ${sessions} sessions, ${events} events, ${tokenRows} token rows`
)
);
});