Skip to content

feat(core): add workflow orchestration module for multi-agent execution#39

Merged
Pedrovaleriolopez merged 4 commits intomainfrom
feat/workflow-orchestration-module
Jan 13, 2026
Merged

feat(core): add workflow orchestration module for multi-agent execution#39
Pedrovaleriolopez merged 4 commits intomainfrom
feat/workflow-orchestration-module

Conversation

@Pedrovaleriolopez
Copy link
Contributor

@Pedrovaleriolopez Pedrovaleriolopez commented Jan 13, 2026

Summary

Add comprehensive workflow orchestration system that enables:

  • Real subagent dispatch with persona transformation
  • Task-based execution (loads real .md task files, not generic prompts)
  • Deterministic code operations (mkdir, file validation via JavaScript)
  • Checklist-based quality validation
  • Parallel phase execution for independent workflow phases
  • Context persistence between workflow phases

New Files

File Purpose
workflow-orchestrator.js Main orchestrator with phase execution
subagent-prompt-builder.js Loads agent definitions, tasks, checklists, templates
context-manager.js Persists workflow state between phases
checklist-runner.js Programmatic checklist validation
parallel-executor.js Concurrent phase execution
index.js Module exports

CodeRabbit Fixes Applied

All critical issues from CodeRabbit review have been addressed:

Round 1 (commit 63639d9)

  • Add passedCount/failedCount tracking in checklist-runner
  • Fix validation to fail on missing files
  • Unknown validation rules now fail with warning
  • Fix loadState() to persist initial state
  • Implement deep copy in exportState/importState
  • Add block scoping to switch cases
  • Convert _projectHasDatabase to async

Round 2 (commit b408dec)

  • Fix directory-exists validation consistency
  • Add null guard to exportState()
  • Unknown pre/post action types now fail-fast
  • resumeFrom() honors parallel_phases config

Round 3 (commit 2300a13)

  • Guard reset() against undefined metadata
  • Fix race condition in parallel execution (thread-safe checklist handling)

Test Plan

  • ESLint: 0 errors
  • Orchestration tests: 19/19 passed
  • Full test suite: 2676 passed

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

New Features

  • Workflow execution system supporting multi-phase processes with persistent state tracking
  • Automated validation checklist framework for quality verification
  • Parallel task execution to improve workflow efficiency
  • Intelligent task orchestration with contextual guidance

✏️ Tip: You can customize this high-level summary in your review settings.

oalanicolas and others added 4 commits January 13, 2026 16:50
Add comprehensive workflow orchestration system that enables:

- Real subagent dispatch with persona transformation
- Task-based execution (loads real .md task files, not generic prompts)
- Deterministic code operations (mkdir, file validation via JavaScript)
- Checklist-based quality validation
- Parallel phase execution for independent workflow phases
- Context persistence between workflow phases

New files:
- workflow-orchestrator.js: Main orchestrator with phase execution
- subagent-prompt-builder.js: Loads agent definitions, tasks, checklists, templates
- context-manager.js: Persists workflow state between phases
- checklist-runner.js: Programmatic checklist validation
- parallel-executor.js: Concurrent phase execution
- index.js: Module exports

This module improves the brownfield-discovery workflow by:
1. Using REAL task files instead of generating generic prompts
2. Running pre/post actions via deterministic JavaScript code
3. Validating outputs with existing checklists
4. Supporting parallel execution of independent phases (1-3)

Co-Authored-By: Claude <noreply@anthropic.com>
## Checklist Runner (checklist-runner.js)
- Add passedCount/failedCount tracking for visibility
- Fix validation to fail on missing files (not empty, contains, min-size)
- Change unknown validation rules to fail with warning instead of passing
- Improve duplicate detection to use full string comparison
- Add YAML parse error warnings

## Context Manager (context-manager.js)
- Fix loadState() to persist initial state to disk
- Implement deep copy in exportState() and importState()

## Workflow Orchestrator (workflow-orchestrator.js)
- Add block scoping to switch cases in _executePreAction
- Add block scoping to switch cases in _executePostAction
- Convert _projectHasDatabase to async with fs.pathExists/readFile
- Make _evaluateCondition async to support file system checks
- Remove unused ParallelExecutor instantiation
- Fix linting warnings (quotes, trailing commas, unused params)

## Subagent Prompt Builder (subagent-prompt-builder.js)
- Add console.warn for YAML parse errors in checklist extraction
- Add console.warn for YAML parse errors in template extraction
- Document asterisk stripping convention in task references

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…H-1]

## Checklist Runner (checklist-runner.js)
- Fix directory-exists validation to mirror file-exists logic
- Use explicit continue for falsy targetPath
- Use fs.pathExists() before fs.stat() for consistency

## Context Manager (context-manager.js)
- Add null guard to exportState() - returns {} if state not loaded

## Workflow Orchestrator (workflow-orchestrator.js)
- Unknown pre-action types now return { success: false } (fail-fast)
- Unknown post-action types now return { success: false } (consistent)
- resumeFrom() now honors parallel_phases configuration
- resumeFrom() resets executionState.startTime for accurate timing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Address final 2 CodeRabbit critical issues:
- context-manager.js: Guard reset() against undefined metadata
  with fallback to empty object when state not yet loaded
- workflow-orchestrator.js: Fix race condition by threading
  checklist through prepResult instead of shared instance field

Also includes ESLint auto-fix for trailing commas.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@github-actions github-actions bot added type: docs Documentation improvements core area: agents Agent system related area: workflows Workflow system related squad squad: etl squad: creator mcp docker-mcp type: test Test coverage and quality labels Jan 13, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 13, 2026

Note

.coderabbit.yaml has unrecognized properties

CodeRabbit is using all valid settings from your configuration. Unrecognized properties (listed below) have been ignored and may indicate typos or deprecated fields that can be removed.

⚠️ Parsing warnings (1)
Validation error: Unrecognized key(s) in object: 'tools', 'abort_on_close'
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

