diff --git a/workflows/gitea-issue-solver/moderator.ts b/workflows/gitea-issue-solver/moderator.ts index cdfd0e6..402cfee 100644 --- a/workflows/gitea-issue-solver/moderator.ts +++ b/workflows/gitea-issue-solver/moderator.ts @@ -18,16 +18,23 @@ export type WorkflowMeta = { "pr-publisher": PrPublisherMeta; }; -const MAX_IMPLEMENTER_SELF_ITERATIONS = 5; -const MAX_REVIEWER_REJECTIONS = 3; -const MAX_TESTER_REJECTIONS = 3; +const MAX_IMPLEMENTER_ROUNDS = 20; +const MAX_TOTAL_REJECTIONS = 10; -function countRejections(steps: { role: string; meta: unknown }[], rejector: string, metaKey: string): number { - return steps.filter((s) => s.role === rejector && !(s.meta as Record)[metaKey]).length; +function implementerRounds(steps: { role: string }[]): number { + return steps.filter((s) => s.role === "implementer").length; } -function countImplementerSelfIterations(steps: { role: string; meta: unknown }[]): number { - return steps.filter((s) => s.role === "implementer" && !(s.meta as Record).implementationOk).length; +function totalRejections(steps: { role: string; meta: unknown }[]): number { + return steps.filter((s) => { + if (s.role === "reviewer") return !(s.meta as Record).approved; + if (s.role === "tester") return !(s.meta as Record).passed; + return false; + }).length; +} + +function canRetryImplementer(steps: { role: string; meta: unknown }[]): boolean { + return implementerRounds(steps) < MAX_IMPLEMENTER_ROUNDS && totalRejections(steps) < MAX_TOTAL_REJECTIONS; } export const moderator: Moderator = (context) => { @@ -55,18 +62,18 @@ export const moderator: Moderator = (context) => { if (last.role === "implementer") { const meta = last.meta as WorkflowMeta["implementer"]; if (meta.implementationOk) return "reviewer"; - return countImplementerSelfIterations(context.steps) < MAX_IMPLEMENTER_SELF_ITERATIONS ? "implementer" : END; + return canRetryImplementer(context.steps) ? "implementer" : END; } if (last.role === "reviewer") { if (last.meta.approved) return "tester"; - return countRejections(context.steps, "reviewer", "approved") < MAX_REVIEWER_REJECTIONS ? "implementer" : END; + return canRetryImplementer(context.steps) ? "implementer" : END; } if (last.role === "tester") { const meta = last.meta as WorkflowMeta["tester"]; if (meta.passed) return "pr-publisher"; - return countRejections(context.steps, "tester", "passed") < MAX_TESTER_REJECTIONS ? "implementer" : END; + return canRetryImplementer(context.steps) ? "implementer" : END; } if (last.role === "pr-publisher") { diff --git a/workflows/sense-generator/moderator.ts b/workflows/sense-generator/moderator.ts index ac941ef..c9d49e0 100644 --- a/workflows/sense-generator/moderator.ts +++ b/workflows/sense-generator/moderator.ts @@ -14,17 +14,24 @@ export type SenseMeta = { committer: CommitterMeta; }; -const MAX_CODER_SELF_ITERATIONS = 5; -const MAX_REVIEWER_REJECTIONS = 3; -const MAX_TESTER_REJECTIONS = 3; -const MAX_COMMITTER_REJECTIONS = 2; +const MAX_CODER_ROUNDS = 20; +const MAX_TOTAL_REJECTIONS = 10; -function countRejections(steps: { role: string; meta: unknown }[], rejector: string, metaKey: string): number { - return steps.filter((s) => s.role === rejector && !(s.meta as Record)[metaKey]).length; +function coderRounds(steps: { role: string }[]): number { + return steps.filter((s) => s.role === "coder").length; } -function countCoderSelfIterations(steps: { role: string; meta: unknown }[]): number { - return steps.filter((s) => s.role === "coder" && !(s.meta as Record).done).length; +function totalRejections(steps: { role: string; meta: unknown }[]): number { + return steps.filter((s) => { + if (s.role === "reviewer") return !(s.meta as Record).approved; + if (s.role === "tester") return !(s.meta as Record).passed; + if (s.role === "committer") return !(s.meta as Record).success; + return false; + }).length; +} + +function canRetryCoder(steps: { role: string; meta: unknown }[]): boolean { + return coderRounds(steps) < MAX_CODER_ROUNDS && totalRejections(steps) < MAX_TOTAL_REJECTIONS; } export const moderator: Moderator = (context) => { @@ -36,22 +43,22 @@ export const moderator: Moderator = (context) => { if (last.role === "coder") { if (last.meta.done) return "reviewer"; - return countCoderSelfIterations(context.steps) < MAX_CODER_SELF_ITERATIONS ? "coder" : END; + return canRetryCoder(context.steps) ? "coder" : END; } if (last.role === "reviewer") { if (last.meta.approved) return "tester"; - return countRejections(context.steps, "reviewer", "approved") < MAX_REVIEWER_REJECTIONS ? "coder" : END; + return canRetryCoder(context.steps) ? "coder" : END; } if (last.role === "tester") { if (last.meta.passed) return "committer"; - return countRejections(context.steps, "tester", "passed") < MAX_TESTER_REJECTIONS ? "coder" : END; + return canRetryCoder(context.steps) ? "coder" : END; } if (last.role === "committer") { if (last.meta.success) return END; - return countRejections(context.steps, "committer", "success") < MAX_COMMITTER_REJECTIONS ? "coder" : END; + return canRetryCoder(context.steps) ? "coder" : END; } return END; diff --git a/workflows/workflow-generator/moderator.ts b/workflows/workflow-generator/moderator.ts index dfec6b3..dd5aa27 100644 --- a/workflows/workflow-generator/moderator.ts +++ b/workflows/workflow-generator/moderator.ts @@ -14,18 +14,24 @@ export type WorkflowMeta = { committer: CommitterMeta; }; -const MAX_CODER_SELF_ITERATIONS = 5; -const MAX_REVIEWER_REJECTIONS = 3; -const MAX_TESTER_REJECTIONS = 3; -const MAX_COMMITTER_REJECTIONS = 2; +const MAX_CODER_ROUNDS = 20; +const MAX_TOTAL_REJECTIONS = 10; -function countRejections(steps: { role: string; meta: unknown }[], rejector: string, metaKey: string): number { - return steps.filter((s) => s.role === rejector && !(s.meta as Record)[metaKey]).length; +function coderRounds(steps: { role: string }[]): number { + return steps.filter((s) => s.role === "coder").length; } -function countCoderSelfIterations(steps: { role: string; meta: unknown }[]): number { - // coder rounds where done=false (self-iteration, not a rejection retry) - return steps.filter((s) => s.role === "coder" && !(s.meta as Record).done).length; +function totalRejections(steps: { role: string; meta: unknown }[]): number { + return steps.filter((s) => { + if (s.role === "reviewer") return !(s.meta as Record).approved; + if (s.role === "tester") return !(s.meta as Record).passed; + if (s.role === "committer") return !(s.meta as Record).success; + return false; + }).length; +} + +function canRetryCoder(steps: { role: string; meta: unknown }[]): boolean { + return coderRounds(steps) < MAX_CODER_ROUNDS && totalRejections(steps) < MAX_TOTAL_REJECTIONS; } export const moderator: Moderator = (context) => { @@ -39,23 +45,22 @@ export const moderator: Moderator = (context) => { if (last.role === "coder") { if (last.meta.done) return "reviewer"; - // coder says not done yet — allow self-iteration - return countCoderSelfIterations(context.steps) < MAX_CODER_SELF_ITERATIONS ? "coder" : END; + return canRetryCoder(context.steps) ? "coder" : END; } if (last.role === "reviewer") { if (last.meta.approved) return "tester"; - return countRejections(context.steps, "reviewer", "approved") < MAX_REVIEWER_REJECTIONS ? "coder" : END; + return canRetryCoder(context.steps) ? "coder" : END; } if (last.role === "tester") { if (last.meta.passed) return "committer"; - return countRejections(context.steps, "tester", "passed") < MAX_TESTER_REJECTIONS ? "coder" : END; + return canRetryCoder(context.steps) ? "coder" : END; } if (last.role === "committer") { if (last.meta.success) return END; - return countRejections(context.steps, "committer", "success") < MAX_COMMITTER_REJECTIONS ? "coder" : END; + return canRetryCoder(context.steps) ? "coder" : END; } return END;