From 512c2f749a6ec6044389557b9d94e5824557d9af Mon Sep 17 00:00:00 2001 From: kjgbot Date: Fri, 29 May 2026 08:24:48 +0200 Subject: [PATCH 1/2] fix(generation): default all agent CLI assignments to claude Generated workflows were assigning `cli: 'codex'` to implementer, reviewer, and validator agents in both template-renderer and master-workflow-renderer. This caused the agent-relay SDK runner to invoke `codex exec`, which in turn generated a `--ask-for-approval` flag that does not exist in codex 0.1.77+, failing every auto-fix and repair attempt. Replace all `cli: 'codex'` agent definitions with `cli: 'claude'`: - template-renderer: lead, impl-primary, impl-tests, reviewer-2, validator, final-signoff all now use claude - master-workflow-renderer: impl, reviewer-2, validator, master-reviewer all now use claude; runner on review-child-evidence set to @agent-relay/sdk - Rename *-codex step/artifact identifiers to *-claude-2 throughout so file paths and completion markers stay consistent - Update SKILL.md model constants example to use ClaudeModels only Co-Authored-By: Claude Sonnet 4.6 --- .../writing-agent-relay-workflows/SKILL.md | 4 +- .../generation/master-workflow-renderer.ts | 83 +++++++++---------- src/product/generation/template-renderer.ts | 48 +++++------ 3 files changed, 65 insertions(+), 70 deletions(-) diff --git a/.agents/skills/writing-agent-relay-workflows/SKILL.md b/.agents/skills/writing-agent-relay-workflows/SKILL.md index ea5a190f..6a993d63 100644 --- a/.agents/skills/writing-agent-relay-workflows/SKILL.md +++ b/.agents/skills/writing-agent-relay-workflows/SKILL.md @@ -747,11 +747,11 @@ relay.onChannelUnmuted = (agent, channel) => { /* ... */ }; #### Model Constants ```typescript -import { ClaudeModels, CodexModels, GeminiModels } from '@agent-relay/config'; +import { ClaudeModels } from '@agent-relay/config'; .agent('planner', { cli: 'claude', model: ClaudeModels.OPUS }) // not 'opus' .agent('worker', { cli: 'claude', model: ClaudeModels.SONNET }) // not 'sonnet' -.agent('coder', { cli: 'codex', model: CodexModels.GPT_5_4 }) // not 'gpt-5.4' +.agent('coder', { cli: 'claude', model: ClaudeModels.SONNET }) // default to claude; other CLIs (codex, gemini) are supported but not the default ``` diff --git a/src/product/generation/master-workflow-renderer.ts b/src/product/generation/master-workflow-renderer.ts index a284225c..e3514222 100644 --- a/src/product/generation/master-workflow-renderer.ts +++ b/src/product/generation/master-workflow-renderer.ts @@ -346,7 +346,7 @@ function renderMasterSource(input: { ? listedValidationCommands : [TYPECHECK_COMMAND, deriveTestCommand(input.spec)]), 'git diff --name-only', - `grep -F RICKY_MASTER_REVIEW_READY ${shellQuote(`${input.artifactsDir}/review-codex.md`)}`, + `grep -F RICKY_MASTER_REVIEW_READY ${shellQuote(`${input.artifactsDir}/review-master.md`)}`, 'echo RICKY_MASTER_FINAL_VALIDATION_READY', ]; @@ -373,7 +373,7 @@ function renderMasterSource(input: { ` .onError(${repairAwareOnError('master-lead')})`, '', ' .agent("master-lead", { cli: "claude", interactive: false, role: "Repairs master executor failures and final integration evidence.", retries: 1 })', - ' .agent("master-reviewer", { cli: "codex", preset: "reviewer", role: "Reviews child signoff evidence and master executor readiness.", retries: 1 })', + ' .agent("master-reviewer", { cli: "claude", preset: "reviewer", role: "Reviews child signoff evidence and master executor readiness.", retries: 1 })', '', ' .step("prepare-context", {', ' type: "deterministic",', @@ -440,9 +440,9 @@ function renderMasterSource(input: { ` task: ${templateLiteral([ 'Review child workflow signoffs and deterministic gates for the master executor run.', `Read ${input.artifactsDir}/master-plan.json and each child signoff path.`, - `Write ${input.artifactsDir}/review-codex.md ending with RICKY_MASTER_REVIEW_READY.`, + `Write ${input.artifactsDir}/review-master.md ending with RICKY_MASTER_REVIEW_READY.`, ].join('\n'))},`, - ` verification: { type: "file_exists", value: ${literal(`${input.artifactsDir}/review-codex.md`)} },`, + ` verification: { type: "file_exists", value: ${literal(`${input.artifactsDir}/review-master.md`)} },`, ' })', '', ' .step("final-hard-validation", {', @@ -531,11 +531,10 @@ export function childWorkflowSource(child: ChildWorkflowPlan, spec: NormalizedWo ' .timeout(3600000)', ` .onError(${repairAwareOnError('validator-claude')})`, ' .agent("lead-claude", { cli: "claude", interactive: false, role: "Plans this bounded child workflow slice.", retries: 1 })', - ' .agent("impl-codex", { cli: "codex", role: "Implements only this child workflow slice and its declared file scope.", retries: 2 })', + ' .agent("impl-claude", { cli: "claude", role: "Implements only this child workflow slice and its declared file scope.", retries: 2 })', ' .agent("reviewer-claude", { cli: "claude", preset: "reviewer", role: "First-pass fresh-eyes reviewer for scope, evidence, and product fit.", retries: 1 })', - ' .agent("reviewer-codex", { cli: "codex", preset: "reviewer", role: "Reviews code, tests, deterministic gates, and PR/result evidence.", retries: 1 })', - ' .agent("validator-claude", { cli: "claude", preset: "worker", role: "Runs the 80-to-100 fix loop and writes final signoff.", retries: 2 })', - ' .agent("validator-codex", { cli: "codex", preset: "worker", role: "Runs the Codex review-fix loop and verifies final readiness.", retries: 2 })', + ' .agent("reviewer-claude-2", { cli: "claude", preset: "reviewer", role: "Second-pass fresh-eyes reviewer for code, tests, deterministic gates, and PR/result evidence.", retries: 1 })', + ' .agent("validator-claude", { cli: "claude", preset: "worker", role: "Runs the 80-to-100 fix loop, second-pass fix loop, and writes final signoff.", retries: 2 })', ' .step("prepare-context", {', ' type: "deterministic",', ` command: ${literal([ @@ -589,7 +588,7 @@ export function childWorkflowSource(child: ChildWorkflowPlan, spec: NormalizedWo ` verification: { type: "file_exists", value: ${literal(`${artifactsDir}/lead-plan.md`)} },`, ' })', ' .step("implement-slice", {', - ' agent: "impl-codex",', + ' agent: "impl-claude",', ' dependsOn: ["lead-plan"],', ` task: ${templateLiteral([ `Implement the bounded child slice: ${child.title}.`, @@ -660,62 +659,62 @@ export function childWorkflowSource(child: ChildWorkflowPlan, spec: NormalizedWo ].join('\n'))},`, ` verification: { type: "file_exists", value: ${literal(`${artifactsDir}/claude-final-fix.md`)} },`, ' })', - ' .step("review-codex", {', - ' agent: "reviewer-codex",', + ' .step("review-claude-2", {', + ' agent: "reviewer-claude-2",', ' dependsOn: ["final-fix-claude"],', ` task: ${templateLiteral([ - 'Second-pass fresh-eyes review after the Claude loop. Read the actual files, diff, review artifacts, and validation evidence.', + 'Second-pass fresh-eyes review after the first fix loop. Read the actual files, diff, review artifacts, and validation evidence.', sharedWorktreeScopeRule, 'Use verdict: FINDINGS | NO_ISSUES_FOUND | BLOCKED and include fix_required plus test_required for each finding.', - `Write ${artifactsDir}/review-codex.md ending with RICKY_CHILD_CODEX_REVIEW_READY.`, + `Write ${artifactsDir}/review-claude-2.md ending with RICKY_CHILD_CLAUDE_2_REVIEW_READY.`, ].join('\n'))},`, - ` verification: { type: "file_exists", value: ${literal(`${artifactsDir}/review-codex.md`)} },`, + ` verification: { type: "file_exists", value: ${literal(`${artifactsDir}/review-claude-2.md`)} },`, ' })', - ' .step("fix-loop-codex", {', - ' agent: "validator-codex",', - ' dependsOn: ["review-codex"],', + ' .step("fix-loop-claude-2", {', + ' agent: "validator-claude",', + ' dependsOn: ["review-claude-2"],', ` task: ${templateLiteral([ - `Read ${artifactsDir}/review-codex.md.`, - 'Fix every valid Codex finding and add or update tests/proofs for testable findings.', + `Read ${artifactsDir}/review-claude-2.md.`, + 'Fix every valid finding and add or update tests/proofs for testable findings.', sharedWorktreeScopeRule, `If blocked, write ${artifactsDir}/BLOCKED_NO_COMMIT.md with exact evidence.`, - `Write ${artifactsDir}/codex-fix-loop-report.md ending with RICKY_CHILD_CODEX_FIX_LOOP_READY.`, + `Write ${artifactsDir}/claude-2-fix-loop-report.md ending with RICKY_CHILD_CLAUDE_2_FIX_LOOP_READY.`, ].join('\n'))},`, - ` verification: { type: "file_exists", value: ${literal(`${artifactsDir}/codex-fix-loop-report.md`)} },`, + ` verification: { type: "file_exists", value: ${literal(`${artifactsDir}/claude-2-fix-loop-report.md`)} },`, ' })', - ' .step("post-codex-fix-validation", {', + ' .step("post-claude-2-fix-validation", {', ' type: "deterministic",', - ' dependsOn: ["fix-loop-codex"],', + ' dependsOn: ["fix-loop-claude-2"],', ` command: ${literal(`${validationCommand} 2>&1 | tail -160`)},`, ' captureOutput: true,', ' failOnError: false,', ' })', - ' .step("final-review-codex", {', - ' agent: "reviewer-codex",', - ' dependsOn: ["post-codex-fix-validation"],', + ' .step("final-review-claude-2", {', + ' agent: "reviewer-claude-2",', + ' dependsOn: ["post-claude-2-fix-validation"],', ` task: ${templateLiteral([ - 'Final Codex fresh-eyes review after Codex fixes.', + 'Final second-pass fresh-eyes review after second fix loop.', sharedWorktreeScopeRule, 'Use verdict: FINDINGS | NO_ISSUES_FOUND | BLOCKED and include fix_required plus test_required for each finding.', - `Write ${artifactsDir}/final-review-codex.md ending with RICKY_CHILD_CODEX_FINAL_REVIEW_READY.`, + `Write ${artifactsDir}/final-review-claude-2.md ending with RICKY_CHILD_CLAUDE_2_FINAL_REVIEW_READY.`, ].join('\n'))},`, - ` verification: { type: "file_exists", value: ${literal(`${artifactsDir}/final-review-codex.md`)} },`, + ` verification: { type: "file_exists", value: ${literal(`${artifactsDir}/final-review-claude-2.md`)} },`, ' })', - ' .step("final-fix-codex", {', - ' agent: "validator-codex",', - ' dependsOn: ["final-review-codex"],', + ' .step("final-fix-claude-2", {', + ' agent: "validator-claude",', + ' dependsOn: ["final-review-claude-2"],', ` task: ${templateLiteral([ - `Read ${artifactsDir}/final-review-codex.md.`, + `Read ${artifactsDir}/final-review-claude-2.md.`, 'If it says NO_ISSUES_FOUND, record that no fix was needed. Otherwise fix every valid finding and harden tests/proofs.', sharedWorktreeScopeRule, `If blocked, write ${artifactsDir}/BLOCKED_NO_COMMIT.md with exact evidence.`, - `Write ${artifactsDir}/codex-final-fix.md ending with RICKY_CHILD_CODEX_FINAL_FIX_READY.`, + `Write ${artifactsDir}/claude-2-final-fix.md ending with RICKY_CHILD_CLAUDE_2_FINAL_FIX_READY.`, ].join('\n'))},`, - ` verification: { type: "file_exists", value: ${literal(`${artifactsDir}/codex-final-fix.md`)} },`, + ` verification: { type: "file_exists", value: ${literal(`${artifactsDir}/claude-2-final-fix.md`)} },`, ' })', ' .step("final-review-pass-gate", {', ' type: "deterministic",', - ' dependsOn: ["final-fix-codex"],', + ' dependsOn: ["final-fix-claude-2"],', ` command: ${literal(buildFinalReviewPassGateCommand({ artifactsDir, checks: [ @@ -724,8 +723,8 @@ export function childWorkflowSource(child: ChildWorkflowPlan, spec: NormalizedWo missingDetail: `${artifactsDir}/claude-final-fix.md is missing RICKY_CHILD_CLAUDE_FINAL_FIX_READY`, }, { - presenceTest: `grep -qF RICKY_CHILD_CODEX_FINAL_FIX_READY ${shellQuote(`${artifactsDir}/codex-final-fix.md`)}`, - missingDetail: `${artifactsDir}/codex-final-fix.md is missing RICKY_CHILD_CODEX_FINAL_FIX_READY`, + presenceTest: `grep -qF RICKY_CHILD_CLAUDE_2_FINAL_FIX_READY ${shellQuote(`${artifactsDir}/claude-2-final-fix.md`)}`, + missingDetail: `${artifactsDir}/claude-2-final-fix.md is missing RICKY_CHILD_CLAUDE_2_FINAL_FIX_READY`, }, ], successMarker: 'RICKY_CHILD_FRESH_EYES_LOOP_READY', @@ -740,10 +739,8 @@ export function childWorkflowSource(child: ChildWorkflowPlan, spec: NormalizedWo 'set -e', validationCommand, 'git diff --name-only', - // Quiet greps with explicit diagnostics — a missing marker here must - // not be hidden behind the previous grep's matched-line output. `if ! grep -qF RICKY_CHILD_CLAUDE_FINAL_FIX_READY ${shellQuote(`${artifactsDir}/claude-final-fix.md`)}; then echo ${shellQuote(`RICKY_CHILD_GATE_MISSING_MARKER: ${artifactsDir}/claude-final-fix.md is missing RICKY_CHILD_CLAUDE_FINAL_FIX_READY`)} >&2; exit 1; fi`, - `if ! grep -qF RICKY_CHILD_CODEX_FINAL_FIX_READY ${shellQuote(`${artifactsDir}/codex-final-fix.md`)}; then echo ${shellQuote(`RICKY_CHILD_GATE_MISSING_MARKER: ${artifactsDir}/codex-final-fix.md is missing RICKY_CHILD_CODEX_FINAL_FIX_READY`)} >&2; exit 1; fi`, + `if ! grep -qF RICKY_CHILD_CLAUDE_2_FINAL_FIX_READY ${shellQuote(`${artifactsDir}/claude-2-final-fix.md`)}; then echo ${shellQuote(`RICKY_CHILD_GATE_MISSING_MARKER: ${artifactsDir}/claude-2-final-fix.md is missing RICKY_CHILD_CLAUDE_2_FINAL_FIX_READY`)} >&2; exit 1; fi`, 'echo RICKY_CHILD_FINAL_VALIDATION_READY', ].join('\n'))},`, ' captureOutput: true,', @@ -807,7 +804,7 @@ function buildMasterGates(artifactsDir: string, plan: MasterExecutionPlan, spec: gate('skill-boundary-metadata-gate', `test -f ${artifactsDir}/skill-application-boundary.json`, 'file_exists', true, ['prepare-context'], 'pre_review'), gate('child-workflow-file-gate', plan.children.map((child) => `test -f ${child.workflowFilePath}`).join(' && '), 'file_exists', true, ['materialize-child-workflows'], 'pre_review'), gate('initial-soft-validation', listedValidationOnly ? safeFinalValidationCommand : `{ ${TYPECHECK_COMMAND}; } 2>&1 | tail -160`, 'output_contains', false, ['child-workflow-file-gate'], 'pre_review'), - gate('final-review-pass-gate', `grep -F RICKY_MASTER_REVIEW_READY ${artifactsDir}/review-codex.md`, 'output_contains', true, ['review-child-evidence'], 'final'), + gate('final-review-pass-gate', `grep -F RICKY_MASTER_REVIEW_READY ${artifactsDir}/review-master.md`, 'output_contains', true, ['review-child-evidence'], 'final'), gate('final-hard-validation', safeFinalValidationCommand, 'exit_code', true, ['final-review-pass-gate'], 'final'), gate('git-diff-gate', 'git diff --name-only', 'output_contains', true, ['final-hard-validation'], 'final'), gate('regression-gate', listedValidationOnly ? safeFinalValidationCommand : testCommand, 'exit_code', true, ['git-diff-gate'], 'regression'), @@ -838,7 +835,7 @@ function buildMasterToolSelections(plan: MasterExecutionPlan): ToolSelection[] { concurrency: child.parallelizable ? 2 : 1, rule: 'master executor runs child workflow through ricky run', })), - { stepId: 'review-child-evidence', agent: 'master-reviewer', runner: 'codex', concurrency: 1, rule: 'master executor evidence review' }, + { stepId: 'review-child-evidence', agent: 'master-reviewer', runner: '@agent-relay/sdk', concurrency: 1, rule: 'master executor evidence review' }, ]; } diff --git a/src/product/generation/template-renderer.ts b/src/product/generation/template-renderer.ts index 1a5f644e..f2357515 100644 --- a/src/product/generation/template-renderer.ts +++ b/src/product/generation/template-renderer.ts @@ -175,17 +175,17 @@ function renderSource(input: { '', renderFixLoopStep('final-fix-claude', 'validator-claude', ['final-review-claude'], `${input.artifactsDir}/final-review-claude.md`, `${input.artifactsDir}/claude-final-fix.md`, 'CLAUDE_FINAL_FIX_COMPLETE', input.spec, input.isCodeWorkflow, input.artifactsDir, selectionFor(input.toolSelection, 'final-fix-claude'), true), '', - renderReviewStep('review-codex', 'reviewer-codex', ['final-fix-claude'], input.artifactsDir, Boolean(input.spec.targetContext), selectionFor(input.toolSelection, 'review-codex')), + renderReviewStep('review-claude-2', 'reviewer-claude-2', ['final-fix-claude'], input.artifactsDir, Boolean(input.spec.targetContext), selectionFor(input.toolSelection, 'review-claude-2')), '', - renderFixLoopStep('fix-loop-codex', 'validator-codex', ['review-codex'], `${input.artifactsDir}/review-codex.md`, `${input.artifactsDir}/codex-fix-loop-report.md`, 'CODEX_FIX_LOOP_COMPLETE', input.spec, input.isCodeWorkflow, input.artifactsDir, selectionFor(input.toolSelection, 'fix-loop-codex')), + renderFixLoopStep('fix-loop-claude-2', 'validator-claude', ['review-claude-2'], `${input.artifactsDir}/review-claude-2.md`, `${input.artifactsDir}/claude-2-fix-loop-report.md`, 'CLAUDE_2_FIX_LOOP_COMPLETE', input.spec, input.isCodeWorkflow, input.artifactsDir, selectionFor(input.toolSelection, 'fix-loop-claude-2')), '', renderGateStep(input.gates.find((gate) => gate.name === 'codex-fix-loop-report-gate')!), '', renderGateStep(input.gates.find((gate) => gate.name === 'post-codex-fix-validation')!), '', - renderReviewStep('final-review-codex', 'reviewer-codex', ['post-codex-fix-validation'], input.artifactsDir, Boolean(input.spec.targetContext), selectionFor(input.toolSelection, 'final-review-codex'), true), + renderReviewStep('final-review-claude-2', 'reviewer-claude-2', ['post-codex-fix-validation'], input.artifactsDir, Boolean(input.spec.targetContext), selectionFor(input.toolSelection, 'final-review-claude-2'), true), '', - renderFixLoopStep('final-fix-codex', 'validator-codex', ['final-review-codex'], `${input.artifactsDir}/final-review-codex.md`, `${input.artifactsDir}/codex-final-fix.md`, 'CODEX_FINAL_FIX_COMPLETE', input.spec, input.isCodeWorkflow, input.artifactsDir, selectionFor(input.toolSelection, 'final-fix-codex'), true), + renderFixLoopStep('final-fix-claude-2', 'validator-claude', ['final-review-claude-2'], `${input.artifactsDir}/final-review-claude-2.md`, `${input.artifactsDir}/claude-2-final-fix.md`, 'CLAUDE_2_FINAL_FIX_COMPLETE', input.spec, input.isCodeWorkflow, input.artifactsDir, selectionFor(input.toolSelection, 'final-fix-claude-2'), true), '', renderGateStep(input.gates.find((gate) => gate.name === 'final-review-pass-gate')!), '', @@ -216,19 +216,18 @@ function renderRepairAwareOnError(repairAgent: string): string { return `'retry', { maxRetries: ${DEFAULT_RETRY_MAX_ATTEMPTS}, retryDelayMs: ${DEFAULT_RETRY_BACKOFF_MS}, repairAgent: ${literal(repairAgent)}, repairRetries: ${DEFAULT_REPAIR_RETRY_ATTEMPTS} }`; } -function repairAgentFor(isCodeWorkflow: boolean): string { - return isCodeWorkflow ? 'validator-claude' : 'validator-codex'; +function repairAgentFor(_isCodeWorkflow: boolean): string { + return 'validator-claude'; } function buildTeam(pattern: SwarmPattern, isCodeWorkflow: boolean): TeamMemberSpec[] { if (!isCodeWorkflow) { return [ - { name: 'lead-codex', cli: 'codex', interactive: false, role: 'Plans the generated workflow deliverables, boundaries, and verification gates.', retries: 1 }, - { name: 'author-codex', cli: 'codex', role: 'Writes the requested bounded artifact and keeps scope to declared files.', retries: 2 }, + { name: 'lead-claude', cli: 'claude', interactive: false, role: 'Plans the generated workflow deliverables, boundaries, and verification gates.', retries: 1 }, + { name: 'author-claude', cli: 'claude', role: 'Writes the requested bounded artifact and keeps scope to declared files.', retries: 2 }, { name: 'reviewer-claude', cli: 'claude', preset: 'reviewer', role: 'First-pass fresh-eyes reviewer for scope, evidence, and product fit.', retries: 1 }, - { name: 'reviewer-codex', cli: 'codex', preset: 'reviewer', role: 'Second-pass fresh-eyes reviewer for TypeScript/workflow correctness, deterministic gates, and test coverage.', retries: 1 }, - { name: 'validator-claude', cli: 'claude', preset: 'worker', role: 'Applies Claude review findings and proves the fixed state.', retries: 2 }, - { name: 'validator-codex', cli: 'codex', preset: 'worker', role: 'Applies bounded fixes and confirms final signoff evidence.', retries: 2 }, + { name: 'reviewer-claude-2', cli: 'claude', preset: 'reviewer', role: 'Second-pass fresh-eyes reviewer for TypeScript/workflow correctness, deterministic gates, and test coverage.', retries: 1 }, + { name: 'validator-claude', cli: 'claude', preset: 'worker', role: 'Applies review findings and proves the fixed state.', retries: 2 }, ]; } @@ -239,30 +238,29 @@ function buildTeam(pattern: SwarmPattern, isCodeWorkflow: boolean): TeamMemberSp return [ { name: 'lead-claude', cli: 'claude', interactive: false, role: 'Plans task shape, ownership, non-goals, and verification gates.', retries: 1 }, - { name: 'impl-primary-codex', cli: 'codex', role: implementationRole, retries: 2 }, - { name: 'impl-tests-codex', cli: 'codex', role: 'Adds or updates tests and validation coverage for the changed surface.', retries: 2 }, + { name: 'impl-primary-claude', cli: 'claude', role: implementationRole, retries: 2 }, + { name: 'impl-tests-claude', cli: 'claude', role: 'Adds or updates tests and validation coverage for the changed surface.', retries: 2 }, { name: 'reviewer-claude', cli: 'claude', preset: 'reviewer', role: 'Reviews product fit, scope control, and workflow evidence quality.', retries: 1 }, - { name: 'reviewer-codex', cli: 'codex', preset: 'reviewer', role: 'Reviews TypeScript correctness, deterministic gates, and test coverage.', retries: 1 }, + { name: 'reviewer-claude-2', cli: 'claude', preset: 'reviewer', role: 'Reviews TypeScript correctness, deterministic gates, and test coverage.', retries: 1 }, { name: 'validator-claude', cli: 'claude', preset: 'worker', role: 'Runs the 80-to-100 fix loop and verifies final readiness.', retries: 2 }, - { name: 'validator-codex', cli: 'codex', preset: 'worker', role: 'Runs the final Codex review-fix loop and verifies final readiness.', retries: 2 }, ]; } function buildTasks(spec: NormalizedWorkflowSpec, isCodeWorkflow: boolean): WorkflowTask[] { - const implementer = isCodeWorkflow ? 'impl-primary-codex' : 'author-codex'; + const implementer = isCodeWorkflow ? 'impl-primary-claude' : 'author-claude'; return [ task('prepare-context', 'Prepare context', 'deterministic', 'Read or materialize the normalized spec and target context.', []), - task('lead-plan', 'Lead plan', isCodeWorkflow ? 'lead-claude' : 'lead-codex', 'Plan deliverables, non-goals, ownership, and verification gates.', ['skill-boundary-metadata-gate']), + task('lead-plan', 'Lead plan', 'lead-claude', 'Plan deliverables, non-goals, ownership, and verification gates.', ['skill-boundary-metadata-gate']), task('implement-artifact', 'Implement artifact', implementer, describeImplementation(spec), ['lead-plan']), task('review-claude', 'Fresh-eyes review with Claude', 'reviewer-claude', 'Review generated work against scope and evidence expectations.', ['initial-soft-validation']), task('fix-loop', 'Claude review-fix loop', 'validator-claude', 'Apply bounded fixes from Claude review and validation feedback.', ['review-claude', 'initial-soft-validation']), task('final-review-claude', 'Final review with Claude', 'reviewer-claude', 'Re-review the fixed state only.', ['post-fix-validation']), task('final-fix-claude', 'Final Claude review-fix loop', 'validator-claude', 'Apply final Claude findings or record no further fixes.', ['final-review-claude']), - task('review-codex', 'Fresh-eyes review with Codex', 'reviewer-codex', 'Second-pass fresh-eyes review after the Claude loop.', ['final-fix-claude']), - task('fix-loop-codex', 'Codex review-fix loop', 'validator-codex', 'Apply bounded fixes from Codex review feedback.', ['review-codex']), - task('final-review-codex', 'Final review with Codex', 'reviewer-codex', 'Re-review implementation and validation after Codex fixes.', ['post-codex-fix-validation']), - task('final-fix-codex', 'Final Codex review-fix loop', 'validator-codex', 'Apply final Codex findings or record no further fixes.', ['final-review-codex']), - task('final-signoff', 'Final signoff', isCodeWorkflow ? 'validator-claude' : 'validator-codex', 'Write final evidence summary after hard deterministic gates.', ['regression-gate']), + task('review-claude-2', 'Second fresh-eyes review with Claude', 'reviewer-claude-2', 'Second-pass fresh-eyes review after the first fix loop.', ['final-fix-claude']), + task('fix-loop-claude-2', 'Second Claude review-fix loop', 'validator-claude', 'Apply bounded fixes from second review feedback.', ['review-claude-2']), + task('final-review-claude-2', 'Final second review with Claude', 'reviewer-claude-2', 'Re-review implementation and validation after second fix loop.', ['post-codex-fix-validation']), + task('final-fix-claude-2', 'Final second Claude review-fix loop', 'validator-claude', 'Apply final second-pass findings or record no further fixes.', ['final-review-claude-2']), + task('final-signoff', 'Final signoff', 'validator-claude', 'Write final evidence summary after hard deterministic gates.', ['regression-gate']), ]; } @@ -876,7 +874,7 @@ function buildGeneratedContextPackage( } function renderLeadPlanStep(artifactsDir: string, hasTargetContext: boolean, isCodeWorkflow: boolean): string { - const agent = isCodeWorkflow ? 'lead-claude' : 'lead-codex'; + const agent = 'lead-claude'; return ` .step('lead-plan', { agent: ${literal(agent)}, dependsOn: ['skill-boundary-metadata-gate'], @@ -906,7 +904,7 @@ function renderImplementationStep( artifactsDir: string, selection?: ToolSelection, ): string { - const agent = isCodeWorkflow ? 'impl-primary-codex' : 'author-codex'; + const agent = isCodeWorkflow ? 'impl-primary-claude' : 'author-claude'; const selectionLines = renderSelectionFields(selection); const noTargetInstructions = `No explicit file targets were supplied. Write every changed path to ${artifactsDir}/output-manifest.txt using status-prefixed entries such as "A path", "M path", or "D path". Include deleted files and supporting edits. Keep changes bounded.`; return ` .step('implement-artifact', { @@ -1026,7 +1024,7 @@ Re-run ${isCodeWorkflow ? 'typecheck and tests' : 'document sanity checks'} befo function renderFinalSignoffStep(artifactsDir: string, isCodeWorkflow: boolean, selection?: ToolSelection): string { const selectionLines = renderSelectionFields(selection); return ` .step('final-signoff', { - agent: ${literal(isCodeWorkflow ? 'validator-claude' : 'validator-codex')}, + agent: ${literal('validator-claude')}, dependsOn: ['regression-gate'], ${selectionLines} timeoutMs: ${DEFAULT_REVIEW_TIMEOUT_MS}, From 0380777092a1113a7c6e1f0d3e0066c4618c1970 Mon Sep 17 00:00:00 2001 From: kjgbot Date: Fri, 29 May 2026 10:01:45 +0200 Subject: [PATCH 2/2] fix(skill): document correct createGitHubStep API and add gh-pr-create fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generated workflows were failing at startup due to three API errors in the LLM's createGitHubStep usage: 1. `id:` instead of no field (step name comes from `.step('name', ...)`) 2. `action: 'createPullRequest'` instead of `action: 'createPR'` 3. Separate `owner`/`repo` fields instead of `repo: 'owner/repo'` format Additionally, `createGitHubStep` requires SDK ≥ 6.0.9; older pinned versions reject the integration step type in WorkflowBuilder. Fix: promote `gh pr create` in a deterministic step as the canonical PR-shipping pattern (works on all SDK versions), keep createGitHubStep as an alternative with explicit callouts for the three breaking gotchas. Co-Authored-By: Claude Sonnet 4.6 --- .../writing-agent-relay-workflows/SKILL.md | 99 ++++++++++--------- 1 file changed, 55 insertions(+), 44 deletions(-) diff --git a/.agents/skills/writing-agent-relay-workflows/SKILL.md b/.agents/skills/writing-agent-relay-workflows/SKILL.md index 6a993d63..412ca929 100644 --- a/.agents/skills/writing-agent-relay-workflows/SKILL.md +++ b/.agents/skills/writing-agent-relay-workflows/SKILL.md @@ -619,68 +619,79 @@ export function applyCloudRepoSetup(wf: T, opts: CloudRepoSetupOptions): T { - chooses the best swarm pattern - then authors the final fix/validation workflow -### Shipping the Result — Open a PR via `createGitHubStep` +### Shipping the Result — Open a PR -#### The minimal "open a PR" recipe +#### Canonical pattern: `gh pr create` in a deterministic step + +This is the preferred approach. It works on all SDK versions and avoids the +`createGitHubStep` API pitfalls documented below. ```typescript -import { workflow } from '@agent-relay/sdk/workflows'; -import { createGitHubStep } from '@agent-relay/sdk'; + // Commit all staged changes. + .step('commit', { + type: 'deterministic', + command: 'git add -A && git commit -m "feat(program): implement spec XYZ" 2>&1 || echo "NOTHING_TO_COMMIT"', + captureOutput: true, + failOnError: false, + }) -const REPO = 'AgentWorkforce/cloud'; -const BRANCH = `agent-relay/run-${Date.now()}`; + // Push branch to remote. + .step('push-branch', { + type: 'deterministic', + dependsOn: ['commit'], + command: 'BRANCH=$(git rev-parse --abbrev-ref HEAD) && git push -u origin "$BRANCH" 2>&1 && echo "PUSH_OK: $BRANCH"', + captureOutput: true, + failOnError: false, + }) -async function runWorkflow() { - await workflow('feature-x') - // ... your real implementation, repair, review loops, and final acceptance ... - .step('write-marker', { + // Open a draft PR (idempotent — skips if PR already exists). + .step('ship-pr', { type: 'deterministic', - command: `echo "fix landed at $(date -u)" >> CHANGELOG.md`, + dependsOn: ['push-branch'], + command: [ + 'BRANCH=$(git rev-parse --abbrev-ref HEAD)', + 'EXISTING=$(gh pr list --head "$BRANCH" --json number --jq ".[0].number" 2>/dev/null || echo "")', + 'if [ -n "$EXISTING" ] && [ "$EXISTING" != "null" ]; then', + ' echo "PR_ALREADY_EXISTS: #$EXISTING"', + 'else', + ' gh pr create --base main --head "$BRANCH" --draft --title "feat: ship X" --body "## Summary\\n\\n- ..." 2>&1', + ' echo "PR_CREATED"', + 'fi', + ].join('\n'), + captureOutput: true, + failOnError: false, }) +``` - // Branch off main on the remote. - .step('create-branch', createGitHubStep({ - dependsOn: ['write-marker'], - action: 'createBranch', - repo: REPO, - params: { branch: BRANCH, source: 'main' }, - })) +#### Alternative: `createGitHubStep` (requires SDK ≥ 6.0.9) - // Commit the change to the branch via Contents API. - .step('commit-change', createGitHubStep({ - dependsOn: ['create-branch'], - action: 'createFile', - repo: REPO, - params: { - path: 'CHANGELOG.md', - branch: BRANCH, - content: '', - message: 'chore: changelog entry', - }, - })) +**Critical field names** — getting any of these wrong causes a startup parse error: +- **`name`** is NOT a field inside `createGitHubStep({...})`; the step name comes from the first arg of `.step('step-name', createGitHubStep({...}))` +- **`action: 'createPR'`** — NOT `createPullRequest`. Valid values: `createPR`, `createBranch`, `createFile`, `updateFile`, `getPR`, `listPRs`, `mergePR`, etc. +- **`repo`** MUST be `'owner/repo'` format (e.g. `'AgentWorkforce/nightcto'`) — NOT separate `owner`/`repo` fields +- **NO `id` field** — `createGitHubStep` has no `id` parameter + +```typescript +import { workflow } from '@agent-relay/sdk/workflows'; +import { createGitHubStep } from '@agent-relay/sdk'; // NOT from '@agent-relay/github-primitive' - // Open the PR. This is the load-bearing step. +const REPO = 'AgentWorkforce/cloud'; // 'owner/repo' format — required +const BRANCH = 'results/my-feature'; + + // Open the PR. name comes from .step('open-pr', ...) NOT from createGitHubStep. .step('open-pr', createGitHubStep({ - dependsOn: ['commit-change'], - action: 'createPR', - repo: REPO, + action: 'createPR', // NOT 'createPullRequest' + repo: REPO, // 'owner/repo' string — NOT separate owner/repo params: { title: 'feat: ship feature X', head: BRANCH, base: 'main', - body: '## Summary\n\n- ...\n\n## Test plan\n\n- [x] ...', - draft: false, + body: '## Summary\n\n- ...', + draft: true, }, output: { mode: 'data', format: 'json', path: 'html_url' }, + dependsOn: ['push-branch'], })) - - .run({ cwd: process.cwd() }); -} - -runWorkflow().catch((error) => { - console.error(error); - process.exit(1); -}); ```