Skip to content

fix: resolve #793 — Two workflow primitives gaps: role-based context loss and no convergence loops#833

Open
SlncTrZ wants to merge 3 commits into
AgentWorkforce:mainfrom
SlncTrZ:CntrB/improve/quality/add-stepcontext-and-enhanced-workflowste
Open

fix: resolve #793 — Two workflow primitives gaps: role-based context loss and no convergence loops#833
SlncTrZ wants to merge 3 commits into
AgentWorkforce:mainfrom
SlncTrZ:CntrB/improve/quality/add-stepcontext-and-enhanced-workflowste

Conversation

@SlncTrZ
Copy link
Copy Markdown

@SlncTrZ SlncTrZ commented May 10, 2026

Summary

fix: resolve #793 — Two workflow primitives gaps: role-based context loss and no convergence loops

Problem

Severity: High | File: src/workflow/types.ts

This file currently defines core types (WorkflowStep, Workflow, etc.). We need to extend it to support structured context propagation and the new loop primitive. The existing WorkflowStep interface is modified to accept and return StepContext instead of plain strings. A new LoopStep interface is added to model iterative execution.

Solution


## Changes

- `src/workflow/types.ts` (new)
- `src/templates/feature-dev.ts` (new)
- `src/templates/review-loop.ts` (new)

## Testing

- [ ] Existing tests pass
- [ ] Manual review completed
- [ ] No new warnings/errors introduced"

SlncTrZ added 3 commits May 11, 2026 01:38
…based context loss and no convergence loops

Fixes AgentWorkforce#793

Signed-off-by: Dinh Truong (SlncTrZ) <46520299+SlncTrZ@users.noreply.github.com>
…based context loss and no convergence loops

Fixes AgentWorkforce#793

Signed-off-by: Dinh Truong (SlncTrZ) <46520299+SlncTrZ@users.noreply.github.com>
…based context loss and no convergence loops

Fixes AgentWorkforce#793

Signed-off-by: Dinh Truong (SlncTrZ) <46520299+SlncTrZ@users.noreply.github.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 10, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR introduces workflow execution primitives and two template implementations. It adds TypeScript type definitions for workflow steps and context management, implements a feature-dev template that passes context through sequential planning/development/review phases, and provides a review-loop factory function for iterative review-and-fix workflows.

Changes

Workflow Primitives and Templates

Layer / File(s) Summary
Workflow Type Definitions
src/workflow/types.ts
StepContext holds per-step data, optional metadata, and optional history. WorkflowStep defines name and sync/async execute. LoopStep extends WorkflowStep with condition, nested steps, and optional maxIterations. Workflow defines name and steps array.
Feature-Dev Data Contracts
src/templates/feature-dev.ts (lines 1–17)
Plan defines strategy and reasoning. DeveloperOutput holds decisions array and implementationDetails. ReviewerOutput records deviations array and approved boolean.
Feature-Dev Workflow Execution
src/templates/feature-dev.ts (lines 18–62)
featureDevTemplate.run constructs a Context, stores requirements, sequentially executes planner/developer/reviewer while persisting results in context for downstream access. Planner derives strategy from requirements; developer reads stored plan to produce decisions; reviewer validates decisions against plan strategy using context visibility.
Review Loop Factory
src/templates/review-loop.ts
createReviewLoop factory constructs a LoopStep that iteratively executes review and address-feedback steps until review passes or maxIterations (default 5) is reached. Returns final code and iteration count. Defines ReviewStepOutput, AddressFeedbackInput, AddressFeedbackOutput, and ReviewLoopConfig types.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

A rabbit hops through workflows clear,
With steps and loops, the path is near.
Context flows where roles align,
From plan to dev to review's design. 🐰✨
Now loops converge 'til all agree!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly references issue #793 and accurately summarizes the two main objectives: preventing role-based context loss and adding convergence loops. However, the phrase 'Two workflow primitives gaps' is somewhat technical jargon that a non-expert might not immediately grasp.
Description check ✅ Passed The description includes a summary, clearly states the problem and solution, lists all changed files, and provides a testing checklist matching the template structure. All major required sections are present and adequately filled.
Linked Issues check ✅ Passed The code changes directly address both objectives from #793: new StepContext in types.ts enables context propagation across role boundaries, and createReviewLoop enables iterative convergence loops that re-run multiple steps until conditions are met.
Out of Scope Changes check ✅ Passed All three files added (workflow types, feature-dev template, review-loop helper) directly support the stated objectives of adding structured context and loop primitives. No unrelated changes detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 5 additional findings.

