Skip to content

fix: resolve 4 resilience subsystem bugs (ideation, gotchas, condition, circuit-breaker)#584

Open
oalanicolas wants to merge 5 commits intomainfrom
fix/bugfix-517-475-472-469-resilience-cluster
Open

fix: resolve 4 resilience subsystem bugs (ideation, gotchas, condition, circuit-breaker)#584
oalanicolas wants to merge 5 commits intomainfrom
fix/bugfix-517-475-472-469-resilience-cluster

Conversation

@oalanicolas
Copy link
Collaborator

@oalanicolas oalanicolas commented Mar 11, 2026

Summary

Fixes 4 interconnected bugs in the resilience/core subsystem:

Test plan

  • All 303 test suites pass (7690 tests)
  • npm run lint — 0 errors
  • npm run typecheck — passes
  • Each fix is minimal and targeted — single line or small block change

Closes #517
Closes #475
Closes #472
Closes #469

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Error tracking now uses a sliding time window with timestamp pruning and resets stale entries.
    • Known-issue filtering now logs warnings on lookup failures instead of failing silently.
    • Circuit breaker preserves recovery timing while open, retaining existing failure transitions.
  • New Features

    • Added runtime condition overrides so orchestrations can set/modify loop and flow signals at runtime.
  • Changes

    • Unknown workflow conditions now default to fail-safe (false); several guidance checks enabled by default.
  • Chores

    • Updated install manifest metadata (file hashes and sizes).

@vercel
Copy link

vercel bot commented Mar 11, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
aios-core Ready Ready Preview, Comment Mar 11, 2026 3:22pm

Request Review

@github-actions github-actions bot added area: agents Agent system related area: workflows Workflow system related squad mcp type: test Test coverage and quality area: core Core framework (.aios-core/core/) area: installer Installer and setup (packages/installer/) area: synapse SYNAPSE context engine area: cli CLI tools (bin/, packages/aios-pro-cli/) area: pro Pro features (pro/) area: health-check Health check system area: docs Documentation (docs/) area: devops CI/CD, GitHub Actions (.github/) labels Mar 11, 2026
@coderabbitai
Copy link

coderabbitai bot commented Mar 11, 2026

Walkthrough

Adjusts multiple core modules: fixes named-import in ideation engine and its gotchas call, converts gotchas tracking to a sliding time-window, prevents circuit-breaker OPEN state from resetting recovery timeout, adds runtime override APIs and fail-safe defaults in condition evaluator, and updates install manifest metadata.

Changes

