cli, seat, harness: a project clone in each role workspace (#1585)
Row 91, round 3. Darkwing built it, Dewey approved it (comment 28150) on manifest ca570607 (8 paths, base2b2a9e14). At each launch the host makes or updates a git clone of the project in the role's workspace, and the session prompt names each clone. Round 3 closes Filbert's round 2 escape: a clone with a submodule is kept as it is, and no host git recurses into one (decision 130 thread). Live at the next bus restart; the prompt lines appear only when the launcher passes checkouts. Sage gate onfc6d5822plus the patch, 14:18:25Z to 14:22:27Z: bus 115, business 60, cli 131, control-board 124, conversation 182, discord 178, harness 67, ledger 78, mosaic 69, queue 150, runs 43, seat 89, tasks 89, webui 104, all fail 0. Every scripts/test-*.sh exited 0; test-release ran 14 cases. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -55,6 +55,13 @@ export function sessionModel(resolved) {
|
||||
return { harness, provider: v["execution.provider"] ?? null, model, thinking: v.thinking ?? null };
|
||||
}
|
||||
|
||||
// Row 91 (#1585): the project clones the host made or kept in the workspace.
|
||||
function checkoutLines(checkouts) {
|
||||
if (!checkouts.length) return [];
|
||||
const each = checkouts.map((c) => (c.outcome === "kept" ? `${c.project} at ${c.path}, not updated at this launch (${c.reason})` : `${c.project} at ${c.path}, on branch ${c.branch}`));
|
||||
return [`- Project clones: ${each.join("; ")}. Each is a git clone of the project. The host fetches from its origin at each launch; the origin is outside this sandbox.`];
|
||||
}
|
||||
|
||||
function prompt({ contract, resolved, session, tools, untyped }) {
|
||||
const lines = [
|
||||
contract.trimEnd(),
|
||||
@@ -64,6 +71,7 @@ function prompt({ contract, resolved, session, tools, untyped }) {
|
||||
`- Business: ${resolved.business}. Role instance: ${resolved.instance} (definition ${resolved.definition}). Run: ${session.run}.`,
|
||||
`- Launched by: ${session.launchedBy}. Harness: ${session.harness}, model ${session.model}.`,
|
||||
`- Workspace: ${session.workspace}. File tools work inside it only.`,
|
||||
...checkoutLines(session.checkouts ?? []),
|
||||
// Row 89 (#1583): the names the harness gives its built-ins, as the manifest lists them.
|
||||
`- Built-in tools: ${(session.harness === "pi" ? resolved.limits.tools : claudeBuiltins({ tools: resolved.limits.tools })).join(", ") || "none"}.`,
|
||||
`- Bus tools: ${tools.map((t) => t.name).join(", ")}. The broker decides every call; a refusal names its reason.`,
|
||||
@@ -83,7 +91,9 @@ function prompt({ contract, resolved, session, tools, untyped }) {
|
||||
// arbiter true when the business names this instance as an arbiter
|
||||
// tracker true when the broker has task verbs for the business
|
||||
// credentials the broker's credentialStatus(...) for the instance (metadata only)
|
||||
// session { run, launchedBy, workspace, toolSocket, turnMarker }
|
||||
// session { run, launchedBy, workspace, toolSocket, turnMarker, checkouts }
|
||||
// checkouts is ensureCheckouts(...) output (packages/seat/src/checkout.mjs),
|
||||
// named in the prompt; none when absent
|
||||
// piVersion the pinned pi version, for the manifest
|
||||
// claudeVersion the host's `claude --version`, for the manifest
|
||||
// Returns { manifest, files } with absolute paths.
|
||||
|
||||
@@ -103,6 +103,23 @@ test("the prompt's built-in tools line is the harness's own names, as the manife
|
||||
}
|
||||
});
|
||||
|
||||
// Row 91 (#1585): the prompt names each project clone, and why a kept one wasn't updated.
|
||||
test("the prompt names the project clones in the workspace, and none when there are none", (t) => {
|
||||
const line = (b) => readFileSync(b.files.prompt, "utf8").split("\n").filter((l) => l.startsWith("- Project clones: "));
|
||||
const plain = build(t, resolvedFor(t, "pm"));
|
||||
assert.deepEqual(line(plain), []);
|
||||
const dir = scratch(t);
|
||||
const session = (checkouts) => ({ run: "run-1", launchedBy: "pm", workspace: join(dir, "ws"), toolSocket: join(dir, "tools.sock"), turnMarker: join(dir, "turn.done"), checkouts });
|
||||
const two = build(t, resolvedFor(t, "pm"), { session: session([
|
||||
{ project: "stack", path: join(dir, "ws", "stack"), outcome: "updated", branch: "main" },
|
||||
{ project: "site", path: join(dir, "ws", "site"), outcome: "kept", reason: "dirty" },
|
||||
]) });
|
||||
assert.deepEqual(line(two), [`- Project clones: stack at ${join(dir, "ws", "stack")}, on branch main; site at ${join(dir, "ws", "site")}, not updated at this launch (dirty). Each is a git clone of the project. The host fetches from its origin at each launch; the origin is outside this sandbox.`]);
|
||||
const prompt = readFileSync(two.files.prompt, "utf8");
|
||||
assert.ok(prompt.indexOf("- Workspace: ") < prompt.indexOf("- Project clones: "));
|
||||
assert.deepEqual(line(build(t, resolvedFor(t, "pm"), { session: session([]) })), []);
|
||||
});
|
||||
|
||||
test("a bundle is written once: an existing file refuses", (t) => {
|
||||
const dir = scratch(t);
|
||||
mkdirSync(join(dir, "bundle"));
|
||||
|
||||
Reference in New Issue
Block a user