Introduces a comprehensive orchestration framework for multi-agent workflows consisting of five new modules: WorkflowOrchestrator manages phase execution with subagent integration, ContextManager persists workflow state, ChecklistRunner validates outputs, SubagentPromptBuilder constructs task-specific prompts, and ParallelExecutor handles concurrent phase execution. An index module re-exports these components and provides factory functions.

Changes

Cohort / File(s) Summary
Core Orchestration Engine
.aios-core/core/orchestration/workflow-orchestrator.js, .aios-core/core/orchestration/parallel-executor.js
WorkflowOrchestrator (728 lines) loads YAML workflows, manages phase execution with pre/post-action hooks, integrates subagent dispatch callbacks, and tracks execution state. ParallelExecutor (225 lines) manages concurrent phase execution with configurable concurrency limits, task tracking, and status reporting.
State & Context Management
.aios-core/core/orchestration/context-manager.js
ContextManager (294 lines) persists workflow execution state to .aios/workflow-state/{workflowId}.json with in-memory caching, provides phase output retrieval, lifecycle state updates (markCompleted, markFailed), and context assembly for subagent prompts.
Validation & Prompt Building
.aios-core/core/orchestration/checklist-runner.js, .aios-core/core/orchestration/subagent-prompt-builder.js
ChecklistRunner (350 lines) executes validation checklists from Markdown/YAML, parses and normalizes items, evaluates against target paths using code-based rules, and handles blocker items. SubagentPromptBuilder (363 lines) assembles complete task-specific prompts by combining agent definitions, task definitions, loaded checklists/templates, and prior phase context.
Public API & Exports
.aios-core/core/orchestration/index.js
Centralizes exports of WorkflowOrchestrator, ContextManager, ChecklistRunner, ParallelExecutor, and SubagentPromptBuilder; provides factory functions createOrchestrator(), createContextManager(), and runChecklist() for streamlined instantiation.

Sequence Diagram

sequenceDiagram
    actor User
    participant WO as WorkflowOrchestrator
    participant PE as ParallelExecutor
    participant CM as ContextManager
    participant SPB as SubagentPromptBuilder
    participant Subagent as Subagent<br/>(via callback)
    participant CR as ChecklistRunner

    User->>WO: run(workflow)
    WO->>WO: loadWorkflow()
    WO->>CM: initialize()
    CM-->>WO: state
    WO->>WO: groupPhases()
    loop For each phase group
        WO->>WO: _executePreAction()
        alt Parallel phases
            WO->>PE: executeParallel(phases)
            PE->>PE: _executeWithConcurrencyLimit()
            loop For each phase
                PE->>SPB: buildPrompt()
                SPB-->>PE: prompt
                PE->>Subagent: dispatchSubagent(prompt)
                Subagent-->>PE: result
                PE->>CM: savePhaseOutput()
            end
            PE-->>WO: results
        else Sequential phases
            WO->>SPB: buildPrompt()
            SPB-->>WO: prompt
            WO->>Subagent: dispatchSubagent(prompt)
            Subagent-->>WO: result
            WO->>CM: savePhaseOutput()
        end
        WO->>WO: _executePostAction()
        WO->>CR: run(checklist)
        CR-->>WO: validation result
    end
    WO->>CM: markCompleted()
    WO-->>User: workflow result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Suggested labels

core, workflows, agents, tools

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(core): add workflow orchestration module for multi-agent execution' clearly and specifically describes the main addition—a workflow orchestration module for multi-agent execution—which aligns with the primary changeset introducing WorkflowOrchestrator and supporting classes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions
Copy link
Contributor

📊 Coverage Report

Coverage report not available

📈 Full coverage report available in Codecov


Generated by PR Automation (Story 6.1)

Copy link

@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: 4

🤖 Fix all issues with AI agents
In @.aios-core/core/orchestration/context-manager.js:
- Around line 1-294: Add unit tests for the ContextManager class to raise
.aios-core/core/ coverage above 60% by exercising lifecycle and edge cases: test
initialize() and loadState() create and return the initial state and persist it
to disk, savePhaseOutput(phaseNum, output) persists phase outputs and updates
currentPhase/status and isPhaseCompleted(phaseNum) reflects completion,
getContextForPhase(phaseNum) returns previousPhases correctly,
updateMetadata(metadata) merges metadata, markCompleted() and markFailed(error,
failedPhase) set status/completedAt/failedAt and error fields,
reset(keepMetadata) resets state while preserving or clearing metadata as
expected, and importState()/exportState() perform deep copy behavior; use
temporary filesystem fixtures (mock or tmp dirs) to avoid touching real
projectRoot and assert on file contents and JSON read/write to cover persistence
code paths.
- Around line 28-38: The constructor currently uses the raw workflowId to build
this.stateDir and this.statePath, allowing path traversal; in the constructor of
context-manager.js sanitize workflowId before using it by (a) normalizing to a
safe token: take only a validated basename (e.g., safeId =
path.basename(workflowId || '')) and enforce a whitelist regex like
/^[A-Za-z0-9._-]+$/; (b) if the whitelist check fails or result is empty,
replace with a deterministic safe fallback such as a hash (e.g.,
crypto.createHash('sha256').update(workflowId || '').digest('hex')); then use
that sanitized token when setting this.statePath and this.stateDir to ensure no
directory traversal (update references to workflowId -> safeId in the
constructor and any code that builds paths).

In @.aios-core/core/orchestration/parallel-executor.js:
- Around line 37-60: The concurrency limiter is broken because phases.map(async
...) starts all executions immediately; instead change the mapping to produce
thunks (functions) like () => executePhaseThunk(phase) that when invoked will
set this.runningTasks (use phaseId and startTime inside the thunk), call
executePhase(phase), and resolve/reject with the same result shape, and pass
that array of thunks to _executeWithConcurrencyLimit; then update
_executeWithConcurrencyLimit to expect and invoke each thunk (e.g., await
thunk()) rather than awaiting already-started promises so the maxConcurrency
limit is honored.