Cohort / File(s) Summary
Ideation engine import & gotcha handling
.aiox-core/core/ideation/ideation-engine.js
Switches import to destructure GotchasMemory from the memory module, calls listGotchas() (instead of getAll()), and logs a warning on failure to load gotchas.
Gotchas memory sliding-window
.aiox-core/core/memory/gotchas-memory.js
Replaces simple count++ with per-error timestamps array and sliding-window logic: prune entries outside errorWindowMs, recompute count/firstSeen/lastSeen, reset tracking when a new window starts.
Circuit breaker timing
.aiox-core/core/ids/circuit-breaker.js
recordFailure() no longer updates _lastFailureTime when the circuit is already OPEN, preserving the original recovery timeout so the breaker can move to HALF_OPEN as intended.
Condition evaluator runtime overrides & safety
.aiox-core/core/orchestration/condition-evaluator.js
Adds _runtimeOverrides and new APIs setRuntimeCondition(name, value) and setRuntimeConditions(overrides). Introduces runtime-driven conditions and changes unknown-condition behavior to fail-safe (false), with explanatory messaging.
Install manifest
.aiox-core/install-manifest.yaml
Regenerated manifest timestamp and updated per-file hashes/sizes (file count adjusted); metadata updated to reflect content changes. No public API signature changes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • Pedrovaleriolopez
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR introduces runtime override support in ConditionEvaluator (setRuntimeCondition/setRuntimeConditions) with permissive fallback behavior, which extends beyond the fail-safe requirement stated in #472 and PR description. Clarify intent: runtime overrides appear to be legitimate infrastructure for orchestrator control, but verify alignment with #472's fail-safe requirement and document if this represents intentional architectural change beyond the four stated bug fixes.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main change: fixing four interconnected bugs in the resilience subsystem (ideation, gotchas, condition, circuit-breaker).
Linked Issues check ✅ Passed The PR addresses all four linked issues (#517, #475, #472, #469) with corresponding code fixes: destructured import in ideation-engine, sliding-window error tracking in gotchas-memory, fail-safe fallback in condition-evaluator, and preserved recovery timeout in circuit-breaker.
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 (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/bugfix-517-475-472-469-resilience-cluster

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

github-actions bot commented Mar 11, 2026

📊 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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.aiox-core/core/orchestration/condition-evaluator.js (1)

109-110: ⚠️ Potential issue | 🟠 Major

Negated unknown conditions still fail open.

At Lines 109-110, !condition negates the recursive result, so an unknown condition now becomes !false instead of staying blocked. !typo_condition will therefore execute even though the inner condition is unrecognized, which misses the fail-safe goal for typoed conditions.

Also applies to: 148-150

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.aiox-core/core/orchestration/condition-evaluator.js around lines 109 - 110,
The negation branch in the evaluator currently negates whatever
this.evaluate(...) returns, turning unknown/blocked results into true; update
the '!' handling in the evaluate method (the condition.startsWith('!') branch)
to first call this.evaluate on condition.substring(1).trim(), check for an
unknown/blocked sentinel (e.g., null or undefined or whatever the evaluator uses
to signal "unknown") and if it is unknown return that sentinel unchanged; only
if the inner result is a definite boolean return its negation. Apply the same
defensive pattern to the other negation occurrence referenced (the block around
lines 148-150) so negated unknown conditions remain blocked rather than flipping
open.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.aiox-core/core/ideation/ideation-engine.js:
- Line 16: The code calls this.gotchasMemory.getAll() but GotchasMemory only
exposes listGotchas(), and the resulting TypeError is swallowed by an empty
catch; replace the call to this.gotchasMemory.getAll() with
this.gotchasMemory.listGotchas() (or add a getAll alias in GotchasMemory that
forwards to listGotchas) and improve the empty catch around the filtering logic
in the ideation engine by logging the error with context (or rethrow) so
failures aren’t silently ignored; reference GotchasMemory, listGotchas, and the
site that currently calls this.gotchasMemory.getAll() when making the change.

In @.aiox-core/core/memory/gotchas-memory.js:
- Around line 262-267: The current gap-based reset (checking if now -
tracking.lastSeen > this.options.errorWindowMs) allows stale occurrences to
accumulate; instead prune old samples by filtering tracking.samples (or a
timestamps array you maintain) to only those with timestamp >= now -
this.options.errorWindowMs, then recompute tracking.count and set
tracking.firstSeen to the earliest remaining timestamp (or now if empty); update
tracking.lastSeen as appropriate and use this pruning in the branch around the
code handling stale errors (the block referencing tracking.lastSeen,
tracking.count, tracking.firstSeen, tracking.samples and the later similar block
at lines ~270-272) so the errorWindowMs truly bounds counted occurrences rather
than relying on inactivity gaps.

---

Outside diff comments:
In @.aiox-core/core/orchestration/condition-evaluator.js:
- Around line 109-110: The negation branch in the evaluator currently negates
whatever this.evaluate(...) returns, turning unknown/blocked results into true;
update the '!' handling in the evaluate method (the condition.startsWith('!')
branch) to first call this.evaluate on condition.substring(1).trim(), check for
an unknown/blocked sentinel (e.g., null or undefined or whatever the evaluator
uses to signal "unknown") and if it is unknown return that sentinel unchanged;
only if the inner result is a definite boolean return its negation. Apply the
same defensive pattern to the other negation occurrence referenced (the block
around lines 148-150) so negated unknown conditions remain blocked rather than
flipping open.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 49493c73-d40c-401c-88a4-17ca3b7981e7

📥 Commits

Reviewing files that changed from the base of the PR and between ff711c1 and 6970d35.

📒 Files selected for processing (6)
  • .aiox-core/core/ideation/ideation-engine.js
  • .aiox-core/core/ids/circuit-breaker.js
  • .aiox-core/core/memory/gotchas-memory.js
  • .aiox-core/core/orchestration/condition-evaluator.js
  • .aiox-core/data/entity-registry.yaml
  • .aiox-core/install-manifest.yaml

@nikolasdehor
Copy link
Contributor

@Pedrovaleriolopez, os fixes de ideation e gotchas nesse PR já estão no nosso #521 (aberto 26/fev) e o condition evaluator/circuit-breaker nos nossos PRs anteriores. Temos PRs individuais com testes dedicados pra cada bug — abertos há 2 semanas aguardando review.

@Pedrovaleriolopez
Copy link
Contributor

Hey @oalanicolas — CodeRabbit flagged 3 actionable issues:

Actionable:

  1. .aiox-core/core/ideation/ideation-engine.js (L16): Calls this.gotchasMemory.getAll() but GotchasMemory only exposes listGotchas() — TypeError swallowed by empty catch. Fix the method call and improve the catch block with error logging.
  2. .aiox-core/core/memory/gotchas-memory.js (L262-267): Gap-based reset allows stale occurrences to accumulate. Switch to sliding window — prune tracking.samples to only those within errorWindowMs, then recompute count and firstSeen.
  3. (outside diff) .aiox-core/core/orchestration/condition-evaluator.js (L109-110, L148-150): Negated unknown conditions fail open (!falsetrue). Check for unknown/blocked sentinel before negating — only negate definite booleans.

CI is green. Please apply fixes and push.

oalanicolas added a commit that referenced this pull request Mar 11, 2026
- ideation-engine.js: replace getAll() (non-existent) with listGotchas()
  and improve empty catch to log the error with context so filtering
  failures are no longer silently ignored

- gotchas-memory.js: replace gap-based reset with true sliding window
  using a timestamps array; prune entries older than errorWindowMs on
  each trackError() call and recompute count/firstSeen from the pruned
  window so errorWindowMs genuinely bounds counted occurrences

- condition-evaluator.js: register six workflow guidance conditions
  (user_wants_ai_generation, stories_remaining, epic_complete,
  architecture_suggests_prd_changes, po_checklist_issues,
  user_has_generated_ui) that are human-driven and cannot be inferred
  from TechStackProfile; defaulting to true preserves backwards
  compatibility with greenfield-ui.yaml after the fail-safe false
  introduced in #472

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
.aiox-core/core/orchestration/condition-evaluator.js (1)

119-121: ⚠️ Potential issue | 🟠 Major

Negated unknown conditions still fail open.

!missing_condition now evaluates to true, and the same happens for negated composites like !(project_has_frontend && missing_condition), because the negation path only sees the inner false after the unknown-condition fallback. Please propagate an explicit “unknown” sentinel through recursive evaluation and only negate definite booleans.

Also applies to: 159-161

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.aiox-core/core/orchestration/condition-evaluator.js around lines 119 - 121,
The negation branch in evaluate (the if handling condition.startsWith('!'))
currently blindly negates whatever evaluate(...) returns, causing unknown
conditions to be treated as true; change it to propagate an explicit "unknown"
sentinel from recursive evaluate calls and only negate when the inner result is
a definite boolean (true/false). Concretely: have evaluate return a tri-state
(true / false / UNKNOWN) and in the '!' branch and the parenthesized/composite
handling (the corresponding evaluate code around the other branch at lines
~159-161) check if inner === UNKNOWN and if so return UNKNOWN, otherwise return
!inner; update any callers to handle the UNKNOWN sentinel consistently. Use the
existing evaluate function name and the negation/parenthesis handling sites to
locate the edits.
.aiox-core/core/memory/gotchas-memory.js (1)

275-295: ⚠️ Potential issue | 🟡 Minor

Windowed counts still carry stale sample metadata.

tracking.count is pruned to errorWindowMs, but tracking.samples is not. _autoCaptureGotcha() later uses tracking.samples to populate relatedFiles and trigger.files, so a newly captured gotcha can still point at files/contexts from outside the active window. Prune tracking.samples with the same cutoff before pushing the new sample.

Suggested fix
     const windowStart = now - this.options.errorWindowMs;
     tracking.timestamps.push(now);
     tracking.timestamps = tracking.timestamps.filter((ts) => ts >= windowStart);
+    tracking.samples = (tracking.samples || []).filter((sample) => {
+      const ts = Date.parse(sample.timestamp);
+      return Number.isFinite(ts) && ts >= windowStart;
+    });
 
     // Recompute derived fields from the pruned sliding window
     tracking.count = tracking.timestamps.length;
     tracking.firstSeen = tracking.timestamps.length > 0 ? tracking.timestamps[0] : now;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.aiox-core/core/memory/gotchas-memory.js around lines 275 - 295, The
tracking.samples array is not being pruned to the same sliding window as
tracking.timestamps, so stale sample metadata can be included when
_autoCaptureGotcha() builds relatedFiles/trigger.files; update the code in the
same block that computes windowStart (and updates tracking.timestamps) to filter
tracking.samples to only keep entries with timestamps >= windowStart (parse
sample.timestamp or store numeric timestamps consistently), then push the new
sample and still enforce the samples cap (e.g., samples.length < 5) — modify the
logic around tracking.samples in the function that updates tracking (refer to
tracking.timestamps, tracking.samples, windowStart, and _autoCaptureGotcha) so
samples reflect the same active window.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.aiox-core/core/ideation/ideation-engine.js:
- Line 16: The GotchasMemory instance is being created against process.cwd()
instead of the configured project root; update the IdeationEngine initialization
to instantiate GotchasMemory with this.rootPath (e.g., replace any new
GotchasMemory() or calls that default to process.cwd() with new
GotchasMemory(this.rootPath)) so known-gotcha filtering reads the correct .aiox
store; apply the same change for the other occurrence referenced (around the
GotchasMemory usage at lines ~30).

In @.aiox-core/core/memory/gotchas-memory.js:
- Around line 270-286: Persisted tracking entries that lack tracking.timestamps
should be backfilled from preserved metadata before pruning: if
tracking.timestamps is not an array, initialize it by using existing
tracking.count and tracking.lastSeen (or tracking.firstSeen if lastSeen missing)
to synthesize a timestamps array (e.g., create tracking.count timestamps ending
at tracking.lastSeen spaced backward by small deltas or evenly within
this.options.errorWindowMs), then proceed to push(now), filter by windowStart,
and recompute tracking.count/firstSeen as you already do; update the block
operating on tracking.timestamps, tracking.count, tracking.lastSeen, and
tracking.firstSeen to perform this seeding step when timestamps are absent.

---

Outside diff comments:
In @.aiox-core/core/memory/gotchas-memory.js:
- Around line 275-295: The tracking.samples array is not being pruned to the
same sliding window as tracking.timestamps, so stale sample metadata can be
included when _autoCaptureGotcha() builds relatedFiles/trigger.files; update the
code in the same block that computes windowStart (and updates
tracking.timestamps) to filter tracking.samples to only keep entries with
timestamps >= windowStart (parse sample.timestamp or store numeric timestamps
consistently), then push the new sample and still enforce the samples cap (e.g.,
samples.length < 5) — modify the logic around tracking.samples in the function
that updates tracking (refer to tracking.timestamps, tracking.samples,
windowStart, and _autoCaptureGotcha) so samples reflect the same active window.

In @.aiox-core/core/orchestration/condition-evaluator.js:
- Around line 119-121: The negation branch in evaluate (the if handling
condition.startsWith('!')) currently blindly negates whatever evaluate(...)
returns, causing unknown conditions to be treated as true; change it to
propagate an explicit "unknown" sentinel from recursive evaluate calls and only
negate when the inner result is a definite boolean (true/false). Concretely:
have evaluate return a tri-state (true / false / UNKNOWN) and in the '!' branch
and the parenthesized/composite handling (the corresponding evaluate code around
the other branch at lines ~159-161) check if inner === UNKNOWN and if so return
UNKNOWN, otherwise return !inner; update any callers to handle the UNKNOWN
sentinel consistently. Use the existing evaluate function name and the
negation/parenthesis handling sites to locate the edits.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b052b719-5132-45c2-83ed-de49d3d5463f

📥 Commits

Reviewing files that changed from the base of the PR and between 6970d35 and 460f1f9.

📒 Files selected for processing (4)
  • .aiox-core/core/ideation/ideation-engine.js
  • .aiox-core/core/memory/gotchas-memory.js
  • .aiox-core/core/orchestration/condition-evaluator.js
  • .aiox-core/install-manifest.yaml

let GotchasMemory;
try {
GotchasMemory = require('../memory/gotchas-memory');
({ GotchasMemory } = require('../memory/gotchas-memory'));
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

Instantiate GotchasMemory against the configured project root.

Now that the named export makes this path reachable again, the fallback constructor still uses process.cwd() instead of this.rootPath. When IdeationEngine is run against another workspace via config.rootPath, known-gotcha filtering will read the wrong .aiox store.

Suggested fix
-    this.gotchasMemory = config.gotchasMemory || (GotchasMemory ? new GotchasMemory() : null);
+    this.gotchasMemory =
+      config.gotchasMemory || (GotchasMemory ? new GotchasMemory(this.rootPath) : null);
As per coding guidelines, ".aiox-core/core/**": "Ensure backwards compatibility — core modules are consumed by all agents."

Also applies to: 30-30

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.aiox-core/core/ideation/ideation-engine.js at line 16, The GotchasMemory
instance is being created against process.cwd() instead of the configured
project root; update the IdeationEngine initialization to instantiate
GotchasMemory with this.rootPath (e.g., replace any new GotchasMemory() or calls
that default to process.cwd() with new GotchasMemory(this.rootPath)) so
known-gotcha filtering reads the correct .aiox store; apply the same change for
the other occurrence referenced (around the GotchasMemory usage at lines ~30).

Comment on lines +270 to +286
// Ensure timestamps array exists for entries loaded from disk before this fix
if (!Array.isArray(tracking.timestamps)) {
tracking.timestamps = [];
}

// Add current timestamp and prune occurrences outside the sliding error window.
// This ensures errorWindowMs truly bounds the counted occurrences rather than
// relying on inactivity gaps — the previous gap-based reset allowed stale errors
// (e.g., at 0h, 20h, 40h) to keep accumulating as long as each repeat landed
// just inside the gap boundary.
const windowStart = now - this.options.errorWindowMs;
tracking.timestamps.push(now);
tracking.timestamps = tracking.timestamps.filter((ts) => ts >= windowStart);

// Recompute derived fields from the pruned sliding window
tracking.count = tracking.timestamps.length;
tracking.firstSeen = tracking.timestamps.length > 0 ? tracking.timestamps[0] : now;
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

Backfill timestamps for pre-upgrade tracking entries.

When a persisted entry created before this change is loaded, tracking.timestamps is initialized to [], so the next occurrence recomputes tracking.count as 1 even if the stored count/lastSeen are still inside the active window. That breaks pending auto-captures across upgrade boundaries. Seed the window from persisted sample timestamps or preserved count metadata before recomputing.
As per coding guidelines, ".aiox-core/core/**": "Ensure backwards compatibility — core modules are consumed by all agents."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.aiox-core/core/memory/gotchas-memory.js around lines 270 - 286, Persisted
tracking entries that lack tracking.timestamps should be backfilled from
preserved metadata before pruning: if tracking.timestamps is not an array,
initialize it by using existing tracking.count and tracking.lastSeen (or
tracking.firstSeen if lastSeen missing) to synthesize a timestamps array (e.g.,
create tracking.count timestamps ending at tracking.lastSeen spaced backward by
small deltas or evenly within this.options.errorWindowMs), then proceed to
push(now), filter by windowStart, and recompute tracking.count/firstSeen as you
already do; update the block operating on tracking.timestamps, tracking.count,
tracking.lastSeen, and tracking.firstSeen to perform this seeding step when
timestamps are absent.

@Pedrovaleriolopez
Copy link
Contributor

Review ⚡ @devops

Code: APPROVED ✅ — Best PR of the batch. All 4 fixes are technically solid:

  1. ideation-engine.js — Named export fix + correct API call (listGotchas)
  2. circuit-breaker.js — Preserves recovery timeout when already OPEN
  3. gotchas-memory.js — Sliding window with timestamps array — excellent fix with backward compat
  4. condition-evaluator.js — Fail-safe default (false) + explicit workflow conditions

Blocker: ❌ Merge conflict — please rebase against main:

git fetch origin && git rebase origin/main

Once rebased, this is ready to merge immediately.

oalanicolas and others added 3 commits March 11, 2026 11:54
the GotchasMemory class (named vs default export). Used destructuring.

without checking errorWindowMs. Added window expiration check — resets
count, firstSeen, and samples when last occurrence exceeds the window.

Changed to false (fail-safe) — typos and unknown conditions now block
instead of silently authorizing execution.

restarting the recovery timeout on every failure. Now only updates
lastFailureTime when state is not OPEN, preserving the original
recovery window.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- ideation-engine.js: replace getAll() (non-existent) with listGotchas()
  and improve empty catch to log the error with context so filtering
  failures are no longer silently ignored

- gotchas-memory.js: replace gap-based reset with true sliding window
  using a timestamps array; prune entries older than errorWindowMs on
  each trackError() call and recompute count/firstSeen from the pruned
  window so errorWindowMs genuinely bounds counted occurrences

- condition-evaluator.js: register six workflow guidance conditions
  (user_wants_ai_generation, stories_remaining, epic_complete,
  architecture_suggests_prd_changes, po_checklist_issues,
  user_has_generated_ui) that are human-driven and cannot be inferred
  from TechStackProfile; defaulting to true preserves backwards
  compatibility with greenfield-ui.yaml after the fail-safe false
  introduced in #472

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@oalanicolas oalanicolas force-pushed the fix/bugfix-517-475-472-469-resilience-cluster branch from 460f1f9 to 8807e52 Compare March 11, 2026 14:54
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.aiox-core/core/orchestration/condition-evaluator.js (1)

119-121: ⚠️ Potential issue | 🟠 Major

Negated unknown conditions still fail open.

evaluate('!typo') returns true because the negation branch runs before the unknown-condition fallback. Unknown condition names need to stay blocked even when negated, otherwise a typo can still enable a phase.

As per coding guidelines, .aiox-core/core/**: Check for proper input validation on public API methods.

Also applies to: 159-161

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.aiox-core/core/orchestration/condition-evaluator.js around lines 119 - 121,
The negation branch in evaluate allows unknown condition names to pass because
it negates before validating existence; update the evaluate method (function
evaluate in condition-evaluator.js) so when condition.startsWith('!') you first
extract inner = condition.substring(1).trim(), check whether that inner
condition is a known/registered condition (use the same existence check used
elsewhere in this file), and if the inner condition is unknown return false (do
not allow negation to open it), otherwise return !this.evaluate(inner); make the
same change to the other negation handling block referenced around the second
occurrence (the block at the later lines) so both negated branches validate the
inner condition before negating.
♻️ Duplicate comments (1)
.aiox-core/core/memory/gotchas-memory.js (1)

270-286: ⚠️ Potential issue | 🟠 Major

Backfill legacy tracking state before recomputing the window.

If a persisted tracking entry has recent count/lastSeen data but no timestamps, initializing tracking.timestamps to [] makes the next trackError() recompute the windowed count as 1. That can defer or miss auto-capture immediately after upgrading with existing .aiox/error-tracking.json data.

As per coding guidelines, .aiox-core/core/**: Ensure backwards compatibility — core modules are consumed by all agents.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.aiox-core/core/memory/gotchas-memory.js around lines 270 - 286, Legacy
persisted tracking entries that have a nonzero tracking.count and
tracking.lastSeen but no tracking.timestamps get reset to a single new
timestamp, causing the windowed count to drop to 1; in trackError() (where
tracking.timestamps is initialized) detect when tracking.timestamps is
missing/empty and tracking.count > 0 and tracking.lastSeen is present and
backfill tracking.timestamps to reflect the historical occurrences (e.g.,
include tracking.lastSeen and additional inferred earlier timestamps so the
reconstructed array length matches tracking.count, then proceed with pushing now
and filtering by windowStart), and finally recompute tracking.count and
tracking.firstSeen from the pruned timestamps so auto-capture behavior remains
correct after upgrades.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.aiox-core/core/orchestration/condition-evaluator.js:
- Around line 101-110: The hard-coded true guards in ConditionEvaluator (the
keys stories_remaining, user_wants_ai_generation,
architecture_suggests_prd_changes, po_checklist_issues, user_has_generated_ui)
must be replaced with runtime lookups so they act as true guards only when no
runtime hook is provided: change each arrow ()=> true to a function that calls
the evaluator's runtime resolution API (e.g., this.getCondition?
this.evaluateCondition? this.runtime.getFlag — pick the existing method on
ConditionEvaluator to resolve a condition by name) and only fall back to true if
that runtime lookup returns undefined; update or add small shim methods on
ConditionEvaluator to expose a named lookup (e.g., resolveCondition(name)) and
use that from the condition map so optional workflow steps and the
stories_remaining loop can be gated correctly.

In @.aiox-core/install-manifest.yaml:
- Around line 243-246: The manifest contains stale hashes for
core/config/config-loader.js and core/execution/build-state-manager.js which
causes scripts/validate-manifest.js to fail; fix it by checking out a clean tree
(no local changes), regenerating the install-manifest.yaml using the
repository's manifest generation tool (or the same mechanism that produced the
original file) so the hashes for core/config/config-loader.js and
core/execution/build-state-manager.js are recomputed, and then commit the
updated manifest; ensure you run the same validation step
(scripts/validate-manifest.js) after regeneration to confirm no mismatches
remain.

---

Outside diff comments:
In @.aiox-core/core/orchestration/condition-evaluator.js:
- Around line 119-121: The negation branch in evaluate allows unknown condition
names to pass because it negates before validating existence; update the
evaluate method (function evaluate in condition-evaluator.js) so when
condition.startsWith('!') you first extract inner =
condition.substring(1).trim(), check whether that inner condition is a
known/registered condition (use the same existence check used elsewhere in this
file), and if the inner condition is unknown return false (do not allow negation
to open it), otherwise return !this.evaluate(inner); make the same change to the
other negation handling block referenced around the second occurrence (the block
at the later lines) so both negated branches validate the inner condition before
negating.

---

Duplicate comments:
In @.aiox-core/core/memory/gotchas-memory.js:
- Around line 270-286: Legacy persisted tracking entries that have a nonzero
tracking.count and tracking.lastSeen but no tracking.timestamps get reset to a
single new timestamp, causing the windowed count to drop to 1; in trackError()
(where tracking.timestamps is initialized) detect when tracking.timestamps is
missing/empty and tracking.count > 0 and tracking.lastSeen is present and
backfill tracking.timestamps to reflect the historical occurrences (e.g.,
include tracking.lastSeen and additional inferred earlier timestamps so the
reconstructed array length matches tracking.count, then proceed with pushing now
and filtering by windowStart), and finally recompute tracking.count and
tracking.firstSeen from the pruned timestamps so auto-capture behavior remains
correct after upgrades.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e7c98a45-d13b-4546-b0cb-b59b46def759

📥 Commits

Reviewing files that changed from the base of the PR and between 460f1f9 and 8807e52.

📒 Files selected for processing (6)
  • .aiox-core/core/ideation/ideation-engine.js
  • .aiox-core/core/ids/circuit-breaker.js
  • .aiox-core/core/memory/gotchas-memory.js
  • .aiox-core/core/orchestration/condition-evaluator.js
  • .aiox-core/data/entity-registry.yaml
  • .aiox-core/install-manifest.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • .aiox-core/core/ideation/ideation-engine.js

Clean manifest regeneration after rebase. Add runtime override support
for workflow guidance conditions instead of hard-coding true.

- Add _runtimeOverrides map to ConditionEvaluator constructor
- Add setRuntimeCondition(name, value) and setRuntimeConditions(overrides)
  public API so workflow engines can resolve human-intent conditions at
  runtime (stories_remaining, epic_complete, user_wants_ai_generation,
  architecture_suggests_prd_changes, po_checklist_issues,
  user_has_generated_ui)
- Each condition checks _runtimeOverrides first; falls back to true when
  no override is registered (preserves backwards-compatible permissive
  default for unmanaged executions)
- Add skip explanations for all 6 runtime-resolved conditions in
  getSkipExplanation()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.aiox-core/core/orchestration/condition-evaluator.js (1)

171-174: ⚠️ Potential issue | 🟠 Major

Negated unknown conditions fail open, undermining the fail-safe change.

The negation logic at line 173 inverts the result of evaluating the inner condition. If !typo_condition is evaluated:

  1. typo_condition is unknown → returns false (line 213)
  2. Negation inverts it → !false = true

This allows execution on typos like !user_wantsAI_generation, defeating the fail-safe intent of PR #472.

🐛 Proposed fix: detect unknown before negation
    // Handle negation first
    if (condition.startsWith('!')) {
-     return !this.evaluate(condition.substring(1).trim());
+     const innerCondition = condition.substring(1).trim();
+     // Check if inner condition is known before negating
+     const innerResult = this.evaluate(innerCondition);
+     // If inner condition logged "Unknown condition", the negation should also fail-safe.
+     // We need a way to distinguish "known false" from "unknown".
+     // Option: use a sentinel or check if innerCondition exists in evaluators/overrides.
+     const isKnown = this._isKnownCondition(innerCondition);
+     if (!isKnown) {
+       console.warn(`[ConditionEvaluator] Negation of unknown condition: ${condition}`);
+       return false; // fail-safe
+     }
+     return !innerResult;
    }

Add helper method:

/**
 * Check if a condition is known (exists in evaluators or runtime overrides)
 * `@private`
 * `@param` {string} condition
 * `@returns` {boolean}
 */
_isKnownCondition(condition) {
  // Check runtime overrides
  if (this._runtimeOverrides.hasOwnProperty(condition)) {
    return true;
  }
  // Check if it's a built-in evaluator key (would need to extract evaluators or maintain a set)
  // For simplicity, evaluate and check if warning was logged - or maintain known keys set
  const knownConditions = new Set([
    'project_has_database', 'project_has_frontend', 'project_has_backend',
    'project_has_typescript', 'project_has_tests', 'supabase_configured',
    'database_has_rls', 'database_has_migrations', 'frontend_has_react',
    'frontend_has_vue', 'frontend_has_tailwind', 'qa_review_approved',
    'phase_2_completed', 'phase_3_completed', 'all_collection_phases_complete',
    'has_any_data_to_analyze', 'user_wants_ai_generation', 'stories_remaining',
    'epic_complete', 'architecture_suggests_prd_changes', 'po_checklist_issues',
    'user_has_generated_ui'
  ]);
  return knownConditions.has(condition) || condition.includes('.') || 
         condition.includes('&&') || condition.includes('||');
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.aiox-core/core/orchestration/condition-evaluator.js around lines 171 - 174,
Negation currently inverts unknown conditions (making typos evaluate true); add
a private helper _isKnownCondition(condition) that checks runtime overrides and
known evaluator keys (or a maintained set of known condition strings) and then
change the negation branch in evaluate() to first compute inner =
condition.substring(1).trim() and if !_isKnownCondition(inner) return false (or
propagate "unknown"/safe-fail) instead of inverting — only call and invert
this.evaluate(inner) when _isKnownCondition(inner) is true; reference the
evaluate method, the negation branch, and the new _isKnownCondition helper to
locate changes.
🧹 Nitpick comments (1)
.aiox-core/core/orchestration/condition-evaluator.js (1)

139-162: Consider extracting a helper for runtime condition lookups.

The repeated pattern this._runtimeOverrides[X] !== undefined ? this._runtimeOverrides[X] : true could be simplified with a helper method. This is optional but improves maintainability if more runtime conditions are added.

♻️ Optional DRY refactor

Add a helper method:

/**
 * Get runtime condition value with permissive default
 * `@private`
 * `@param` {string} name - Condition name
 * `@param` {boolean} [defaultValue=true] - Value when not overridden
 * `@returns` {boolean}
 */
_getRuntimeCondition(name, defaultValue = true) {
  return this._runtimeOverrides[name] !== undefined
    ? this._runtimeOverrides[name]
    : defaultValue;
}

Then simplify the evaluators:

-      user_wants_ai_generation: () =>
-        this._runtimeOverrides['user_wants_ai_generation'] !== undefined
-          ? this._runtimeOverrides['user_wants_ai_generation']
-          : true,
+      user_wants_ai_generation: () => this._getRuntimeCondition('user_wants_ai_generation'),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.aiox-core/core/orchestration/condition-evaluator.js around lines 139 - 162,
Extract the repeated ternary into a private helper (e.g.,
_getRuntimeCondition(name, defaultValue = true)) that returns
this._runtimeOverrides[name] when defined otherwise defaultValue, then update
each evaluator (user_wants_ai_generation, stories_remaining, epic_complete,
architecture_suggests_prd_changes, po_checklist_issues, user_has_generated_ui)
to call _getRuntimeCondition('condition_name') instead of repeating the ternary;
ensure the helper lives on the same class/object so references to
this._runtimeOverrides remain valid.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.aiox-core/core/orchestration/condition-evaluator.js:
- Around line 73-85: Validate inputs for the public API: in
setRuntimeCondition(name, value) ensure name is a non-empty string (throw a
TypeError if not) before assigning this._runtimeOverrides[name] =
Boolean(value); in setRuntimeConditions(overrides) ensure overrides is a
non-null plain object (throw a TypeError if it's null/undefined or not an
object), then iterate Object.entries(overrides) and validate each key is a
non-empty string before setting this._runtimeOverrides[name] = Boolean(value);
keep using the same _runtimeOverrides storage and preserve current Boolean
coercion for values.
- Around line 143-146: The orchestrator currently never invokes
setRuntimeConditions, so the condition function stories_remaining in
condition-evaluator.js always returns the default true; update the orchestrator
(where it detects the story backlog exhaustion) to call setRuntimeConditions({
stories_remaining: false }) so loop guards can terminate; likewise set other
documented termination flags (e.g., epic_complete) via setRuntimeConditions when
their corresponding orchestrator state changes so condition-evaluator's runtime
overrides are honored.

---

Outside diff comments:
In @.aiox-core/core/orchestration/condition-evaluator.js:
- Around line 171-174: Negation currently inverts unknown conditions (making
typos evaluate true); add a private helper _isKnownCondition(condition) that
checks runtime overrides and known evaluator keys (or a maintained set of known
condition strings) and then change the negation branch in evaluate() to first
compute inner = condition.substring(1).trim() and if !_isKnownCondition(inner)
return false (or propagate "unknown"/safe-fail) instead of inverting — only call
and invert this.evaluate(inner) when _isKnownCondition(inner) is true; reference
the evaluate method, the negation branch, and the new _isKnownCondition helper
to locate changes.

---

Nitpick comments:
In @.aiox-core/core/orchestration/condition-evaluator.js:
- Around line 139-162: Extract the repeated ternary into a private helper (e.g.,
_getRuntimeCondition(name, defaultValue = true)) that returns
this._runtimeOverrides[name] when defined otherwise defaultValue, then update
each evaluator (user_wants_ai_generation, stories_remaining, epic_complete,
architecture_suggests_prd_changes, po_checklist_issues, user_has_generated_ui)
to call _getRuntimeCondition('condition_name') instead of repeating the ternary;
ensure the helper lives on the same class/object so references to
this._runtimeOverrides remain valid.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b5171a63-9bad-4f92-942f-67ccb65b3a60

📥 Commits

Reviewing files that changed from the base of the PR and between 8807e52 and 1d18b38.

📒 Files selected for processing (2)
  • .aiox-core/core/orchestration/condition-evaluator.js
  • .aiox-core/install-manifest.yaml

Comment on lines +73 to +85
setRuntimeCondition(name, value) {
this._runtimeOverrides[name] = Boolean(value);
}

/**
* Set multiple runtime conditions at once.
* @param {Object.<string, boolean>} overrides - Map of condition name to value
*/
setRuntimeConditions(overrides) {
for (const [name, value] of Object.entries(overrides)) {
this._runtimeOverrides[name] = Boolean(value);
}
}
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

Add input validation on public API methods.

Per coding guidelines for core modules, public APIs should validate inputs. Currently:

  • setRuntimeCondition doesn't validate that name is a non-empty string.
  • setRuntimeConditions will throw if overrides is null/undefined.
🛡️ Proposed defensive checks
  setRuntimeCondition(name, value) {
+   if (typeof name !== 'string' || !name.trim()) {
+     console.warn('[ConditionEvaluator] setRuntimeCondition called with invalid name:', name);
+     return;
+   }
    this._runtimeOverrides[name] = Boolean(value);
  }

  setRuntimeConditions(overrides) {
+   if (!overrides || typeof overrides !== 'object') {
+     console.warn('[ConditionEvaluator] setRuntimeConditions called with invalid overrides:', overrides);
+     return;
+   }
    for (const [name, value] of Object.entries(overrides)) {
      this._runtimeOverrides[name] = Boolean(value);
    }
  }
📝 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
setRuntimeCondition(name, value) {
this._runtimeOverrides[name] = Boolean(value);
}
/**
* Set multiple runtime conditions at once.
* @param {Object.<string, boolean>} overrides - Map of condition name to value
*/
setRuntimeConditions(overrides) {
for (const [name, value] of Object.entries(overrides)) {
this._runtimeOverrides[name] = Boolean(value);
}
}
setRuntimeCondition(name, value) {
if (typeof name !== 'string' || !name.trim()) {
console.warn('[ConditionEvaluator] setRuntimeCondition called with invalid name:', name);
return;
}
this._runtimeOverrides[name] = Boolean(value);
}
/**
* Set multiple runtime conditions at once.
* `@param` {Object.<string, boolean>} overrides - Map of condition name to value
*/
setRuntimeConditions(overrides) {
if (!overrides || typeof overrides !== 'object') {
console.warn('[ConditionEvaluator] setRuntimeConditions called with invalid overrides:', overrides);
return;
}
for (const [name, value] of Object.entries(overrides)) {
this._runtimeOverrides[name] = Boolean(value);
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.aiox-core/core/orchestration/condition-evaluator.js around lines 73 - 85,
Validate inputs for the public API: in setRuntimeCondition(name, value) ensure
name is a non-empty string (throw a TypeError if not) before assigning
this._runtimeOverrides[name] = Boolean(value); in
setRuntimeConditions(overrides) ensure overrides is a non-null plain object
(throw a TypeError if it's null/undefined or not an object), then iterate
Object.entries(overrides) and validate each key is a non-empty string before
setting this._runtimeOverrides[name] = Boolean(value); keep using the same
_runtimeOverrides storage and preserve current Boolean coercion for values.

Comment on lines +143 to +146
stories_remaining: () =>
this._runtimeOverrides['stories_remaining'] !== undefined
? this._runtimeOverrides['stories_remaining']
: true, // Repeat story cycle; orchestrator sets false when backlog is empty
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:

#!/bin/bash
# Description: Search for orchestrators calling setRuntimeConditions or setRuntimeCondition
# Expected: At least one call setting stories_remaining to false

echo "=== Searching for setRuntimeCondition(s) calls ==="
rg -n 'setRuntimeCondition' --type js -C3

echo ""
echo "=== Searching for stories_remaining assignments ==="
rg -n 'stories_remaining' --type js --type yaml -C2

Repository: SynkraAI/aiox-core

Length of output: 165


🏁 Script executed:

#!/bin/bash
# Check if the files exist and examine ConditionEvaluator usage

echo "=== Checking for condition-evaluator.js ==="
find . -name "condition-evaluator.js" -type f

echo ""
echo "=== Checking for workflow-orchestrator.js ==="
find . -name "workflow-orchestrator.js" -type f

echo ""
echo "=== Searching for ConditionEvaluator instantiation ==="
rg -n "new ConditionEvaluator\|ConditionEvaluator" --type js -C 2

echo ""
echo "=== Checking for any loop conditions or stories_remaining in workflows ==="
rg -n "while\|for\|loop" --type js -B2 -A2 | head -50

echo ""
echo "=== Checking for TODO/FIXME related to conditions ==="
rg -n "TODO|FIXME" --type js -C 2 | grep -i "condition\|loop\|orchestrat" -C 2

Repository: SynkraAI/aiox-core

Length of output: 884


🏁 Script executed:

#!/bin/bash
# Read the specific files referenced in the review

echo "=== Reading condition-evaluator.js (lines 130-160) ==="
sed -n '130,160p' ./.aiox-core/core/orchestration/condition-evaluator.js

echo ""
echo "=== Reading workflow-orchestrator.js (lines 355-375) ==="
sed -n '355,375p' ./.aiox-core/core/orchestration/workflow-orchestrator.js

echo ""
echo "=== Checking full condition-evaluator.js file length ==="
wc -l ./.aiox-core/core/orchestration/condition-evaluator.js

echo ""
echo "=== Checking full workflow-orchestrator.js file length ==="
wc -l ./.aiox-core/core/orchestration/workflow-orchestrator.js

echo ""
echo "=== Searching for imports of ConditionEvaluator across codebase ==="
rg -n "import.*ConditionEvaluator\|require.*condition-evaluator" --type js

Repository: SynkraAI/aiox-core

Length of output: 3365


🏁 Script executed:

#!/bin/bash
# Check for setRuntimeConditions method and its usage

echo "=== Searching for setRuntimeConditions method definition ==="
rg -n "setRuntimeConditions\|setRuntimeCondition" --type js -A 5

echo ""
echo "=== Searching for conditionEvaluator method calls (evaluate, etc.) ==="
rg -n "conditionEvaluator\." --type js -B 1 -A 1

echo ""
echo "=== Searching for stories_remaining usage in workflow logic ==="
rg -n "stories_remaining" --type js -B 2 -A 2

echo ""
echo "=== Checking workflow execution loops and conditionals ==="
rg -n "while.*condition|if.*condition" --type js -C 2 | head -60

Repository: SynkraAI/aiox-core

Length of output: 1785


🏁 Script executed:

#!/bin/bash
# Read the entire condition-evaluator.js file to understand the class structure

echo "=== Reading condition-evaluator.js class definition (first 100 lines) ==="
head -100 ./.aiox-core/core/orchestration/condition-evaluator.js

echo ""
echo "=== Reading for public methods and setRuntimeConditions ==="
rg -n "^\s+(set|get|evaluate)" ./.aiox-core/core/orchestration/condition-evaluator.js

echo ""
echo "=== Checking export statements ==="
tail -50 ./.aiox-core/core/orchestration/condition-evaluator.js

Repository: SynkraAI/aiox-core

Length of output: 5249


🏁 Script executed:

#!/bin/bash
# Search for actual usage of conditionEvaluator and evaluate method calls

echo "=== Searching for evaluate method calls ==="
rg -n "\.evaluate\(" --type js -B 2 -A 2

echo ""
echo "=== Searching for condition evaluation in orchestrator ==="
grep -n "should.*Execute\|evaluate.*Phase\|stories_remaining" ./.aiox-core/core/orchestration/workflow-orchestrator.js -C 3

echo ""
echo "=== Checking if stories_remaining is referenced in phase configs ==="
rg -n "stories_remaining\|loop.*condition" --type js --type json --type yaml -C 2

Repository: SynkraAI/aiox-core

Length of output: 7274


🏁 Script executed:

#!/bin/bash
# Check phase definitions and configurations

echo "=== Searching for phase definitions with condition properties ==="
rg -n "phase.*condition\|condition.*stories\|condition.*epic_complete" --type js --type json -B 2 -A 2

echo ""
echo "=== Checking orchestrator for phase configuration loading ==="
grep -n "phases\|getPhases\|phaseConfig" ./.aiox-core/core/orchestration/workflow-orchestrator.js -C 2 | head -40

echo ""
echo "=== Looking for any YAML/JSON phase definitions ==="
find ./.aiox-core -name "*.yaml" -o -name "*.yml" -o -name "*.json" | xargs grep -l "phase\|condition" 2>/dev/null | head -10

echo ""
echo "=== Checking if setRuntimeConditions is ever called (comprehensive search) ==="
git grep -n "setRuntimeCondition" 2>/dev/null || rg -r "setRuntimeCondition" . --type js --type json --type yaml

Repository: SynkraAI/aiox-core

Length of output: 2806


🏁 Script executed:

#!/bin/bash
# Check actual workflow structure

echo "=== Reading a sample workflow definition ==="
head -100 ./.aiox-core/development/workflows/brownfield-discovery.yaml

echo ""
echo "=== Checking for condition properties in workflows ==="
grep -n "condition:" ./.aiox-core/development/workflows/*.yaml ./.aiox-core/development/templates/squad/workflow-template.yaml

echo ""
echo "=== Checking bob surface criteria for phase conditions ==="
head -80 ./.aiox-core/core/orchestration/bob-surface-criteria.yaml

Repository: SynkraAI/aiox-core

Length of output: 12452


Integrate runtime condition setting into orchestrator to enable loop termination.

The stories_remaining condition is actively used across multiple workflows (brownfield-fullstack.yaml:226, greenfield-fullstack.yaml:246, etc.) as a loop guard. However, setRuntimeConditions() is never called anywhere in the codebase, meaning stories_remaining will always evaluate to its default value of true, preventing loop termination even when the orchestrator detects an empty backlog.

To fix this, the orchestrator must call setRuntimeConditions({stories_remaining: false}) when the story backlog is exhausted. The same applies to other loop-termination conditions (epic_complete) and user-driven conditions that are documented in condition-evaluator.js but are currently non-functional due to missing orchestrator integration.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.aiox-core/core/orchestration/condition-evaluator.js around lines 143 - 146,
The orchestrator currently never invokes setRuntimeConditions, so the condition
function stories_remaining in condition-evaluator.js always returns the default
true; update the orchestrator (where it detects the story backlog exhaustion) to
call setRuntimeConditions({ stories_remaining: false }) so loop guards can
terminate; likewise set other documented termination flags (e.g., epic_complete)
via setRuntimeConditions when their corresponding orchestrator state changes so
condition-evaluator's runtime overrides are honored.

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: cli CLI tools (bin/, packages/aios-pro-cli/) area: core Core framework (.aios-core/core/) area: devops CI/CD, GitHub Actions (.github/) area: docs Documentation (docs/) area: health-check Health check system area: installer Installer and setup (packages/installer/) area: pro Pro features (pro/) area: synapse SYNAPSE context engine area: workflows Workflow system related mcp squad type: test Test coverage and quality

Projects

None yet

3 participants