fix wrong herdr response parse + add labels and branch names to delegations
CI / verify (pull_request) Failing after 8s

This commit is contained in:
2026-09-17 21:32:26 +02:00
parent 5cf0beb74b
commit 9494936160
9 changed files with 291 additions and 38 deletions
+21 -6
View File
@@ -109,7 +109,13 @@ export class Herdr {
return matches.length === 1 ? matches[0] : undefined;
}
async create(root: HerdrRoot, base: string, branch: string, signal: AbortSignal) {
async create(
root: HerdrRoot,
base: string,
branch: string,
signal: AbortSignal,
label?: string,
) {
const current = await this.root(root.directory, signal);
if (!current || current.workspaceID !== root.workspaceID || current.repo !== root.repo)
throw new Error("Herdr root workspace is no longer eligible");
@@ -150,6 +156,7 @@ export class Herdr {
branch,
"--base",
commit,
...(label === undefined ? [] : ["--label", label]),
"--no-focus",
],
root.directory,
@@ -185,7 +192,7 @@ export class Herdr {
throw new Error(
"Created checkout does not match the requested repository and base",
);
await this.verify(directory, workspaceID, root.repo, branch, signal);
await this.verify(directory, workspaceID, root, branch, signal);
} catch (error) {
throw new Error(
`Retained Herdr workspace ${workspaceID}, checkout ${directory}, branch ${branch}`,
@@ -198,7 +205,7 @@ export class Herdr {
async verify(
directory: string,
workspaceID: string,
repo: string,
root: HerdrRoot,
branch: string,
signal: AbortSignal,
) {
@@ -214,12 +221,20 @@ export class Herdr {
const checkout = value.result.worktrees.find(
(item) => item.open_workspace_id === workspaceID && item.is_linked_worktree,
);
const sourceCheckout = value.result.worktrees.find(
(item) =>
item.open_workspace_id === value.result.source.source_workspace_id &&
!item.is_linked_worktree &&
!item.is_bare,
);
if (
!checkout ||
value.result.source.source_workspace_id !== workspaceID ||
(await realpath(value.result.source.source_checkout_path)) !== directory ||
!sourceCheckout ||
value.result.source.source_workspace_id !== root.workspaceID ||
(await realpath(value.result.source.source_checkout_path)) !== root.directory ||
(await realpath(sourceCheckout.path)) !== root.directory ||
(await realpath(checkout.path)) !== directory ||
(await realpath(value.result.source.repo_key)) !== repo
(await realpath(value.result.source.repo_key)) !== root.repo
)
throw new Error("Bound Herdr checkout is no longer available");
if (
+16 -5
View File
@@ -28,7 +28,11 @@ const inputSchema = z
sessionID: z.string().optional(),
background: z.boolean().optional(),
worktree: z
.object({ base: z.string().trim().min(1) })
.object({
base: z.string().trim().min(1),
branch: z.string().trim().min(1).optional(),
label: z.string().trim().min(1).optional(),
})
.strict()
.optional(),
})
@@ -180,7 +184,7 @@ export const installMultiWork = (
agent: { type: "string", enum: ["fast-lo", "coordinate"] },
};
tool.description =
"Delegate to fast-lo locally, or coordinate in a new Herdr worktree using worktree: {base}. Continue an owned coordinate child with sessionID and no worktree argument. Native background and results apply.";
"Delegate to fast-lo locally, or coordinate in a new Herdr worktree using worktree: {base, branch?, label?}. Prefer descriptive human-readable branch and label values, for example {base:'main',branch:'fix/login-timeout',label:'Fix login timeout'}. Continue an owned coordinate child with sessionID and no worktree argument; naming fields are create-only. Native background and results apply.";
}
}).pipe(Effect.orDie),
);
@@ -275,7 +279,7 @@ export const installMultiWork = (
herdr.verify(
binding.directory,
binding.workspaceID,
binding.root.repo,
binding.root,
binding.branch,
signal,
),
@@ -319,7 +323,8 @@ export const installMultiWork = (
throw new Error(
"Native executor returned an unexpected child",
);
const branch = `multi-work/${randomUUID()}`;
const branch =
worktree.branch ?? `multi-work/${randomUUID()}`;
yield* context.storage.set(callKey, {
child: id,
branch,
@@ -327,7 +332,13 @@ export const installMultiWork = (
});
yield* call.progress({ ...metadata, branch });
const destination = yield* Effect.tryPromise((signal) =>
herdr.create(root, worktree.base, branch, signal),
herdr.create(
root,
worktree.base,
branch,
signal,
worktree.label,
),
);
const binding = {
owner: call.sessionID,
+6 -4
View File
@@ -16,12 +16,14 @@ export const descriptions = {
export const multiWorkGuide = `You are multi-work, a primary orchestrator in a verified Herdr root checkout.
Read, plan, track todos and delegate; never implement or execute shell commands yourself.
Use native subagent with agent fast-lo for focused local retrieval. Use agent coordinate
for work in a new Herdr worktree, supplying worktree: {base: "source-branch"}, description,
and a complete prompt with scope, constraints and acceptance checks. The deterministic tool
creates a unique branch from that locally available source branch, moves the coordinate child
for work in a new Herdr worktree, supplying descriptive human-readable names such as
worktree: {base: "main", branch: "fix/login-timeout", label: "Fix login timeout"}, plus
description and a complete prompt with scope, constraints and acceptance checks. The tool
uses the requested branch and Herdr workspace label; when omitted, it creates the prior unique
multi-work/<uuid> branch and lets Herdr choose its default label. It moves the coordinate child
before its task begins, and preserves your root session. Dirty root changes are not copied.
Resume an owned coordinate child using its sessionID and no worktree argument. Never switch
its agent or location. Only fast-lo and coordinate are permitted. Workers are leaves.
its agent or location; naming fields are create-only. Only fast-lo and coordinate are permitted. Workers are leaves.
Parallelize independent work with disjoint ownership; serialize dependencies. Native background
delegation returns immediately and notifies you when complete; do not poll or duplicate work.
Reconcile results and report checkout, branch and child IDs. Retained resources on failure