In @.aios-core/core/orchestration/workflow-orchestrator.js:
- Around line 268-271: The catch block that logs checklistRunner.run() errors
currently swallows failures; update the catch to record the error into the
validation object (e.g., push a descriptive error into validation.errors) or
rethrow the error so validation fails; specifically modify the try/catch around
checklistRunner.run() in the workflow-orchestrator to either add
validation.errors.push({ message: `Checklist error: ${error.message}`, error })
or throw error after logging so upstream validation observes the failure.
🧹 Nitpick comments (8)
.aios-core/core/orchestration/parallel-executor.js (1)

192-222: getSummary does not count cancelled tasks.

The switch statement handles completed, failed, and running, but cancelled tasks (set by cancelAll()) are silently ignored. Consider adding a cancelled counter for completeness.

♻️ Suggested enhancement
  getSummary() {
    let completed = 0;
    let failed = 0;
    let running = 0;
+   let cancelled = 0;
    let totalDuration = 0;

    for (const [, status] of this.runningTasks) {
      switch (status.status) {
        case 'completed':
          completed++;
          if (status.startTime && status.endTime) {
            totalDuration += status.endTime - status.startTime;
          }
          break;
        case 'failed':
          failed++;
          break;
        case 'running':
          running++;
          break;
+       case 'cancelled':
+         cancelled++;
+         break;
      }
    }

    return {
      total: this.runningTasks.size,
      completed,
      failed,
      running,
+     cancelled,
      averageDuration: completed > 0 ? Math.round(totalDuration / completed) : 0,
    };
  }
.aios-core/core/orchestration/subagent-prompt-builder.js (1)

318-326: Template content is always wrapped in a YAML code block.

Templates loaded from .md files will still be wrapped in ```yaml fences (line 324), which could be misleading. Consider detecting the file extension and using appropriate formatting.

♻️ Suggested enhancement

Track the file type when loading templates and format accordingly:

  // In loadTemplate method, return object with content and type
  async loadTemplate(templateName) {
    const extensions = ['.yaml', '.yml', '.md'];
    const baseName = templateName.replace(/\.(yaml|yml|md)$/, '');

    for (const ext of extensions) {
      const filePath = path.join(this.paths.templates, `${baseName}${ext}`);
      if (await fs.pathExists(filePath)) {
-       return await fs.readFile(filePath, 'utf8');
+       return {
+         content: await fs.readFile(filePath, 'utf8'),
+         type: ext === '.md' ? 'markdown' : 'yaml',
+       };
      }
    }

    return null;
  }

Then adjust the assembly:

  for (const template of templates) {
    prompt += `### ${template.name}\n\n`;
-   prompt += `\`\`\`yaml\n${template.content}\n\`\`\`\n\n`;
+   const fence = template.type === 'markdown' ? '' : 'yaml';
+   prompt += `\`\`\`${fence}\n${template.content}\n\`\`\`\n\n`;
  }
.aios-core/core/orchestration/checklist-runner.js (2)

60-75: Non-blocker failures don't affect overall passed status.

Currently, result.passed only becomes false when a blocker item fails (line 71). Non-blocker failures are counted in failedCount but don't mark the overall result as failed. This appears intentional, but consider documenting this behavior in the JSDoc or adding a passedWithWarnings status.


216-322: Validation rule matching relies on substring checks, which can be fragile.

Rules like validationLower.includes('file') && validationLower.includes('exist') could match unintended strings (e.g., "profile exists in database"). Consider using more structured rule definitions or stricter regex patterns for reliability.

♻️ Suggested approach

Consider a more explicit rule format:

// Instead of free-form strings, define structured rules:
// validation: { rule: 'file_exists' }
// validation: { rule: 'contains', value: 'some text' }
// validation: { rule: 'min_size', bytes: 1024 }

async executeValidation(validation, targetPaths) {
  // Support both string (legacy) and object (structured) formats
  if (typeof validation === 'object') {
    return this._executeStructuredValidation(validation, targetPaths);
  }
  // Fall back to current string-based matching
  return this._executeStringValidation(validation, targetPaths);
}

This provides a migration path while maintaining backward compatibility.

.aios-core/core/orchestration/workflow-orchestrator.js (1)

94-115: Directory list is hardcoded.

The directory structure is specific to a particular workflow type. Consider making this configurable via the workflow YAML or constructor options for reusability.

