feat: committer distinguishes recoverable vs unrecoverable failures

CommitterMeta is now a 3-way discriminated union:
- committed: success with branch + commitSha
- recoverable: coder can fix (hook failures, lint, test, conflicts)
- unrecoverable: can't be fixed by code (auth, permissions, disk)

Moderator routes recoverable → coder for retry.
This commit is contained in:
2026-05-06 10:53:17 +00:00
parent 267ca73a1b
commit 196562c82a
3 changed files with 17 additions and 6 deletions
@@ -68,7 +68,7 @@ describe("createCommitterRole", () => {
test("returns failed meta when extraction reports failure", async () => {
const failed = {
status: "failed" as const,
status: "recoverable" as const,
error: "working tree clean; nothing to commit",
logRef: null as string | null,
};
@@ -91,7 +91,7 @@ describe("createCommitterRole", () => {
test("returns failed meta with logRef when extraction includes it", async () => {
const failed = {
status: "failed" as const,
status: "recoverable" as const,
error: "push rejected",
logRef: "LOGREF01",
};
@@ -125,7 +125,7 @@ describe("createCommitterRole", () => {
const out = await role(makeCtx());
expect(out.meta).toEqual({
status: "failed",
status: "unrecoverable",
error: "committer role threw before structured result",
logRef: null,
});
@@ -15,7 +15,12 @@ export const committerMetaSchema = z.discriminatedUnion("status", [
commitSha: z.string(),
}),
z.object({
status: z.literal("failed"),
status: z.literal("recoverable"),
error: z.string(),
logRef: z.string().nullable(),
}),
z.object({
status: z.literal("unrecoverable"),
error: z.string(),
logRef: z.string().nullable(),
}),
@@ -54,7 +59,10 @@ Create a branch, commit the changes, and push. Report whether the push succeeded
## On failure
If any git operation fails (hook rejection, push denied, merge conflict, etc.), **do not attempt to fix it yourself**. Your job is to capture the key error output and report it back clearly so other roles in the workflow can address it.`;
If any git operation fails, **do not attempt to fix it yourself**. Capture the key error output and classify it:
- **Recoverable**: failures that a coder can fix (lint/test hook rejection, merge conflict, commit validation errors)
- **Unrecoverable**: failures beyond code changes (no push permission, remote not found, authentication denied, disk full)`;
}
/**
@@ -83,7 +91,7 @@ export function createCommitterRole(
onFail<CommitterMeta>({
label: "committer",
meta: {
status: "failed",
status: "unrecoverable",
error: "committer role threw before structured result",
logRef: null,
},