Open in Devin Review

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/templates/feature-dev.ts (1)

27-33: ⚡ Quick win

Avoid this-bound method dispatch in run.

If run is called as a detached function, this can be undefined. Prefer explicit object references for stable behavior.

Proposed fix
-    const plan = await this.planner(ctx);
+    const plan = await featureDevTemplate.planner(ctx);
     ctx.set('plan', plan);

-    const development = await this.developer(ctx);
+    const development = await featureDevTemplate.developer(ctx);
     ctx.set('development', development);

-    const review = await this.reviewer(ctx);
+    const review = await featureDevTemplate.reviewer(ctx);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/templates/feature-dev.ts` around lines 27 - 33, The run method uses
this-bound calls (this.planner, this.developer, this.reviewer) which break if
run is detached; change run to capture the functions from the instance first or
call them via a stable object reference instead of relying on this at call
time—e.g., within run assign const planner = this.planner.bind(this) or const {
planner, developer, reviewer } = this and then call planner(ctx),
developer(ctx), reviewer(ctx), and continue to set ctx with the returned
plan/development/review values; update references to planner, developer,
reviewer and ctx.set accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/templates/review-loop.ts`:
- Around line 3-11: Change ReviewStepOutput to a discriminated union so a
failing review must include feedback: replace the current single interface with
a union such that when passed is true feedback is optional, and when passed is
false feedback is required; update any references to ReviewStepOutput (and
ensure AddressFeedbackInput still matches where used) so callers construct the
correct shape for pass vs fail cases.
- Around line 30-33: The loop dereferences ctx.getStepOutput(reviewStep) without
guarding for undefined; change the retrieval to const reviewOut =
ctx.getStepOutput(reviewStep) as ReviewStepOutput | undefined and update the
condition to use optional chaining (e.g., return !(reviewOut?.passed)) so the
loop handles a missing review output consistently with the finalize callback.

---

Nitpick comments:
In `@src/templates/feature-dev.ts`:
- Around line 27-33: The run method uses this-bound calls (this.planner,
this.developer, this.reviewer) which break if run is detached; change run to
capture the functions from the instance first or call them via a stable object
reference instead of relying on this at call time—e.g., within run assign const
planner = this.planner.bind(this) or const { planner, developer, reviewer } =
this and then call planner(ctx), developer(ctx), reviewer(ctx), and continue to
set ctx with the returned plan/development/review values; update references to
planner, developer, reviewer and ctx.set accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4a474deb-526b-40ab-9e50-8e52a2e62fe0

📥 Commits

Reviewing files that changed from the base of the PR and between bffd6b2 and 21d47c8.

📒 Files selected for processing (3)
  • src/templates/feature-dev.ts
  • src/templates/review-loop.ts
  • src/workflow/types.ts

Comment on lines +3 to +11
export interface ReviewStepOutput {
passed: boolean;
feedback?: string;
}