♻️ Suggested enhancement
  async setupDirectories() {
-   const dirs = [
-     'docs/architecture',
-     'docs/frontend',
-     // ... hardcoded list
-   ];
+   // Allow workflow to define required directories, with sensible defaults
+   const defaultDirs = ['.aios/workflow-state'];
+   const workflowDirs = this.workflow?.setup?.directories || [];
+   const dirs = [...new Set([...defaultDirs, ...workflowDirs])];
.aios-core/core/orchestration/context-manager.js (3)

88-103: Duplicate state initialization logic.

The loadState method (lines 98-102) duplicates the "create initial state if not exists" pattern from initialize() (lines 57-59). This violates DRY and could lead to inconsistencies if one path is updated but not the other.

♻️ Proposed refactor: Delegate to initialize()
  async loadState() {
    if (this._stateCache) {
      return this._stateCache;
    }

-   if (await fs.pathExists(this.statePath)) {
-     this._stateCache = await fs.readJson(this.statePath);
-     return this._stateCache;
-   }
-
-   // No state file exists - create, cache, and persist initial state
-   await this.ensureStateDir();
-   this._stateCache = this._createInitialState();
-   await fs.writeJson(this.statePath, this._stateCache, { spaces: 2 });
-   return this._stateCache;
+   // Delegate to initialize() which handles both cases
+   return this.initialize();
  }

165-180: Synchronous accessors depend on cache being loaded.

getPreviousPhaseOutputs() and getPhaseOutput() return empty/null if called before initialize() or loadState(). This is handled gracefully with null guards, but callers should be aware of this precondition.

Consider adding a brief note to the JSDoc indicating the cache-dependent behavior, or alternatively making these async with automatic cache loading.


288-291: Consider validating imported state structure.

importState accepts any object without validating required fields (workflowId, status, phases, etc.). Importing malformed state could cause runtime errors in subsequent operations.

♻️ Proposed fix: Add basic validation
  async importState(state) {
+   if (!state || typeof state !== 'object') {
+     throw new Error('Invalid state: must be an object');
+   }
+   if (!state.workflowId || !state.status || !state.phases) {
+     throw new Error('Invalid state: missing required fields (workflowId, status, phases)');
+   }
    this._stateCache = JSON.parse(JSON.stringify(state));
    await this._saveState();
  }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between feca895 and 2300a13.

📒 Files selected for processing (6)
  • .aios-core/core/orchestration/checklist-runner.js
  • .aios-core/core/orchestration/context-manager.js
  • .aios-core/core/orchestration/index.js
  • .aios-core/core/orchestration/parallel-executor.js
  • .aios-core/core/orchestration/subagent-prompt-builder.js
  • .aios-core/core/orchestration/workflow-orchestrator.js
🧰 Additional context used
🧬 Code graph analysis (4)
.aios-core/core/orchestration/parallel-executor.js (2)
.aios-core/core/orchestration/workflow-orchestrator.js (1)
  • chalk (15-15)
.aios-core/core/orchestration/index.js (1)
  • ParallelExecutor (18-18)
.aios-core/core/orchestration/index.js (2)
.aios-core/core/orchestration/workflow-orchestrator.js (3)
  • SubagentPromptBuilder (17-17)
  • ContextManager (18-18)
  • ChecklistRunner (19-19)
.aios-core/cli/commands/migrate/analyze.js (1)
  • module (193-193)
.aios-core/core/orchestration/checklist-runner.js (2)
.aios-core/core/orchestration/workflow-orchestrator.js (1)
  • ChecklistRunner (19-19)
.aios-core/core/orchestration/index.js (1)
  • ChecklistRunner (17-17)
.aios-core/core/orchestration/context-manager.js (2)
.aios-core/core/orchestration/workflow-orchestrator.js (1)
  • ContextManager (18-18)
.aios-core/core/orchestration/index.js (1)
  • ContextManager (16-16)
🪛 GitHub Actions: CI
.aios-core/core/orchestration/subagent-prompt-builder.js

[error] 1-1: Jest: coverage threshold for lines not met. Required 60%, received 52.71% in ".aios-core/core/". This caused the CI to fail.

.aios-core/core/orchestration/parallel-executor.js

[error] 1-1: Jest: coverage threshold for lines not met. Required 60%, received 52.71% in ".aios-core/core/". This caused the CI to fail.

.aios-core/core/orchestration/index.js

[error] 1-1: Jest: coverage threshold for lines not met. Required 60%, received 52.71% in ".aios-core/core/". This caused the CI to fail.

.aios-core/core/orchestration/checklist-runner.js

[error] 1-1: Jest: coverage threshold for lines not met. Required 60%, received 52.71% in ".aios-core/core/". This caused the CI to fail.

.aios-core/core/orchestration/workflow-orchestrator.js

[error] 1-1: Jest: coverage threshold for lines not met. Required 60%, received 52.71% in ".aios-core/core/". This caused the CI to fail.

.aios-core/core/orchestration/context-manager.js

[error] 1-1: Jest: coverage threshold for lines not met. Required 60%, received 52.71% in ".aios-core/core/". This caused the CI to fail.

🔇 Additional comments (23)
.aios-core/core/orchestration/parallel-executor.js (1)

16-20: LGTM!

The constructor initializes reasonable defaults. The runningTasks Map provides a clean way to track phase execution state.

.aios-core/core/orchestration/subagent-prompt-builder.js (5)

23-38: LGTM!

The constructor properly initializes paths relative to the project root with a clear structure for agents, tasks, checklists, and templates.


83-97: LGTM!

The fallback pattern handling (hyphenated and underscored filenames) and graceful degradation when agent definitions are missing is a practical approach for development workflows.


131-166: LGTM!

Good implementation with proper precedence (override checklist first), graceful YAML parsing error handling, and deduplication of checklists already loaded via override.


232-245: LGTM!

The multi-extension support (.yaml, .yml, .md) provides good flexibility for template formats.


252-271: LGTM!

The context section formatting correctly handles the case when no previous phases exist and provides clear output structure.

.aios-core/core/orchestration/index.js (1)

14-45: LGTM!

Clean facade pattern providing centralized access to orchestration components with convenient factory functions. The async runChecklist helper simplifies standalone checklist execution.

.aios-core/core/orchestration/checklist-runner.js (3)

101-143: LGTM!

Good dual-format parsing (YAML blocks and Markdown checkboxes) with proper deduplication and graceful error handling for invalid YAML.


151-172: LGTM!

The normalization handles both string and object formats well. The support for Portuguese field names (tipo, validação) alongside English equivalents is a nice touch for internationalized checklist definitions.


329-347: LGTM!

The summary method provides useful metadata about checklist composition including blocker counts and category breakdowns.

.aios-core/core/orchestration/workflow-orchestrator.js (9)

36-63: LGTM!

Well-structured constructor with sensible defaults, dependency injection for dispatchSubagent, and proper binding of default callbacks. The execution state tracking provides good visibility into workflow progress.


123-159: LGTM!

Solid preparation logic with proper directory creation, preAction execution, and the thread-safe pattern of threading checklist through prepResult rather than relying on instance state during parallel execution.


173-176: check_tool always returns success without verification.

The comment indicates tools are "assumed available in Claude Code environment," but this doesn't actually verify tool availability. If a tool is missing at runtime, the phase will fail unexpectedly later.

Consider whether tool availability can be verified programmatically, or document this limitation clearly in the workflow YAML schema.


320-363: LGTM!

The execution flow is well-structured: load workflow → setup directories → group phases → execute groups respecting parallelization. The separation of concerns between grouping and execution is clean.


369-394: LGTM!

The grouping logic correctly batches consecutive parallel phases while separating sequential ones.


420-518: LGTM!

Comprehensive single-phase execution with proper condition/dependency checks, context building, subagent dispatch (with fallback for manual execution), and validation. The error handling correctly tracks failed phases.


525-554: LGTM!

The condition evaluation handles both string-based and object-based conditions with sensible defaults. The set of built-in conditions is appropriate for the current workflow use cases.


693-725: LGTM!

Clean resume implementation that reuses the phase grouping and execution logic from execute(), properly honoring parallel phase configuration and resetting timing for accurate duration reporting.


1-11: CI failure: Test coverage threshold not met.

The pipeline indicates Jest coverage is at 52.71%, below the required 60% threshold. This orchestration module introduces significant new code that needs test coverage to pass CI.

Consider adding tests for:

  • ParallelExecutor._executeWithConcurrencyLimit (with the fix for actual concurrency limiting)
  • ChecklistRunner.executeValidation for each validation rule type
  • WorkflowOrchestrator._executeSinglePhase with mocked dependencies
  • Error paths and edge cases in phase execution
.aios-core/core/orchestration/context-manager.js (4)

1-18: Well-documented module header.

The JSDoc documentation clearly outlines the module's purpose and responsibilities. The imports are minimal and appropriate for the filesystem operations needed.


121-134: Clean phase output persistence.

The method correctly timestamps phase completion and updates workflow status. The spread pattern allows caller-provided fields while ensuring completedAt is always set.


142-159: Context collection logic is sound.

The method correctly gathers outputs from all prior phases (1-indexed). The sparse phase handling (skipping missing phases) is appropriate for resume scenarios.


182-217: Lifecycle methods are well-structured.

markCompleted, markFailed, and updateMetadata follow a consistent pattern and correctly persist state changes. Error tracking with failedPhase enables good debuggability.

Comment on lines +1 to +294
/**
* Context Manager - Persists workflow state between phases
*
* DETERMINISTIC: All operations use file system operations (fs-extra),
* no AI involvement in state management.
*
* Responsibilities:
* - Save phase outputs to JSON files
* - Provide context to subsequent phases
* - Track workflow execution state
* - Enable workflow resume from any phase
*
* @module core/orchestration/context-manager
* @version 1.0.0
*/

const fs = require('fs-extra');
const path = require('path');

/**
* Manages workflow execution context and state persistence
*/
class ContextManager {
/**
* @param {string} workflowId - Unique workflow identifier
* @param {string} projectRoot - Project root directory
*/
constructor(workflowId, projectRoot) {
this.workflowId = workflowId;
this.projectRoot = projectRoot;

// State file path
this.stateDir = path.join(projectRoot, '.aios', 'workflow-state');
this.statePath = path.join(this.stateDir, `${workflowId}.json`);

// In-memory cache
this._stateCache = null;
}

/**
* Ensure state directory exists
* DETERMINISTIC: Pure fs operation
*/
async ensureStateDir() {
await fs.ensureDir(this.stateDir);
}

/**
* Initialize or load existing workflow state
* @returns {Promise<Object>} Current state
*/
async initialize() {
await this.ensureStateDir();

if (await fs.pathExists(this.statePath)) {
this._stateCache = await fs.readJson(this.statePath);
} else {
this._stateCache = this._createInitialState();
await this._saveState();
}

return this._stateCache;
}

/**
* Create initial state structure
* @private
*/
_createInitialState() {
return {
workflowId: this.workflowId,
status: 'initialized',
startedAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
currentPhase: 0,
phases: {},
metadata: {
projectRoot: this.projectRoot,
},
};
}

/**
* Load state from disk (or cache)
* Persists initial state if no state file exists
* @returns {Promise<Object>} Current state
*/
async loadState() {
if (this._stateCache) {
return this._stateCache;
}

if (await fs.pathExists(this.statePath)) {
this._stateCache = await fs.readJson(this.statePath);
return this._stateCache;
}

// No state file exists - create, cache, and persist initial state
await this.ensureStateDir();
this._stateCache = this._createInitialState();
await fs.writeJson(this.statePath, this._stateCache, { spaces: 2 });
return this._stateCache;
}

/**
* Save state to disk
* DETERMINISTIC: Pure fs operation
* @private
*/
async _saveState() {
await this.ensureStateDir();
this._stateCache.updatedAt = new Date().toISOString();
await fs.writeJson(this.statePath, this._stateCache, { spaces: 2 });
}

/**
* Save phase output and update state
* @param {number} phaseNum - Phase number
* @param {Object} output - Phase output data
*/
async savePhaseOutput(phaseNum, output) {
const state = await this.loadState();

state.phases[phaseNum] = {
...output,
completedAt: new Date().toISOString(),
};

state.currentPhase = phaseNum;
state.status = 'in_progress';

this._stateCache = state;
await this._saveState();
}

/**
* Get context for a specific phase
* Includes outputs from all previous phases
* @param {number} phaseNum - Target phase number
* @returns {Promise<Object>} Context for the phase
*/
async getContextForPhase(phaseNum) {
const state = await this.loadState();

// Collect outputs from all previous phases
const previousPhases = {};
for (let i = 1; i < phaseNum; i++) {
if (state.phases[i]) {
previousPhases[i] = state.phases[i];
}
}

return {
workflowId: this.workflowId,
currentPhase: phaseNum,
previousPhases,
metadata: state.metadata,
};
}

/**
* Get all previous phase outputs
* @returns {Object} Map of phase number to output
*/
getPreviousPhaseOutputs() {
if (!this._stateCache) {
return {};
}
return this._stateCache.phases || {};
}

/**
* Get output from a specific phase
* @param {number} phaseNum - Phase number
* @returns {Object|null} Phase output or null
*/
getPhaseOutput(phaseNum) {
const outputs = this.getPreviousPhaseOutputs();
return outputs[phaseNum] || null;
}

/**
* Mark workflow as completed
*/
async markCompleted() {
const state = await this.loadState();
state.status = 'completed';
state.completedAt = new Date().toISOString();
this._stateCache = state;
await this._saveState();
}

/**
* Mark workflow as failed
* @param {string} error - Error message
* @param {number} failedPhase - Phase that failed
*/
async markFailed(error, failedPhase) {
const state = await this.loadState();
state.status = 'failed';
state.error = error;
state.failedPhase = failedPhase;
state.failedAt = new Date().toISOString();
this._stateCache = state;
await this._saveState();
}

/**
* Update workflow metadata
* @param {Object} metadata - Metadata to merge
*/
async updateMetadata(metadata) {
const state = await this.loadState();
state.metadata = { ...state.metadata, ...metadata };
this._stateCache = state;
await this._saveState();
}

/**
* Get the last completed phase number
* @returns {number} Last completed phase number (0 if none)
*/
getLastCompletedPhase() {
const phases = this.getPreviousPhaseOutputs();
const phaseNums = Object.keys(phases).map(Number);
return phaseNums.length > 0 ? Math.max(...phaseNums) : 0;
}

/**
* Check if a specific phase was completed
* @param {number} phaseNum - Phase number
* @returns {boolean} True if phase was completed
*/
isPhaseCompleted(phaseNum) {
const output = this.getPhaseOutput(phaseNum);
return output !== null && output.completedAt !== undefined;
}

/**
* Get workflow execution summary
* @returns {Object} Execution summary
*/
getSummary() {
const state = this._stateCache || this._createInitialState();
const phases = Object.keys(state.phases || {}).map(Number);

return {
workflowId: state.workflowId,
status: state.status,
startedAt: state.startedAt,
completedAt: state.completedAt,
currentPhase: state.currentPhase,
completedPhases: phases,
totalPhases: phases.length,
};
}

/**
* Reset workflow state (for re-execution)
* @param {boolean} keepMetadata - Whether to preserve metadata
*/
async reset(keepMetadata = true) {
// Guard: ensure metadata is always an object, even if state not loaded yet
const metadata = keepMetadata ? this._stateCache?.metadata || {} : {};
this._stateCache = this._createInitialState();
this._stateCache.metadata = metadata;
await this._saveState();
}

/**
* Export state for external use
* Uses deep copy to prevent external mutation of internal state
* @returns {Object} Complete state object (deep copy), empty object if state not loaded
*/
exportState() {
// Guard against null/undefined _stateCache (called before state loaded)
if (!this._stateCache) {
return {};
}
return JSON.parse(JSON.stringify(this._stateCache));
}

/**
* Import state from external source
* Uses deep copy to prevent external mutation after import
* @param {Object} state - State to import
*/
async importState(state) {
this._stateCache = JSON.parse(JSON.stringify(state));
await this._saveState();
}
}

module.exports = ContextManager;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Search for test files mentioning ContextManager
fd -t f -e js -e ts \( -path '*/test*' -o -path '*/__tests__/*' -o -path '*/spec*' \) | head -20

Repository: SynkraAI/aios-core

Length of output: 230


🏁 Script executed:

# Search for ContextManager references in test files
rg 'ContextManager' --type=js -g '*test*' -g '*spec*' -g '__tests__'

Repository: SynkraAI/aios-core

Length of output: 44


🏁 Script executed:

# Check if there's a specific test file for context-manager
fd -t f 'context-manager' . | grep -E '(test|spec|__tests__)'

Repository: SynkraAI/aios-core

Length of output: 44


🏁 Script executed:

# Explore test directory structure
find . -type d \( -name 'test' -o -name '__tests__' -o -name 'tests' -o -name 'spec' \) 2>/dev/null | head -10

Repository: SynkraAI/aios-core

Length of output: 261


🏁 Script executed:

# List test directory structure
find ./tests -type f -name '*.js' -o -name '*.ts' 2>/dev/null | head -20

Repository: SynkraAI/aios-core

Length of output: 866


🏁 Script executed:

# Check for jest config and coverage settings
find . -maxdepth 2 -type f \( -name 'jest.config.*' -o -name '.jestrc*' -o -name 'coverage*' \) 2>/dev/null

Repository: SynkraAI/aios-core

Length of output: 78


🏁 Script executed:

# Search for any test files in .aios-core directory related to orchestration
find ./.aios-core -type f -name '*test*' -o -name '*spec*' 2>/dev/null | grep -i orchestration

Repository: SynkraAI/aios-core

Length of output: 44


🏁 Script executed:

# Check for test files in the core orchestration directory specifically
ls -la ./.aios-core/core/orchestration/ 2>/dev/null || echo "Directory not found or no files"

Repository: SynkraAI/aios-core

Length of output: 604


🏁 Script executed:

# Check jest configuration for coverage thresholds
cat ./jest.config.js

Repository: SynkraAI/aios-core

Length of output: 4552


🏁 Script executed:

# Search for any indirect references to context-manager in test files
rg 'context-manager|ContextManager' ./tests --type js

Repository: SynkraAI/aios-core

Length of output: 44


🏁 Script executed:

# Check if orchestration is mentioned anywhere in tests
rg 'orchestration' ./tests --type js

Repository: SynkraAI/aios-core

Length of output: 543


🏁 Script executed:

# Verify this is a new file by checking git status
git status .aios-core/core/orchestration/context-manager.js 2>/dev/null || echo "File status check"

Repository: SynkraAI/aios-core

Length of output: 127


🏁 Script executed:

# Check what the actual coverage requirement is and if this file would be included
grep -A 5 "\.aios-core/core/" ./jest.config.js | grep -E "(lines|branches|functions)"

Repository: SynkraAI/aios-core

Length of output: 160


Add test coverage for ContextManager class.

The .aios-core/core/ modules must meet 60% line coverage. Add tests for state initialization, phase output persistence, metadata updates, workflow state transitions (completed/failed), and edge cases like reset with/without metadata preservation.

🧰 Tools
🪛 GitHub Actions: CI

[error] 1-1: Jest: coverage threshold for lines not met. Required 60%, received 52.71% in ".aios-core/core/". This caused the CI to fail.

🤖 Prompt for AI Agents
In @.aios-core/core/orchestration/context-manager.js around lines 1 - 294, Add
unit tests for the ContextManager class to raise .aios-core/core/ coverage above
60% by exercising lifecycle and edge cases: test initialize() and loadState()
create and return the initial state and persist it to disk,
savePhaseOutput(phaseNum, output) persists phase outputs and updates
currentPhase/status and isPhaseCompleted(phaseNum) reflects completion,
getContextForPhase(phaseNum) returns previousPhases correctly,
updateMetadata(metadata) merges metadata, markCompleted() and markFailed(error,
failedPhase) set status/completedAt/failedAt and error fields,
reset(keepMetadata) resets state while preserving or clearing metadata as
expected, and importState()/exportState() perform deep copy behavior; use
temporary filesystem fixtures (mock or tmp dirs) to avoid touching real
projectRoot and assert on file contents and JSON read/write to cover persistence
code paths.

Comment on lines +28 to +38
constructor(workflowId, projectRoot) {
this.workflowId = workflowId;
this.projectRoot = projectRoot;

// State file path
this.stateDir = path.join(projectRoot, '.aios', 'workflow-state');
this.statePath = path.join(this.stateDir, `${workflowId}.json`);

// In-memory cache
this._stateCache = null;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check how ContextManager is instantiated
rg -n -C3 'new ContextManager\(' --type=js

Repository: SynkraAI/aios-core

Length of output: 44


🏁 Script executed:

# Also search for ContextManager instantiation patterns without 'new'
rg -n 'ContextManager' --type=js -A 2 -B 2 | head -100

Repository: SynkraAI/aios-core

Length of output: 44


🏁 Script executed:

# Look for where workflowId comes from
rg -n 'workflowId' --type=js -B 3 | head -150

Repository: SynkraAI/aios-core

Length of output: 523


🏁 Script executed:

# Find the ContextManager file
fd -t f 'context-manager' --type=js

Repository: SynkraAI/aios-core

Length of output: 231


🏁 Script executed:

# Look for imports of ContextManager
rg -n 'ContextManager' --type=js

Repository: SynkraAI/aios-core

Length of output: 44


🏁 Script executed:

# Check the file mentioned in the review
cat -n .aios-core/core/orchestration/context-manager.js | head -50

Repository: SynkraAI/aios-core

Length of output: 1721


🏁 Script executed:

# Search for ContextManager usage more broadly
rg 'ContextManager' -i

Repository: SynkraAI/aios-core

Length of output: 44


🏁 Script executed:

# Look for files that might instantiate ContextManager
fd 'orchestrat' -type f

Repository: SynkraAI/aios-core

Length of output: 232


🏁 Script executed:

# Check the orchestrator or workflow files
ls -la .aios-core/core/orchestration/

Repository: SynkraAI/aios-core

Length of output: 604


🏁 Script executed:

# Check what's exported from index.js
cat -n .aios-core/core/orchestration/index.js

Repository: SynkraAI/aios-core

Length of output: 1725


🏁 Script executed:

# Check workflow-orchestrator.js for ContextManager usage
rg -n 'new ContextManager|ContextManager\(' .aios-core/core/orchestration/workflow-orchestrator.js

Repository: SynkraAI/aios-core

Length of output: 150


🏁 Script executed:

# Look at relevant sections of workflow-orchestrator.js
head -100 .aios-core/core/orchestration/workflow-orchestrator.js

Repository: SynkraAI/aios-core

Length of output: 3812


🏁 Script executed:

# Find where WorkflowOrchestrator is instantiated
rg -n 'new WorkflowOrchestrator|createOrchestrator' --max-count=20

Repository: SynkraAI/aios-core

Length of output: 44


🏁 Script executed:

# Check if there's any validation on workflowId before it's used
rg -n 'validate.*workflowId|sanitize.*workflowId|workflowId.*validate' -i

Repository: SynkraAI/aios-core

Length of output: 44


🏁 Script executed:

# Look at the loadWorkflow method more carefully
sed -n '70,110p' .aios-core/core/orchestration/workflow-orchestrator.js

Repository: SynkraAI/aios-core

Length of output: 1446


🏁 Script executed:

# Search more broadly for WorkflowOrchestrator usage
rg -n 'WorkflowOrchestrator' -i -A 3 | head -80

Repository: SynkraAI/aios-core

Length of output: 44


🏁 Script executed:

# Check main entry points to see how workflows are loaded
find . -name 'main.js' -o -name 'index.js' -o -name 'app.js' -o -name 'cli.js' | head -10

Repository: SynkraAI/aios-core

Length of output: 486


🏁 Script executed:

# Look for CLI or main entry
fd 'main|cli|app' -type f | grep -E '\.(js|ts)$' | head -20

Repository: SynkraAI/aios-core

Length of output: 232


🏁 Script executed:

# Check the CLI main entry
cat -n .aios-core/cli/index.js | head -80

Repository: SynkraAI/aios-core

Length of output: 3253


🏁 Script executed:

# Look for workflow command
ls -la .aios-core/cli/commands/

Repository: SynkraAI/aios-core

Length of output: 555


🏁 Script executed:

# Search for where workflow files are loaded or specified
rg -n 'workflowPath|workflow\.yaml|\.yaml' | grep -v node_modules | grep -v test | head -30

Repository: SynkraAI/aios-core

Length of output: 3191


Potential path traversal vulnerability via workflow.id field.

The workflowId is sourced from the YAML workflow file (this.workflow.workflow?.id) and used directly in path construction without sanitization. If a YAML workflow defines workflow: { id: "../../malicious" }, state files could be written outside the intended .aios/workflow-state/ directory.

Proposed fix: Sanitize workflowId
  constructor(workflowId, projectRoot) {
+   // Sanitize workflowId to prevent path traversal
+   const sanitizedId = path.basename(workflowId).replace(/[^a-zA-Z0-9_-]/g, '_');
+   if (!sanitizedId) {
+     throw new Error('Invalid workflowId');
+   }
+   this.workflowId = sanitizedId;
-   this.workflowId = workflowId;
    this.projectRoot = projectRoot;

    // State file path
    this.stateDir = path.join(projectRoot, '.aios', 'workflow-state');
-   this.statePath = path.join(this.stateDir, `${workflowId}.json`);
+   this.statePath = path.join(this.stateDir, `${sanitizedId}.json`);

    // In-memory cache
    this._stateCache = null;
  }
🤖 Prompt for AI Agents
In @.aios-core/core/orchestration/context-manager.js around lines 28 - 38, The
constructor currently uses the raw workflowId to build this.stateDir and
this.statePath, allowing path traversal; in the constructor of
context-manager.js sanitize workflowId before using it by (a) normalizing to a
safe token: take only a validated basename (e.g., safeId =
path.basename(workflowId || '')) and enforce a whitelist regex like
/^[A-Za-z0-9._-]+$/; (b) if the whitelist check fails or result is empty,
replace with a deterministic safe fallback such as a hash (e.g.,
crypto.createHash('sha256').update(workflowId || '').digest('hex')); then use
that sanitized token when setting this.statePath and this.stateDir to ensure no
directory traversal (update references to workflowId -> safeId in the
constructor and any code that builds paths).

Comment on lines +37 to +60
const promises = phases.map(async (phase) => {
const phaseId = phase.phase || phase.step;
this.runningTasks.set(phaseId, { status: 'running', startTime: Date.now() });

try {
const result = await executePhase(phase);
this.runningTasks.set(phaseId, {
status: 'completed',
endTime: Date.now(),
result,
});
return { phase: phaseId, status: 'fulfilled', result };
} catch (error) {
this.runningTasks.set(phaseId, {
status: 'failed',
endTime: Date.now(),
error: error.message,
});
return { phase: phaseId, status: 'rejected', error: error.message };
}
});

// Execute with concurrency limit
const settled = await this._executeWithConcurrencyLimit(promises, maxConcurrency);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Concurrency limiting does not work as intended.

The phases.map(async ...) on line 37 immediately starts all promises. By the time these promises are passed to _executeWithConcurrencyLimit, they are already executing concurrently. The concurrency limit has no effect.

To properly limit concurrency, you should pass functions (thunks) that create promises, not already-started promises.

🐛 Proposed fix
  async executeParallel(phases, executePhase, options = {}) {
    const maxConcurrency = options.maxConcurrency || this.maxConcurrency;
-   const results = [];
-   const errors = [];

    console.log(chalk.yellow(`\n⚡ Executing ${phases.length} phases in parallel (max ${maxConcurrency} concurrent)`));

-   // Use Promise.allSettled for resilient parallel execution
-   const promises = phases.map(async (phase) => {
+   // Create task functions (thunks) instead of immediately executing
+   const taskFns = phases.map((phase) => async () => {
      const phaseId = phase.phase || phase.step;
      this.runningTasks.set(phaseId, { status: 'running', startTime: Date.now() });

      try {
        const result = await executePhase(phase);
        this.runningTasks.set(phaseId, {
          status: 'completed',
          endTime: Date.now(),
          result,
        });
        return { phase: phaseId, status: 'fulfilled', result };
      } catch (error) {
        this.runningTasks.set(phaseId, {
          status: 'failed',
          endTime: Date.now(),
          error: error.message,
        });
        return { phase: phaseId, status: 'rejected', error: error.message };
      }
    });

    // Execute with concurrency limit
-   const settled = await this._executeWithConcurrencyLimit(promises, maxConcurrency);
+   const settled = await this._executeWithConcurrencyLimit(taskFns, maxConcurrency);

Then update _executeWithConcurrencyLimit to invoke the thunks:

  async _executeWithConcurrencyLimit(tasks, limit) {
    const results = [];
    const executing = new Set();

-   for (const task of tasks) {
-     const p = Promise.resolve().then(() => task);
+   for (const taskFn of tasks) {
+     const p = taskFn();
      results.push(p);

-     if (limit <= tasks.length) {
+     if (tasks.length > limit) {
        const e = p.then(() => executing.delete(e));
        executing.add(e);

        if (executing.size >= limit) {
          await Promise.race(executing);
        }
      }
    }

    return Promise.allSettled(results);
  }
🤖 Prompt for AI Agents
In @.aios-core/core/orchestration/parallel-executor.js around lines 37 - 60, The
concurrency limiter is broken because phases.map(async ...) starts all
executions immediately; instead change the mapping to produce thunks (functions)
like () => executePhaseThunk(phase) that when invoked will set this.runningTasks
(use phaseId and startTime inside the thunk), call executePhase(phase), and
resolve/reject with the same result shape, and pass that array of thunks to
_executeWithConcurrencyLimit; then update _executeWithConcurrencyLimit to expect
and invoke each thunk (e.g., await thunk()) rather than awaiting already-started
promises so the maxConcurrency limit is honored.

Comment on lines +268 to +271
} catch (error) {
console.log(chalk.yellow(` ⚠️ Checklist error: ${error.message}`));
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Checklist execution errors are silently caught.

When checklistRunner.run() throws an exception (line 268), it's logged but doesn't fail validation. This could mask real issues like missing checklist files or I/O errors. Consider failing validation or at least adding an error to validation.errors.

🐛 Proposed fix
      } catch (error) {
        console.log(chalk.yellow(`   ⚠️ Checklist error: ${error.message}`));
+       validation.errors.push(`Checklist execution error: ${error.message}`);
      }
🤖 Prompt for AI Agents
In @.aios-core/core/orchestration/workflow-orchestrator.js around lines 268 -
271, The catch block that logs checklistRunner.run() errors currently swallows
failures; update the catch to record the error into the validation object (e.g.,
push a descriptive error into validation.errors) or rethrow the error so
validation fails; specifically modify the try/catch around checklistRunner.run()
in the workflow-orchestrator to either add validation.errors.push({ message:
`Checklist error: ${error.message}`, error }) or throw error after logging so
upstream validation observes the failure.

@Pedrovaleriolopez
Copy link
Contributor Author

Follow-up Items (Post-Merge)

The following CodeRabbit suggestions will be addressed in subsequent stories:

Security

  • Path traversal in workflowId (context-manager.js:28-38) - Sanitize workflowId to prevent directory traversal attacks

Bugs

  • Concurrency limiter (parallel-executor.js:37-60) - Fix to use thunks instead of immediately-started promises
  • Error swallowing (workflow-orchestrator.js:268-271) - Propagate checklist errors to validation object

Coverage

  • Add orchestration tests - Raise .aios-core/core/ coverage from 52.71% to 60%+

These items are tracked for the next sprint. The core functionality is working and tested locally.

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

Labels

area: agents Agent system related area: workflows Workflow system related docker-mcp mcp squad: creator squad: etl squad type: docs Documentation improvements type: test Test coverage and quality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants