From 58ad27ad401b532e5f3ea92716ed4ad9dcd54406 Mon Sep 17 00:00:00 2001 From: iamnbutler Date: Mon, 19 Oct 2026 17:09:43 -0400 Subject: [PATCH 1/4] feat(host): have the agent push unready lanes before a move to a cloud workspace When a move to a cloud workspace finds lanes with uncommitted or unpushed work, the host asks the channel's agent to commit them and lane push them, waits for that run while the move shows progress, and checks the lanes again. A stopped run or a lane still unready stops the move with the channel where it was. A lane with no branch is refused for the person to remove. The listing now reports computing only from the fence on, so the chat stays open during the agent's run. Why: apps/host/src/move.ts#function push | the agent's push runs before the fence and outside `starting`, so its run stays watchable; a lane with no branch is refused, never sent to the agent Why: apps/host/src/move.ts#function move | after the agent's run the record is checked again with ready() before the fence, since the run can take minutes --- apps/app/src/move-channel.tsx | 2 +- apps/host/src/compute.ts | 63 ++++++++++++++++----------- apps/host/src/gateway.ts | 11 +++-- apps/host/src/move.ts | 81 ++++++++++++++++++++++++++++++++--- docs/architecture.md | 15 +++++-- 5 files changed, 132 insertions(+), 40 deletions(-) diff --git a/apps/app/src/move-channel.tsx b/apps/app/src/move-channel.tsx index 652f0376..973e1982 100644 --- a/apps/app/src/move-channel.tsx +++ b/apps/app/src/move-channel.tsx @@ -14,7 +14,7 @@ const OUTCOMES = { hosted: "The channel becomes a hosted channel there, with this host as its workspace. It keeps its chats, messages, and lanes.", cloud: - "Its tools then run in a container on the team's compute service, which starts from a fresh clone with the channel's pushed lanes; untracked files stay on this Mac. It keeps its chats and messages, and can't move back yet.", + "Its tools then run in a container on the team's compute service, which starts from a fresh clone with the channel's lanes. Its agent first pushes any lane with unpushed work; untracked files stay on this Mac. It keeps its chats and messages, and can't move back yet.", }; type Props = { diff --git a/apps/host/src/compute.ts b/apps/host/src/compute.ts index 87a1aadc..e5318136 100644 --- a/apps/host/src/compute.ts +++ b/apps/host/src/compute.ts @@ -55,6 +55,13 @@ export async function available(root: string): Promise { type Lane = { path: string; branch: string; base?: string }; +/** A lane a cloud workspace can't check out yet, and why. */ +export type Unready = { name: string; path: string; branch?: string; problem: string }; + +export function listed(lanes: Unready[]): string { + return lanes.map((lane) => `${lane.name} (${lane.problem})`).join(", "); +} + async function output(dir: string, args: string[]): Promise { const result = await git(dir, args); if (result.status !== 0) { @@ -64,7 +71,7 @@ async function output(dir: string, args: string[]): Promise { } /** A cloud workspace starts from a fresh clone, so lanes travel only as pushed branches. */ -async function lanes(root: string, prefix: string): Promise { +async function lanes(root: string, prefix: string): Promise<{ ready: Lane[]; unready: Unready[] }> { // Git lists real paths; the channel's records name lanes under its project path. const trees = `${realpathSync(root)}/.lane/trees/${prefix}/`; const found = (await output(root, ["worktree", "list", "--porcelain"])).split("\n\n").flatMap( @@ -80,7 +87,7 @@ async function lanes(root: string, prefix: string): Promise { }]; }, ); - if (!found.length) return []; + if (!found.length) return { ready: [], unready: [] }; const pushed = new Map( (await output(root, ["ls-remote", "origin", `refs/heads/${prefix}/*`])).split("\n") .filter(Boolean) @@ -89,34 +96,34 @@ async function lanes(root: string, prefix: string): Promise { return [ref!.slice("refs/heads/".length), commit]; }), ); - const unready: string[] = []; + const unready: Unready[] = []; const ready: Lane[] = []; - for (const lane of found) { + for (const { path, name, head, branch } of found) { + const blocked = (problem: string) => + unready.push({ name, path, ...(branch ? { branch } : {}), problem }); + if (!branch) { + blocked("no branch"); + continue; + } // lane clones the project's ignored files, such as node_modules, into worktrees whose branch may // not ignore them, so untracked files can't block a move; they stay on this host. - const changed = lane.branch - ? (await output(lane.path, ["diff", "--name-only", "HEAD"])).split("\n").filter(Boolean) - : []; - if (!lane.branch) unready.push(`${lane.name} (no branch)`); - else if (changed.length) { + const changed = (await output(path, ["diff", "--name-only", "HEAD"])).split("\n") + .filter(Boolean); + if (changed.length) { const more = changed.length > 3 ? `, and ${changed.length - 3} more` : ""; - unready.push( - `${lane.name} (uncommitted changes to ${changed.slice(0, 3).join(", ")}${more})`, - ); - } else if (pushed.get(lane.branch) !== lane.head) unready.push(`${lane.name} (not pushed)`); + blocked(`uncommitted changes to ${changed.slice(0, 3).join(", ")}${more}`); + } else if (pushed.get(branch) !== head) blocked("not pushed"); else { - const base = (await git(root, ["config", "--get", `lane.${lane.branch}.base`])).stdout.trim(); - ready.push({ path: lane.path, branch: lane.branch, ...(base ? { base } : {}) }); + const base = (await git(root, ["config", "--get", `lane.${branch}.base`])).stdout.trim(); + ready.push({ path, branch, ...(base ? { base } : {}) }); } } - if (unready.length) { - throw new Error( - `The cloud workspace starts from a fresh clone, so commit and push these lanes first, or remove them: ${ - unready.join(", ") - }.`, - ); - } - return ready; + return { ready, unready }; +} + +/** The channel's lanes that must be committed and pushed before it moves to a cloud workspace. */ +export async function unready(record: catalog.Listing): Promise { + return (await lanes(record.project, record.prefix)).unready; } /** @@ -128,6 +135,14 @@ export async function adoption(record: catalog.Listing) { if (!url || !(await available(record.project))) { throw new Error("Moving to a cloud workspace needs Cloud compute and a GitHub project."); } + const { ready, unready } = await lanes(record.project, record.prefix); + if (unready.length) { + throw new Error( + `The cloud workspace starts from a fresh clone, so commit and push these lanes first, or remove them: ${ + listed(unready) + }.`, + ); + } return { url, body: { @@ -139,7 +154,7 @@ export async function adoption(record: catalog.Listing) { named: record.named, model: record.model, project: record.project, - lanes: await lanes(record.project, record.prefix), + lanes: ready, }, }; } diff --git a/apps/host/src/gateway.ts b/apps/host/src/gateway.ts index 988db731..5e04ec0a 100644 --- a/apps/host/src/gateway.ts +++ b/apps/host/src/gateway.ts @@ -694,11 +694,14 @@ async function handle( const first = !moves.has(request.channel); moves.add(request.channel); try { - const run = () => move(request.channel, request.target, (waiting) => send({ id, waiting })); // A move to a cloud workspace ends by starting it, which the app must wait out like Run in - // cloud before it opens the chat. - if (request.target === "compute") await lifecycle(request.channel, "starting", run); - else await run(); + // cloud before it opens the chat; the agent's lane push before it stays watchable. + await move( + request.channel, + request.target, + (waiting) => send({ id, waiting }), + (run) => lifecycle(request.channel, "starting", run), + ); } finally { if (first) moves.delete(request.channel); // A failed move lifts its fence, which listings and reopened chats must see. diff --git a/apps/host/src/move.ts b/apps/host/src/move.ts index 13162ddd..f42af649 100644 --- a/apps/host/src/move.ts +++ b/apps/host/src/move.ts @@ -13,7 +13,7 @@ import type { ChannelInfo } from "@ace/channel/protocol"; import * as catalog from "./catalog"; import { Connection, hostedAuth } from "./client"; -import { adoption, available, start } from "./compute"; +import { adoption, available, listed, start, type Unready, unready } from "./compute"; import * as directory from "./directory"; import { refusal } from "./http"; import { KEYCHAIN_WAITING } from "./keys"; @@ -168,6 +168,12 @@ function stranded(error: unknown, where: string): Error { }); } +function remains(record: catalog.Listing): string { + return record.hosted + ? `The channel is still on ${new URL(record.hosted).host}.` + : "The channel is still on this Mac."; +} + /** Worker startup checks the fence after taking this lock, so holding it keeps storage still. */ async function park(id: string): Promise { if (isWorkerAlive(id)) { @@ -263,9 +269,7 @@ async function toCompute( waiting: (text: string | null) => void, ): Promise { const { id, hosted: source } = record; - const where = source - ? `The channel is still on ${new URL(source).host}.` - : "The channel is still on this Mac."; + const where = remains(record); let digest = record.moving?.digest; // A resumed move froze its source on the first attempt; thawing an active one is refused. let freezing = !!digest; @@ -359,6 +363,61 @@ async function toLocal(auth: Auth, record: catalog.Listing): Promise { catalog.unlock(id); } +function request(lanes: Unready[]): string { + return [ + "Ace is moving this channel to a cloud workspace. The workspace starts from a fresh clone, so it can only check out lanes whose branches are pushed to origin. These lanes aren't ready:", + "", + ...lanes.map((lane) => + `- ${lane.name}, branch ${lane.branch}, at ${lane.path}: ${lane.problem}` + ), + "", + "For each lane, commit its work if it has uncommitted changes, leaving out dependencies and build output such as node_modules, then run `lane push` in its worktree so its branch reaches origin. Don't open a pull request, and don't remove any lane. The move continues once this run ends with every lane pushed.", + ].join("\n"); +} + +/** + * The channel's agent usually did a lane's work, so it pushes unready lanes before a move to a + * cloud workspace, in a run people can watch. A lane without a branch has nothing to push, so + * the person removes it. + */ +async function push( + record: catalog.Listing, + waiting: (text: string | null) => void, +): Promise { + const lanes = await unready(record); + if (!lanes.length) return; + const orphans = lanes.filter((lane) => !lane.branch).map((lane) => lane.name); + if (orphans.length) { + throw new Error( + `These lanes have no branch to push, so remove them first: ${orphans.join(", ")}`, + ); + } + const names = lanes.map((lane) => lane.name).join(", "); + const connection = await Connection.open(record.id); + let status: string; + try { + const ask = { op: "ask", author: catalog.user, text: request(lanes) } as const; + const { submission } = await connection.request<{ submission: number }>(ask); + waiting(`Waiting for the agent to push ${names}…`); + status = await connection.request({ op: "wait", submission }); + } finally { + connection.close(); + waiting(null); + } + const left = await unready(catalog.read(record.id)); + if (status !== "done") { + const unpushed = left.length ? `; these lanes still need pushing: ${listed(left)}` : ""; + throw new Error(`The agent's run was stopped, so the move stopped${unpushed}`); + } + if (left.length) { + throw new Error( + `The agent's run ended without pushing every lane, so commit and push these or remove them: ${ + listed(left) + }`, + ); + } +} + /** The channel's record, if it can move to `to` now: "local", "compute", or a canonical service URL. */ function ready(id: string, to: string): catalog.Listing { const record = catalog.read(id); @@ -373,21 +432,29 @@ function ready(id: string, to: string): catalog.Listing { /** * Move a channel to `target`: "local", "compute", or a hosting service URL. The frozen source is - * kept, not deleted. + * kept, not deleted. `starting` wraps a move to a cloud workspace from its fence through the + * workspace's start, which the app waits out before it opens the chat again. */ export async function move( id: string, target: string, waiting: (text: string | null) => void = () => {}, + starting: (run: () => Promise) => Promise = (run) => run(), ): Promise { const to = target === "local" || target === COMPUTE ? target : canonical(target); - ready(id, to); + const checked = ready(id, to); + // A resumed move is already fenced, so its lanes were ready before. + if (to === COMPUTE && !checked.moving) { + await push(checked, waiting).catch((error) => { + throw stranded(error, remains(checked)); + }); + } const auth = await credentials(waiting); // The record may have changed during the wait; nothing from here awaits before the fence. const record = ready(id, to); log("info", "move.start", { channel: id, from: record.hosted || "local", to }); if (to === "local") await toLocal(auth, record); - else if (to === COMPUTE) await toCompute(auth, record, waiting); + else if (to === COMPUTE) await starting(() => toCompute(auth, record, waiting)); else await toHosted(auth, record, to); log("info", "move.done", { channel: id, to }); } diff --git a/docs/architecture.md b/docs/architecture.md index 7d36abbe..dc0e5e6a 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -569,10 +569,17 @@ the same move retries activation. Watches on the old placement end and clients r When Cloud compute is available for the project, a local channel's menu, or a hosted channel's whose workspace is this host, also offers Move to cloud workspace (`ace move --compute`). -Its workspace starts from a fresh clone, so the host first refuses any of the channel's lanes that -have uncommitted or unpushed work. It then imports the snapshot through the compute service's -`import`, which stores the channel inactive with this host's project path and the lanes' branches, -activates it, saves the record as a stopped `compute` channel, and starts it. A local channel's +Its workspace starts from a fresh clone, so lanes travel only as pushed branches. When any lane has +uncommitted or unpushed work, the host first asks the channel's agent, in its first chat as the +owner, to commit each one and `lane push` it, without opening a pull request or removing lanes. The +move's progress waits for that run while the chat stays open, then the host checks the lanes again. +A stopped run or a lane still unready ends the move with the channel where it was, naming the lanes +left. A lane with no branch has nothing to push, so the move refuses it for the person to remove, +and a busy channel is refused before any of this. Only then does the host fence the channel, and +the listing reports `computing` from the fence until the workspace starts. The host imports the +snapshot through the compute service's `import`, which stores the channel inactive with this host's +project path and the lanes' branches, activates it, saves the record as a stopped `compute` +channel, and starts it. A local channel's snapshot comes from its parked store. A hosted channel's comes straight from its service, frozen and exported as in a move back, never through local storage; a failure before activation thaws it, and after activation the frozen object remains. The runner links that project path to its clone and From de669875bef8cf7bf7e39956e8a3d15784c4d305 Mon Sep 17 00:00:00 2001 From: iamnbutler Date: Mon, 19 Oct 2026 17:10:01 -0400 Subject: [PATCH 2/4] chore: confirm lane notes --- ...APK9YCQH467YNMKAC8BT5B-a-move-to-a-cloud-workspace.md | 0 ...YX7F0E4JW72ECJTK6SNZK-every-provider-link-opens-se.md | 2 +- ...Z0WJH0JC0ANYXJZEBSZFF-section-buttons-focus-themse.md | 2 +- ...HG0YFSH0H05S3Y0N16BKF-git-worktree-list-prints-rea.md | 8 ++++---- ...N9A1YPT5XAP3TS3ACX5BT-lane-clones-ignored-files-li.md | 7 ++++--- ...YVZ6KGNQ7MFS1RP81MQCE0-a-hosted-channel-s-location.md | 6 +++--- ...2BCKRNHVMYEMC0KH3Q6BN-a-hosted-channel-must-be-rea.md | 6 +++--- ...AZZV2CRMNFXZKM0B3AWRVX-a-move-to-a-cloud-workspace.md | 9 +++++++++ ...W0PE5A5HQNJTBG3961AF3-nothing-that-can-wait-on-a-p.md | 6 +++--- ...Z6NMTVNC1WH65TAV3JB3W-a-hosted-source-is-frozen-an.md | 5 +++-- ...ZZDBG1F569D8F4BWRV4PS-the-agent-s-push-runs-before.md | 8 ++++++++ ...ZZDC1TRZFJ8QCXFRSWH05-after-the-agent-s-run-the-re.md | 8 ++++++++ 12 files changed, 47 insertions(+), 20 deletions(-) rename .lane/{memory => attic}/apps/host/src/gateway.ts/01M5APK9YCQH467YNMKAC8BT5B-a-move-to-a-cloud-workspace.md (100%) create mode 100644 .lane/memory/apps/host/src/gateway.ts/01M5AZZV2CRMNFXZKM0B3AWRVX-a-move-to-a-cloud-workspace.md create mode 100644 .lane/memory/apps/host/src/move.ts/01M5AZZDBG1F569D8F4BWRV4PS-the-agent-s-push-runs-before.md create mode 100644 .lane/memory/apps/host/src/move.ts/01M5AZZDC1TRZFJ8QCXFRSWH05-after-the-agent-s-run-the-re.md diff --git a/.lane/memory/apps/host/src/gateway.ts/01M5APK9YCQH467YNMKAC8BT5B-a-move-to-a-cloud-workspace.md b/.lane/attic/apps/host/src/gateway.ts/01M5APK9YCQH467YNMKAC8BT5B-a-move-to-a-cloud-workspace.md similarity index 100% rename from .lane/memory/apps/host/src/gateway.ts/01M5APK9YCQH467YNMKAC8BT5B-a-move-to-a-cloud-workspace.md rename to .lane/attic/apps/host/src/gateway.ts/01M5APK9YCQH467YNMKAC8BT5B-a-move-to-a-cloud-workspace.md diff --git a/.lane/memory/apps/app/src/settings.tsx/01M5AYX7F0E4JW72ECJTK6SNZK-every-provider-link-opens-se.md b/.lane/memory/apps/app/src/settings.tsx/01M5AYX7F0E4JW72ECJTK6SNZK-every-provider-link-opens-se.md index 93986cf1..e71d4787 100644 --- a/.lane/memory/apps/app/src/settings.tsx/01M5AYX7F0E4JW72ECJTK6SNZK-every-provider-link-opens-se.md +++ b/.lane/memory/apps/app/src/settings.tsx/01M5AYX7F0E4JW72ECJTK6SNZK-every-provider-link-opens-se.md @@ -6,7 +6,7 @@ norm: '1' sig: 8d6f8142ef1a23f2 body_hash: ab1332a896bdd893 raw_hash: b048e59ec08c6130 -vouched: 2026-10-19T20:57:54Z +vouched: 2026-10-19T21:09:57Z lines: 325-537 --- diff --git a/.lane/memory/apps/app/src/settings.tsx/01M5AZ0WJH0JC0ANYXJZEBSZFF-section-buttons-focus-themse.md b/.lane/memory/apps/app/src/settings.tsx/01M5AZ0WJH0JC0ANYXJZEBSZFF-section-buttons-focus-themse.md index 9e0a0458..24a2e498 100644 --- a/.lane/memory/apps/app/src/settings.tsx/01M5AZ0WJH0JC0ANYXJZEBSZFF-section-buttons-focus-themse.md +++ b/.lane/memory/apps/app/src/settings.tsx/01M5AZ0WJH0JC0ANYXJZEBSZFF-section-buttons-focus-themse.md @@ -6,7 +6,7 @@ norm: '1' sig: 8d6f8142ef1a23f2 body_hash: ab1332a896bdd893 raw_hash: b048e59ec08c6130 -vouched: 2026-10-19T20:57:54Z +vouched: 2026-10-19T21:09:57Z lines: 325-545 --- diff --git a/.lane/memory/apps/host/src/compute.ts/01M5AHG0YFSH0H05S3Y0N16BKF-git-worktree-list-prints-rea.md b/.lane/memory/apps/host/src/compute.ts/01M5AHG0YFSH0H05S3Y0N16BKF-git-worktree-list-prints-rea.md index 0328e5cc..c0fae969 100644 --- a/.lane/memory/apps/host/src/compute.ts/01M5AHG0YFSH0H05S3Y0N16BKF-git-worktree-list-prints-rea.md +++ b/.lane/memory/apps/host/src/compute.ts/01M5AHG0YFSH0H05S3Y0N16BKF-git-worktree-list-prints-rea.md @@ -3,10 +3,10 @@ id: 01M5AHG0YFSH0H05S3Y0N16BKF anchor: function lanes created: 2026-10-19T16:56:39Z norm: '1' -sig: a117f6b009a0a5ab -body_hash: 608b9efe149e4af0 -raw_hash: eaf0bb12be123591 -vouched: 2026-10-19T18:03:00Z +sig: 258bf555fdfa6445 +body_hash: 163dbfda499c9a1e +raw_hash: 545a08e7b1625938 +vouched: 2026-10-19T21:09:57Z lines: 67-112 --- diff --git a/.lane/memory/apps/host/src/compute.ts/01M5AN9A1YPT5XAP3TS3ACX5BT-lane-clones-ignored-files-li.md b/.lane/memory/apps/host/src/compute.ts/01M5AN9A1YPT5XAP3TS3ACX5BT-lane-clones-ignored-files-li.md index c5012652..fc370426 100644 --- a/.lane/memory/apps/host/src/compute.ts/01M5AN9A1YPT5XAP3TS3ACX5BT-lane-clones-ignored-files-li.md +++ b/.lane/memory/apps/host/src/compute.ts/01M5AN9A1YPT5XAP3TS3ACX5BT-lane-clones-ignored-files-li.md @@ -3,9 +3,10 @@ id: 01M5AN9A1YPT5XAP3TS3ACX5BT anchor: function lanes created: 2026-10-19T18:02:53Z norm: '1' -sig: a117f6b009a0a5ab -body_hash: 608b9efe149e4af0 -raw_hash: eaf0bb12be123591 +sig: 258bf555fdfa6445 +body_hash: 163dbfda499c9a1e +raw_hash: 545a08e7b1625938 +vouched: 2026-10-19T21:09:57Z lines: 67-120 --- diff --git a/.lane/memory/apps/host/src/gateway.ts/01M4YVZ6KGNQ7MFS1RP81MQCE0-a-hosted-channel-s-location.md b/.lane/memory/apps/host/src/gateway.ts/01M4YVZ6KGNQ7MFS1RP81MQCE0-a-hosted-channel-s-location.md index 7a8a863f..d04b4634 100644 --- a/.lane/memory/apps/host/src/gateway.ts/01M4YVZ6KGNQ7MFS1RP81MQCE0-a-hosted-channel-s-location.md +++ b/.lane/memory/apps/host/src/gateway.ts/01M4YVZ6KGNQ7MFS1RP81MQCE0-a-hosted-channel-s-location.md @@ -4,9 +4,9 @@ anchor: function handle created: 2026-10-15T04:08:49Z norm: '1' sig: e597b5fbc8e664ce -body_hash: 0dbbb4a75790fa93 -raw_hash: 9d6d9723ef47a7dd -vouched: 2026-10-19T21:09:44Z +body_hash: 912de54b0c4933cc +raw_hash: ccd57e839b4e2227 +vouched: 2026-10-19T21:09:57Z lines: 266-519 --- diff --git a/.lane/memory/apps/host/src/gateway.ts/01M4Z2BCKRNHVMYEMC0KH3Q6BN-a-hosted-channel-must-be-rea.md b/.lane/memory/apps/host/src/gateway.ts/01M4Z2BCKRNHVMYEMC0KH3Q6BN-a-hosted-channel-must-be-rea.md index 7364626c..6d575e05 100644 --- a/.lane/memory/apps/host/src/gateway.ts/01M4Z2BCKRNHVMYEMC0KH3Q6BN-a-hosted-channel-must-be-rea.md +++ b/.lane/memory/apps/host/src/gateway.ts/01M4Z2BCKRNHVMYEMC0KH3Q6BN-a-hosted-channel-must-be-rea.md @@ -4,9 +4,9 @@ anchor: function handle created: 2026-10-15T06:00:20Z norm: '1' sig: e597b5fbc8e664ce -body_hash: 0dbbb4a75790fa93 -raw_hash: 9d6d9723ef47a7dd -vouched: 2026-10-19T21:09:44Z +body_hash: 912de54b0c4933cc +raw_hash: ccd57e839b4e2227 +vouched: 2026-10-19T21:09:57Z lines: 281-561 --- diff --git a/.lane/memory/apps/host/src/gateway.ts/01M5AZZV2CRMNFXZKM0B3AWRVX-a-move-to-a-cloud-workspace.md b/.lane/memory/apps/host/src/gateway.ts/01M5AZZV2CRMNFXZKM0B3AWRVX-a-move-to-a-cloud-workspace.md new file mode 100644 index 00000000..807d51da --- /dev/null +++ b/.lane/memory/apps/host/src/gateway.ts/01M5AZZV2CRMNFXZKM0B3AWRVX-a-move-to-a-cloud-workspace.md @@ -0,0 +1,9 @@ +--- +id: 01M5AZZV2CRMNFXZKM0B3AWRVX +anchor: function handle +created: 2026-10-19T21:09:57Z +norm: '1' +supersedes: 01M5APK9YCQH467YNMKAC8BT5B +--- + +a move to a cloud workspace ends by starting it, so the listing reports computing from the move's fence through the start (move's starting wrapper); the agent's lane push runs before that so its chat stays open diff --git a/.lane/memory/apps/host/src/move.ts/01M4YW0PE5A5HQNJTBG3961AF3-nothing-that-can-wait-on-a-p.md b/.lane/memory/apps/host/src/move.ts/01M4YW0PE5A5HQNJTBG3961AF3-nothing-that-can-wait-on-a-p.md index 4ccd461d..389040d8 100644 --- a/.lane/memory/apps/host/src/move.ts/01M4YW0PE5A5HQNJTBG3961AF3-nothing-that-can-wait-on-a-p.md +++ b/.lane/memory/apps/host/src/move.ts/01M4YW0PE5A5HQNJTBG3961AF3-nothing-that-can-wait-on-a-p.md @@ -4,9 +4,9 @@ anchor: function move created: 2026-10-15T04:09:38Z norm: '1' sig: 8cd687d651283c6b -body_hash: 1bd46335713b89d5 -raw_hash: 7a9e8ac3b20028f5 -vouched: 2026-10-19T16:56:54Z +body_hash: 2ff747cd385d660c +raw_hash: 3689b92e77a90616 +vouched: 2026-10-19T21:09:57Z lines: 257-271 --- diff --git a/.lane/memory/apps/host/src/move.ts/01M5AZ6NMTVNC1WH65TAV3JB3W-a-hosted-source-is-frozen-an.md b/.lane/memory/apps/host/src/move.ts/01M5AZ6NMTVNC1WH65TAV3JB3W-a-hosted-source-is-frozen-an.md index 5bf1362e..e211c5ee 100644 --- a/.lane/memory/apps/host/src/move.ts/01M5AZ6NMTVNC1WH65TAV3JB3W-a-hosted-source-is-frozen-an.md +++ b/.lane/memory/apps/host/src/move.ts/01M5AZ6NMTVNC1WH65TAV3JB3W-a-hosted-source-is-frozen-an.md @@ -4,8 +4,9 @@ anchor: function toCompute created: 2026-10-19T20:56:11Z norm: '1' sig: 21af9e438a769213 -body_hash: 8574c71308234fbf -raw_hash: beeaf7b2694ba561 +body_hash: 06f3a62fc503ff0b +raw_hash: ee652da2e9d57120 +vouched: 2026-10-19T21:09:57Z lines: 260-326 --- diff --git a/.lane/memory/apps/host/src/move.ts/01M5AZZDBG1F569D8F4BWRV4PS-the-agent-s-push-runs-before.md b/.lane/memory/apps/host/src/move.ts/01M5AZZDBG1F569D8F4BWRV4PS-the-agent-s-push-runs-before.md new file mode 100644 index 00000000..047091fa --- /dev/null +++ b/.lane/memory/apps/host/src/move.ts/01M5AZZDBG1F569D8F4BWRV4PS-the-agent-s-push-runs-before.md @@ -0,0 +1,8 @@ +--- +id: 01M5AZZDBG1F569D8F4BWRV4PS +anchor: function push +created: 2026-10-19T21:09:43Z +norm: '1' +--- + +the agent's push runs before the fence and outside `starting`, so its run stays watchable; a lane with no branch is refused, never sent to the agent diff --git a/.lane/memory/apps/host/src/move.ts/01M5AZZDC1TRZFJ8QCXFRSWH05-after-the-agent-s-run-the-re.md b/.lane/memory/apps/host/src/move.ts/01M5AZZDC1TRZFJ8QCXFRSWH05-after-the-agent-s-run-the-re.md new file mode 100644 index 00000000..734ed956 --- /dev/null +++ b/.lane/memory/apps/host/src/move.ts/01M5AZZDC1TRZFJ8QCXFRSWH05-after-the-agent-s-run-the-re.md @@ -0,0 +1,8 @@ +--- +id: 01M5AZZDC1TRZFJ8QCXFRSWH05 +anchor: function move +created: 2026-10-19T21:09:43Z +norm: '1' +--- + +after the agent's run the record is checked again with ready() before the fence, since the run can take minutes From bd7b904c949e4922f8ea565d767ec385ce83c08f Mon Sep 17 00:00:00 2001 From: iamnbutler Date: Mon, 19 Oct 2026 17:10:02 -0400 Subject: [PATCH 3/4] lane: sync lane-push memory --- .../01M5AZZV2CRMNFXZKM0B3AWRVX-a-move-to-a-cloud-workspace.md | 4 ++++ ...01M5AZZDBG1F569D8F4BWRV4PS-the-agent-s-push-runs-before.md | 4 ++++ ...01M5AZZDC1TRZFJ8QCXFRSWH05-after-the-agent-s-run-the-re.md | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/.lane/memory/apps/host/src/gateway.ts/01M5AZZV2CRMNFXZKM0B3AWRVX-a-move-to-a-cloud-workspace.md b/.lane/memory/apps/host/src/gateway.ts/01M5AZZV2CRMNFXZKM0B3AWRVX-a-move-to-a-cloud-workspace.md index 807d51da..8aea446e 100644 --- a/.lane/memory/apps/host/src/gateway.ts/01M5AZZV2CRMNFXZKM0B3AWRVX-a-move-to-a-cloud-workspace.md +++ b/.lane/memory/apps/host/src/gateway.ts/01M5AZZV2CRMNFXZKM0B3AWRVX-a-move-to-a-cloud-workspace.md @@ -3,6 +3,10 @@ id: 01M5AZZV2CRMNFXZKM0B3AWRVX anchor: function handle created: 2026-10-19T21:09:57Z norm: '1' +sig: e597b5fbc8e664ce +body_hash: 912de54b0c4933cc +raw_hash: ccd57e839b4e2227 +lines: 384-700 supersedes: 01M5APK9YCQH467YNMKAC8BT5B --- diff --git a/.lane/memory/apps/host/src/move.ts/01M5AZZDBG1F569D8F4BWRV4PS-the-agent-s-push-runs-before.md b/.lane/memory/apps/host/src/move.ts/01M5AZZDBG1F569D8F4BWRV4PS-the-agent-s-push-runs-before.md index 047091fa..eda7cb4e 100644 --- a/.lane/memory/apps/host/src/move.ts/01M5AZZDBG1F569D8F4BWRV4PS-the-agent-s-push-runs-before.md +++ b/.lane/memory/apps/host/src/move.ts/01M5AZZDBG1F569D8F4BWRV4PS-the-agent-s-push-runs-before.md @@ -3,6 +3,10 @@ id: 01M5AZZDBG1F569D8F4BWRV4PS anchor: function push created: 2026-10-19T21:09:43Z norm: '1' +sig: 0b04ba9ef26235c0 +body_hash: bff338d65e17cfc5 +raw_hash: 5a490b1d9f3f458f +lines: 383-419 --- the agent's push runs before the fence and outside `starting`, so its run stays watchable; a lane with no branch is refused, never sent to the agent diff --git a/.lane/memory/apps/host/src/move.ts/01M5AZZDC1TRZFJ8QCXFRSWH05-after-the-agent-s-run-the-re.md b/.lane/memory/apps/host/src/move.ts/01M5AZZDC1TRZFJ8QCXFRSWH05-after-the-agent-s-run-the-re.md index 734ed956..fb1a2c3b 100644 --- a/.lane/memory/apps/host/src/move.ts/01M5AZZDC1TRZFJ8QCXFRSWH05-after-the-agent-s-run-the-re.md +++ b/.lane/memory/apps/host/src/move.ts/01M5AZZDC1TRZFJ8QCXFRSWH05-after-the-agent-s-run-the-re.md @@ -3,6 +3,10 @@ id: 01M5AZZDC1TRZFJ8QCXFRSWH05 anchor: function move created: 2026-10-19T21:09:43Z norm: '1' +sig: 8cd687d651283c6b +body_hash: 2ff747cd385d660c +raw_hash: 3689b92e77a90616 +lines: 438-460 --- after the agent's run the record is checked again with ready() before the fence, since the run can take minutes From b885fe18fea06926196167f83517abd55e8e585c Mon Sep 17 00:00:00 2001 From: iamnbutler Date: Mon, 19 Oct 2026 17:15:07 -0400 Subject: [PATCH 4/4] chore: confirm gateway notes after rebase --- ...1M4YVZ6KGNQ7MFS1RP81MQCE0-a-hosted-channel-s-location.md | 6 +++--- ...M4Z2BCKRNHVMYEMC0KH3Q6BN-a-hosted-channel-must-be-rea.md | 6 +++--- ...1M5AZZV2CRMNFXZKM0B3AWRVX-a-move-to-a-cloud-workspace.md | 5 +++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.lane/memory/apps/host/src/gateway.ts/01M4YVZ6KGNQ7MFS1RP81MQCE0-a-hosted-channel-s-location.md b/.lane/memory/apps/host/src/gateway.ts/01M4YVZ6KGNQ7MFS1RP81MQCE0-a-hosted-channel-s-location.md index d04b4634..060edca9 100644 --- a/.lane/memory/apps/host/src/gateway.ts/01M4YVZ6KGNQ7MFS1RP81MQCE0-a-hosted-channel-s-location.md +++ b/.lane/memory/apps/host/src/gateway.ts/01M4YVZ6KGNQ7MFS1RP81MQCE0-a-hosted-channel-s-location.md @@ -4,9 +4,9 @@ anchor: function handle created: 2026-10-15T04:08:49Z norm: '1' sig: e597b5fbc8e664ce -body_hash: 912de54b0c4933cc -raw_hash: ccd57e839b4e2227 -vouched: 2026-10-19T21:09:57Z +body_hash: 28e153c79ae61537 +raw_hash: 640ff85ff07439ad +vouched: 2026-10-19T21:15:06Z lines: 266-519 --- diff --git a/.lane/memory/apps/host/src/gateway.ts/01M4Z2BCKRNHVMYEMC0KH3Q6BN-a-hosted-channel-must-be-rea.md b/.lane/memory/apps/host/src/gateway.ts/01M4Z2BCKRNHVMYEMC0KH3Q6BN-a-hosted-channel-must-be-rea.md index 6d575e05..deafa3d7 100644 --- a/.lane/memory/apps/host/src/gateway.ts/01M4Z2BCKRNHVMYEMC0KH3Q6BN-a-hosted-channel-must-be-rea.md +++ b/.lane/memory/apps/host/src/gateway.ts/01M4Z2BCKRNHVMYEMC0KH3Q6BN-a-hosted-channel-must-be-rea.md @@ -4,9 +4,9 @@ anchor: function handle created: 2026-10-15T06:00:20Z norm: '1' sig: e597b5fbc8e664ce -body_hash: 912de54b0c4933cc -raw_hash: ccd57e839b4e2227 -vouched: 2026-10-19T21:09:57Z +body_hash: 28e153c79ae61537 +raw_hash: 640ff85ff07439ad +vouched: 2026-10-19T21:15:07Z lines: 281-561 --- diff --git a/.lane/memory/apps/host/src/gateway.ts/01M5AZZV2CRMNFXZKM0B3AWRVX-a-move-to-a-cloud-workspace.md b/.lane/memory/apps/host/src/gateway.ts/01M5AZZV2CRMNFXZKM0B3AWRVX-a-move-to-a-cloud-workspace.md index 8aea446e..1426f5a3 100644 --- a/.lane/memory/apps/host/src/gateway.ts/01M5AZZV2CRMNFXZKM0B3AWRVX-a-move-to-a-cloud-workspace.md +++ b/.lane/memory/apps/host/src/gateway.ts/01M5AZZV2CRMNFXZKM0B3AWRVX-a-move-to-a-cloud-workspace.md @@ -4,8 +4,9 @@ anchor: function handle created: 2026-10-19T21:09:57Z norm: '1' sig: e597b5fbc8e664ce -body_hash: 912de54b0c4933cc -raw_hash: ccd57e839b4e2227 +body_hash: 28e153c79ae61537 +raw_hash: 640ff85ff07439ad +vouched: 2026-10-19T21:15:07Z lines: 384-700 supersedes: 01M5APK9YCQH467YNMKAC8BT5B ---