testSessionsGoOnAfterARestart

tcmts.cli.test.e2e.mcp.testSessionsGoOnAfterARestart · #jyyoseyd · term

A session's server started again for the same agent (from the same directory: the client reconnecting after a pause, its server stopped or killed) goes on with its branch, and says so, as long as the branch holds work main doesn't have. Another agent's (another directory: a worktree reaching the codebase through a link) doesn't take it, nor does a second server from the same directory while the first is on it: two servers never share a branch. A session moves onto a branch it owns with tcm_cli ["switch", ], once no other server is on it, and the branch it leaves goes if it holds nothing; not onto main.

import { okIn, tempDir, withChildren } from "#codebase/tcmts/cli/test/e2e/harness";
import { startedSession } from "#codebase/tcmts/cli/test/e2e/mcp";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";

export const testSessionsGoOnAfterARestart = () =>
  withChildren(async (keep) => {
    const dir = tempDir("mcp restart");
    const tcm = (...args: string[]) => okIn(dir, ...args);
    tcm("init");
    fs.rmSync(path.join(dir, "scratch.ts"), { force: true });
    const a = await startedSession(dir, keep);
    assert.match(a.branch, /^session\//);
    assert.doesNotMatch(a.instructions, /last session left/);
    assert.equal(
      (await a.call("tcm_add", { code: "export const work = 1;\n", namespace: "lib" })).isError,
      false,
    );
    a.close();
    await a.exit;
    const a2 = await startedSession(dir, keep);
    assert.equal(a2.branch, a.branch);
    assert.match(a2.instructions, /this directory's last session left holding its work/);
    assert.match((await a2.call("tcm_view", { names: ["lib.work"] })).text, /work = 1/);
    // killed, rather than stopped: the same
    a2.kill();
    await a2.exit;
    const a3 = await startedSession(dir, keep);
    assert.equal(a3.branch, a.branch);

    const link = path.join(tempDir("mcp worktree"), "codebase");
    fs.symlinkSync(dir, link);
    const b = await startedSession(link, keep, { PWD: link });
    assert.notEqual(b.branch, a.branch);
    // (the same directory, while a3 is on it: it waits a little for a3 to stop, as a server being replaced does)
    const c = await startedSession(dir, keep);
    assert.notEqual(c.branch, a.branch);
    assert.notEqual(c.branch, b.branch);
    const refused = await c.call("tcm_cli", { args: ["switch", a.branch] });
    assert.equal(refused.isError, true, refused.text);
    assert.match(refused.text, /works on session\/\S+: two servers don't share a branch/);

    a3.kill();
    await a3.exit;
    const moved = await c.call("tcm_cli", { args: ["switch", a.branch] });
    assert.equal(moved.isError, false, moved.text);
    assert.match(
      moved.text,
      new RegExp(`works on ${a.branch} now[\\s\\S]*${c.branch}, which it leaves, is gone`),
    );
    assert.match((await c.call("tcm_view", { names: ["lib.work"] })).text, /work = 1/);
    assert.match((await c.call("tcm_status")).text, new RegExp(`^branch\\s+${a.branch}`, "m"));
    assert.doesNotMatch(tcm("branch"), new RegExp(c.branch));
    const main = await c.call("tcm_cli", { args: ["switch", "main"] });
    assert.equal(main.isError, true, main.text);
    assert.match(main.text, /main is the codebase's own branch/);
    b.close();
    c.close();
  })

uses