export interface AddressFeedbackInput {
code: string;
feedback: string;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Enforce feedback when review fails.

ReviewStepOutput allows passed: false with no feedback, but the next step requires feedback: string. Encode this in the type to prevent invalid loop state.

Proposed fix
-export interface ReviewStepOutput {
-  passed: boolean;
-  feedback?: string;
-}
+export type ReviewStepOutput =
+  | { passed: true }
+  | { passed: false; feedback: string };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export interface ReviewStepOutput {
passed: boolean;
feedback?: string;
}
export interface AddressFeedbackInput {
code: string;
feedback: string;
}
export type ReviewStepOutput =
| { passed: true }
| { passed: false; feedback: string };
export interface AddressFeedbackInput {
code: string;
feedback: string;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/templates/review-loop.ts` around lines 3 - 11, Change ReviewStepOutput to
a discriminated union so a failing review must include feedback: replace the
current single interface with a union such that when passed is true feedback is
optional, and when passed is false feedback is required; update any references
to ReviewStepOutput (and ensure AddressFeedbackInput still matches where used)
so callers construct the correct shape for pass vs fail cases.

Comment on lines +30 to +33
while: (ctx: StepContext) => {
const reviewOut = ctx.getStepOutput(reviewStep) as ReviewStepOutput;
return !reviewOut.passed;
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In @agentworkforce/relay, is LoopStep's whilepredicate evaluated before the first iteration, and what doesStepContext.getStepOutput(step) return when that step has not executed yet?

💡 Result:

I couldn’t find any authoritative source in the AgentWorkforce/relay repo or its published docs that defines (1) the evaluation timing of LoopStep’s while predicate (before first iteration vs after) or (2) what StepContext.getStepOutput(step) returns prior to that step executing. The search results I retrieved only cover high-level workflow/run concepts and a generic note about output availability when dependsOn is missing (i.e., output “won’t be available yet”), but they do not mention LoopStep, while, StepContext, or getStepOutput semantics for the “not executed yet” case. If you can share the exact file/class name where LoopStep and StepContext are implemented in your version of @agentworkforce/relay (or paste the relevant code), I can answer precisely.

Citations:


🏁 Script executed:

find . -type f -name "*.ts" -o -name "*.tsx" | head -20

Repository: AgentWorkforce/relay

Length of output: 934


🏁 Script executed:

# Search for LoopStep and StepContext definitions
rg "class LoopStep|interface LoopStep|type LoopStep" -A 10

Repository: AgentWorkforce/relay

Length of output: 1414


🏁 Script executed:

# Search for StepContext definition
rg "class StepContext|interface StepContext|type StepContext" -A 10

Repository: AgentWorkforce/relay

Length of output: 553


🏁 Script executed:

# Search for getStepOutput method
rg "getStepOutput" -B 2 -A 5

Repository: AgentWorkforce/relay

Length of output: 6625


🏁 Script executed:

# Look at the review-loop.ts file context
cat -n src/templates/review-loop.ts

Repository: AgentWorkforce/relay

Length of output: 1598


🏁 Script executed:

# Find LoopStep implementation (class, not just interface)
rg "class LoopStep|export.*LoopStep.*=" -A 20

Repository: AgentWorkforce/relay

Length of output: 46


🏁 Script executed:

# Check for LoopStep in node_modules or dist
find . -name "*loop*" -type f | grep -E "\.(ts|js)$"

Repository: AgentWorkforce/relay

Length of output: 508


🏁 Script executed:

# Look at the imports in review-loop.ts to find where LoopStep comes from
head -5 src/templates/review-loop.ts

Repository: AgentWorkforce/relay

Length of output: 210


🏁 Script executed:

# Search for when conditions/while loops are evaluated in execution logic
rg "while.*condition|condition.*while|evaluateLoop|executeLoop" -B 3 -A 5

Repository: AgentWorkforce/relay

Length of output: 1870


🏁 Script executed:

# Search for LoopStep implementation (likely in relay package)
rg "export.*class LoopStep" -A 30

Repository: AgentWorkforce/relay

Length of output: 46


🏁 Script executed:

# Look for LoopStep in the relay package specifically
fd ".*loop.*" packages/ --type f -e ts -e tsx | head -20

Repository: AgentWorkforce/relay

Length of output: 104


🏁 Script executed:

# Check what's exported from the main relay package
cat packages/relay/src/index.ts 2>/dev/null || cat packages/sdk/src/index.ts 2>/dev/null | grep -i loop

Repository: AgentWorkforce/relay

Length of output: 46


🏁 Script executed:

# Search for usage of while predicate in loop execution
rg "\.while\s*\(" -B 5 -A 10

Repository: AgentWorkforce/relay

Length of output: 46


🏁 Script executed:

# Look at the actual LoopStep type more carefully - check all files with "loop" in workflow
rg "while.*StepContext|condition.*StepContext" -B 2 -A 5

Repository: AgentWorkforce/relay

Length of output: 1021


🏁 Script executed:

# Look at ralph-loop example for LoopStep usage
cat packages/sdk/src/examples/ralph-loop.ts

Repository: AgentWorkforce/relay

Length of output: 12710


🏁 Script executed:

# Check for LoopStep tests
find . -name "*.test.ts" -o -name "*.spec.ts" | xargs grep -l "LoopStep" 2>/dev/null

Repository: AgentWorkforce/relay

Length of output: 48


🏁 Script executed:

# Look for StepContext interface extensions or actual implementation
rg "getStepOutput" -B 5 -A 3

Repository: AgentWorkforce/relay

Length of output: 7255


🏁 Script executed:

# Check package.json to understand relay dependencies
cat package.json | grep -A 5 "@agentworkforce"

Repository: AgentWorkforce/relay

Length of output: 46


🏁 Script executed:

# Look at how ctx parameter is used in loops - check the actual LoopStep implementation in relay module
find . -path ./node_modules -prune -o -name "*.ts" -type f -exec grep -l "class.*LoopStep\|interface.*LoopStep" {} \;

Repository: AgentWorkforce/relay

Length of output: 140


🏁 Script executed:

# Search for LoopStep class/implementation in relay module
rg "new LoopStep" -B 5 -A 15

Repository: AgentWorkforce/relay

Length of output: 3891


🏁 Script executed:

# Look for where LoopStep is actually defined/implemented
find . -path ./node_modules/@agentworkforce -name "*.ts" -o -path ./node_modules/@agentworkforce -name "*.js" | head -20

Repository: AgentWorkforce/relay

Length of output: 46


🏁 Script executed:

# Check what's in `@agentworkforce/relay` node_modules
ls -la node_modules/@agentworkforce 2>/dev/null || echo "Module not installed or not in node_modules"

Repository: AgentWorkforce/relay

Length of output: 108


🏁 Script executed:

# Look at the actual StepContext definition more carefully - maybe it's extended somewhere
rg "interface StepContext|type StepContext" -A 15

Repository: AgentWorkforce/relay

Length of output: 832


🏁 Script executed:

# Check for implementations that show when while/condition is evaluated
rg "executeLoop|runLoop|loopBody|while.*steps" -B 3 -A 10

Repository: AgentWorkforce/relay

Length of output: 4517


🏁 Script executed:

# Search for where StepContext is extended with getStepOutput method
rg "StepContext.*getStepOutput|getStepOutput.*StepContext" -B 5 -A 5

Repository: AgentWorkforce/relay

Length of output: 46


🏁 Script executed:

# Look for Step generic type definition to understand the actual types involved
rg "type Step<|interface Step<" -A 5

Repository: AgentWorkforce/relay

Length of output: 46


🏁 Script executed:

# Check if there's a compiled version or dist folder with implementation
find . -name "*.d.ts" -path "*/relay/*" | head -10

Repository: AgentWorkforce/relay

Length of output: 46


🏁 Script executed:

# Look at line 36 more carefully - it already uses | undefined, check what's different
sed -n '28,41p' src/templates/review-loop.ts

Repository: AgentWorkforce/relay

Length of output: 648


🏁 Script executed:

# Search for how ctx parameter is used in other while/condition predicates
rg "\.while\s*:\s*\(|condition:\s*\(" -A 5 -B 2

Repository: AgentWorkforce/relay

Length of output: 439


🏁 Script executed:

# Look at the full LoopStep interface definition and search for any implementation hints
cat src/workflow/types.ts

Repository: AgentWorkforce/relay

Length of output: 552


🏁 Script executed:

# Search for tests or usage examples that show when while/condition is evaluated
find . -name "*.test.ts" -o -name "*.spec.ts" | xargs grep -l "LoopStep\|while.*condition" 2>/dev/null

Repository: AgentWorkforce/relay

Length of output: 48


🏁 Script executed:

# Check if there's documentation or comments about loop execution timing
rg "before.*iteration|first.*iteration|while.*evaluated" -i -B 2 -A 2

Repository: AgentWorkforce/relay

Length of output: 46


🏁 Script executed:

# Look at the finalize function - it suggests the context after loop completes, so while would be evaluated after first iteration
rg "finalize" -B 3 -A 5

Repository: AgentWorkforce/relay

Length of output: 14284


Guard loop condition against missing review output.

ctx.getStepOutput(reviewStep) is dereferenced without checking for undefined. The finalize callback (line 36) already applies defensive coding for the same method with | undefined, indicating the output can be absent. Apply the same guard to maintain consistency.

Proposed fix
     while: (ctx: StepContext) => {
-      const reviewOut = ctx.getStepOutput(reviewStep) as ReviewStepOutput;
-      return !reviewOut.passed;
+      const reviewOut = ctx.getStepOutput(reviewStep) as ReviewStepOutput | undefined;
+      return reviewOut ? !reviewOut.passed : true;
     },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
while: (ctx: StepContext) => {
const reviewOut = ctx.getStepOutput(reviewStep) as ReviewStepOutput;
return !reviewOut.passed;
},
while: (ctx: StepContext) => {
const reviewOut = ctx.getStepOutput(reviewStep) as ReviewStepOutput | undefined;
return reviewOut ? !reviewOut.passed : true;
},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/templates/review-loop.ts` around lines 30 - 33, The loop dereferences
ctx.getStepOutput(reviewStep) without guarding for undefined; change the
retrieval to const reviewOut = ctx.getStepOutput(reviewStep) as ReviewStepOutput
| undefined and update the condition to use optional chaining (e.g., return
!(reviewOut?.passed)) so the loop handles a missing review output consistently
with the finalize callback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Two workflow primitives gaps: role-based context loss and no convergence loops

1 participant