Skip to content

docs: improve core JSDoc coverage - #679

Merged
rafaelscosta merged 8 commits into
mainfrom
issue-89-docstring-coverage-20260507
May 7, 2026
Merged

docs: improve core JSDoc coverage#679
rafaelscosta merged 8 commits into
mainfrom
issue-89-docstring-coverage-20260507

Conversation

@rafaelscosta

@rafaelscosta rafaelscosta commented May 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Expands JSDoc coverage across the issue chore: increase docstring coverage to 80% #89 priority modules: bob, greenfield, brownfield, session state, lock manager, data lifecycle, config, events, gate evaluator, master orchestrator, and recovery handler.
  • Adds clearer return/throw contracts, dependency lifecycle notes, result shapes, typedefs, and examples without changing runtime behavior.
  • Regenerates the AIOX install manifest for updated hashes and sizes.

Validation

  • npm run validate:manifest
  • npm run validate:paths
  • git diff --check
  • npm run typecheck
  • npm run lint (0 errors; existing trailing-comma warnings remain outside this change)
  • npm test -- --runInBand tests/core/events/dashboard-emitter-bob.test.js tests/core/gate-evaluator.test.js tests/core/orchestration/session-state.test.js tests/core/orchestration/bob-orchestrator.test.js tests/core/orchestration/brownfield-handler.test.js tests/core/orchestration/data-lifecycle-manager.test.js tests/core/orchestration/lock-manager.test.js tests/core/orchestration/greenfield-handler.test.js tests/core/master-orchestrator.test.js tests/core/recovery-handler.test.js

Closes #89

Summary by CodeRabbit

  • New Features

    • Added a helper to load a single configuration section.
  • Improvements

    • Recovery APIs now validate epic identifiers and return deep‑cloned, sanitized attempt history to prevent external mutation.
    • Dashboard startup failures are non‑blocking and logged.
  • Documentation

    • Expanded public docs for config loading, events, orchestration, locking, session state, and recovery.
  • Tests

    • Added tests covering dashboard resilience and recovery-handler validation/sanitization.
  • Chores

    • Regenerated install manifest.

@vercel

vercel Bot commented May 7, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
aiox-core Ready Ready Preview, Comment May 7, 2026 9:56pm

Request Review

@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Expanded JSDoc across core config, events, and orchestration modules; added exported helper getConfigSection(sectionName); hardened RecoveryHandler APIs with epicNum validation and sanitized cloned attempt-history returns; tests added; install manifest hashes refreshed.

Changes

Core Documentation & Public API Clarification

Layer / File(s) Summary
Configuration API & Helpers
core/config/config-loader.js
New exported getConfigSection(sectionName) delegates to loadConfigSections and returns a single section; extensive JSDoc added for cache/TTL, lazy loads, preload, clearCache, validation, and performance metrics.
Event Emission & Dashboard
core/events/dashboard-emitter.js
DashboardEmitter JSDoc clarifies singleton lifecycle, session/context enrichment, non-blocking POST behavior, and fallback JSONL write; method-level docs for emit, _postEvent, _writeToFile.
Orchestration Handlers
core/orchestration/bob-orchestrator.js, brownfield-handler.js, greenfield-handler.js
Class-level JSDoc expanded; private lazy getters (_getWorkflowExecutor, _getSurfaceChecker, _getSessionState) documented; _detectEducationalModeToggle and _checkExistingSession return shapes clarified.
System Managers
core/orchestration/lock-manager.js, data-lifecycle-manager.js, gate-evaluator.js
Added LockFile typedef; LockManager staleness and TTL semantics documented; DataLifecycleManager startup cleanup documented; GateEvaluator docs for verdicts, config loading, logs, and clear() updated.
State Management & Recovery APIs
core/orchestration/session-state.js, recovery-handler.js
SessionState JSDoc expanded for persistence and update semantics. RecoveryHandler now validates epicNum inputs for several APIs, returns cloned/sanitized attempt histories, and adds private helpers _assertValidEpicNum and _cloneAttempt/_sanitizeValue.
Master Orchestrator
core/orchestration/master-orchestrator.js
JSDoc expanded; startDashboard() now catches dashboard start errors and logs them as warnings (non-blocking). Other public method docs clarified.
Installation Manifest & Tests
.aiox-core/install-manifest.yaml, tests/core/*
Manifest hashes and sizes refreshed for modified files; tests added for dashboard-start resilience and RecoveryHandler cloning/validation behavior.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • #89: chore: increase docstring coverage to 80% — This PR expands JSDoc across the exact modules named in that issue.

Possibly related PRs

  • SynkraAI/aiox-core#581: Related to core/config/config-loader.js changes; both touch config metrics/behavior docs.

Suggested labels

type: docs

Suggested reviewers

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

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ❓ Inconclusive One functional change detected: RecoveryHandler now validates epicNum and sanitizes attempt history. While outside pure documentation scope, it directly supports Issue #89's goal of improving code quality and maintainability. Clarify whether the RecoveryHandler functional changes (input validation, sanitization) were intentional enhancements for Issue #89 or should be addressed separately.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'docs: improve core JSDoc coverage' is concise, clear, and directly summarizes the main change—expanding JSDoc documentation across core modules.
Linked Issues check ✅ Passed The PR successfully addresses Issue #89 by expanding JSDoc coverage across all specified modules (high, medium, and low priority) with detailed return/throw contracts and parameter documentation.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-89-docstring-coverage-20260507

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 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 May 7, 2026
@github-actions

github-actions Bot commented May 7, 2026

Copy link
Copy Markdown
Contributor

📊 Coverage Report

Coverage report not available

📈 Full coverage report available in Codecov


Generated by PR Automation (Story 6.1)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (4)
.aiox-core/core/orchestration/recovery-handler.js (2)

699-735: ⚡ Quick win

New public methods accept epicNum without validation — critical path input guard required.

resetAttempts(null) creates this.attempts[null] = [], injecting a null-keyed entry into the internal map. canRetry(undefined) returns true (0 < maxRetries), silently enabling retries for a phantom epic. getAttemptCount propagates the same flaw.

🛡️ Proposed fix — add a shared validator
+  /**
+   * Validates that epicNum is a non-negative integer.
+   * `@param` {number} epicNum
+   * `@throws` {TypeError}
+   * `@private`
+   */
+  _assertValidEpicNum(epicNum) {
+    if (typeof epicNum !== 'number' || !Number.isInteger(epicNum) || epicNum < 0) {
+      throw new TypeError(`epicNum must be a non-negative integer, got: ${JSON.stringify(epicNum)}`);
+    }
+  }

   getAttemptCount(epicNum) {
+    this._assertValidEpicNum(epicNum);
     return (this.attempts[epicNum] || []).length;
   }

   canRetry(epicNum) {
+    this._assertValidEpicNum(epicNum);
     return this.getAttemptCount(epicNum) < this.maxRetries;
   }

   resetAttempts(epicNum) {
+    this._assertValidEpicNum(epicNum);
     this.attempts[epicNum] = [];
     this._log(`Reset attempts for Epic ${epicNum}`, 'info');
   }

As per coding guidelines: "Check for proper input validation on public API methods."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.aiox-core/core/orchestration/recovery-handler.js around lines 699 - 735,
Public methods getAttemptCount, canRetry, and resetAttempts accept epicNum
without validation, allowing null/undefined to create invalid keys; add a shared
validator method (e.g., _validateEpicNum(epicNum)) that asserts epicNum is a
finite non-negative integer (or the project-required integer range) and throws a
TypeError for invalid input, then call this validator at the start of
getAttemptCount, canRetry, and resetAttempts to prevent creating null/undefined
keys and to fail-fast on bad callers.

687-696: ⚡ Quick win

getAttemptHistory() shallow copy exposes mutable internal arrays — callers can silently corrupt recovery state.

{ ...this.attempts } creates a new outer object, but each value is the same array reference as this.attempts[epicNum]. Any caller that pushes to or splices a returned array will corrupt the handler's internal attempt records:

const h = handler.getAttemptHistory();
h[4].push({ fake: true }); // mutates handler.attempts[4] directly

This is a state-corruption risk in a critical orchestration module. Each array should also be copied:

🛡️ Proposed fix
  getAttemptHistory() {
-   return { ...this.attempts };
+   const copy = {};
+   for (const [epicNum, attempts] of Object.entries(this.attempts)) {
+     copy[epicNum] = [...attempts];
+   }
+   return copy;
  }

As per coding guidelines: "Check session-state.js for thread-safety and state corruption prevention" (same principle applies to recovery-handler state).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.aiox-core/core/orchestration/recovery-handler.js around lines 687 - 696,
getAttemptHistory() currently returns a shallow copy of this.attempts so the
outer object is new but the inner arrays are the same references; update
getAttemptHistory to return a new map where each epic's array is cloned (e.g.,
copy each this.attempts[epicNum] into a new array) so callers cannot mutate the
handler's internal arrays; locate getAttemptHistory and replace the
spread-return with logic that builds a new object (or uses
Object.entries/Object.fromEntries) mapping each key to a copied array (e.g.,
array.slice() or equivalent) while preserving the original keys and values
order.
.aiox-core/core/orchestration/master-orchestrator.js (1)

937-945: ⚡ Quick win

startDashboard() should not let dashboard initialization errors propagate unhandled.

The new implementation calls dashboardIntegration.start() with no error guard. If the dashboard integration fails to initialize (e.g., port conflict, write permission, or serialization error), the error propagates to the caller with no context. The dashboard is a non-critical observability subsystem and consistent with how other non-critical paths are handled in this class (e.g., _saveState catches and logs, _evaluateGate swallows on non-strict mode).

🛡️ Proposed fix
  async startDashboard() {
-   await this.dashboardIntegration.start();
+   try {
+     await this.dashboardIntegration.start();
+   } catch (error) {
+     this._log(`Dashboard start failed (non-blocking): ${error.message}`, { level: 'warn' });
+   }
  }

As per coding guidelines: "Verify error handling is comprehensive with proper try/catch and error context."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.aiox-core/core/orchestration/master-orchestrator.js around lines 937 - 945,
startDashboard currently awaits this.dashboardIntegration.start() with no error
handling, so any dashboard init failure will propagate; wrap the call to
this.dashboardIntegration.start() in a try/catch inside startDashboard, catch
any error, log a descriptive non-fatal error via the existing logger (include
the caught error details and context like "dashboard start failed"), and do not
rethrow so the dashboard remains non-critical; ensure you reference
startDashboard and dashboardIntegration.start in your change and keep behavior
consistent with other non-critical handlers (e.g., _saveState).
.aiox-core/core/config/config-loader.js (1)

295-309: ⚡ Quick win

LGTM — getConfigSection is clean and correctly documented.

The implementation correctly delegates to loadConfigSections and returns only the requested key, inheriting all existing cache/TTL behavior. The JSDoc @example aids discoverability in this deprecated module.

One minor note: the module-level migration guide (Lines 6–10) doesn't mention a replacement for getConfigSection. Users who adopt this new function won't have a migration path when the module is removed in v4.0.0.

💡 Suggested migration guide amendment
 * Migration guide:
 * - Config resolution: const { resolveConfig } = require('./config-resolver');
 *                      const config = await resolveConfig(projectRoot);
 * - Agent config:      const { AgentConfigLoader } = require('./agent-config-loader');
 *                      const loader = new AgentConfigLoader(agentId);
 *                      const config = await loader.load(coreConfig);
+* - Single section:    const { resolveConfig } = require('./config-resolver');
+*                      const result = await resolveConfig(projectRoot);
+*                      const section = result?.config?.['sectionName'];
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.aiox-core/core/config/config-loader.js around lines 295 - 309, Update the
module-level migration guide to mention the deprecated getConfigSection function
and show its replacement: call loadConfigSections([sectionName]) and return the
desired key (e.g., const cfg = await loadConfigSections([name]); return
cfg[name]); and include a brief example and note that getConfigSection will be
removed in v4.0.0 so callers should migrate to loadConfigSections (or the new
config API if one exists).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In @.aiox-core/core/config/config-loader.js:
- Around line 295-309: Update the module-level migration guide to mention the
deprecated getConfigSection function and show its replacement: call
loadConfigSections([sectionName]) and return the desired key (e.g., const cfg =
await loadConfigSections([name]); return cfg[name]); and include a brief example
and note that getConfigSection will be removed in v4.0.0 so callers should
migrate to loadConfigSections (or the new config API if one exists).

In @.aiox-core/core/orchestration/master-orchestrator.js:
- Around line 937-945: startDashboard currently awaits
this.dashboardIntegration.start() with no error handling, so any dashboard init
failure will propagate; wrap the call to this.dashboardIntegration.start() in a
try/catch inside startDashboard, catch any error, log a descriptive non-fatal
error via the existing logger (include the caught error details and context like
"dashboard start failed"), and do not rethrow so the dashboard remains
non-critical; ensure you reference startDashboard and dashboardIntegration.start
in your change and keep behavior consistent with other non-critical handlers
(e.g., _saveState).

In @.aiox-core/core/orchestration/recovery-handler.js:
- Around line 699-735: Public methods getAttemptCount, canRetry, and
resetAttempts accept epicNum without validation, allowing null/undefined to
create invalid keys; add a shared validator method (e.g.,
_validateEpicNum(epicNum)) that asserts epicNum is a finite non-negative integer
(or the project-required integer range) and throws a TypeError for invalid
input, then call this validator at the start of getAttemptCount, canRetry, and
resetAttempts to prevent creating null/undefined keys and to fail-fast on bad
callers.
- Around line 687-696: getAttemptHistory() currently returns a shallow copy of
this.attempts so the outer object is new but the inner arrays are the same
references; update getAttemptHistory to return a new map where each epic's array
is cloned (e.g., copy each this.attempts[epicNum] into a new array) so callers
cannot mutate the handler's internal arrays; locate getAttemptHistory and
replace the spread-return with logic that builds a new object (or uses
Object.entries/Object.fromEntries) mapping each key to a copied array (e.g.,
array.slice() or equivalent) while preserving the original keys and values
order.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a82de67d-c809-4513-8664-566e93b1d62f

📥 Commits

Reviewing files that changed from the base of the PR and between afbf7c0 and e2d3481.

📒 Files selected for processing (12)
  • .aiox-core/core/config/config-loader.js
  • .aiox-core/core/events/dashboard-emitter.js
  • .aiox-core/core/orchestration/bob-orchestrator.js
  • .aiox-core/core/orchestration/brownfield-handler.js
  • .aiox-core/core/orchestration/data-lifecycle-manager.js
  • .aiox-core/core/orchestration/gate-evaluator.js
  • .aiox-core/core/orchestration/greenfield-handler.js
  • .aiox-core/core/orchestration/lock-manager.js
  • .aiox-core/core/orchestration/master-orchestrator.js
  • .aiox-core/core/orchestration/recovery-handler.js
  • .aiox-core/core/orchestration/session-state.js
  • .aiox-core/install-manifest.yaml

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 7, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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/recovery-handler.js (1)

167-169: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

handleEpicFailure skips the validation added by this PR.

Every other public epic-number API now calls _assertValidEpicNum, but handleEpicFailure — the primary mutation entry point — does not. Passing a non-integer (e.g. null, "3", 1.5) silently creates a spurious key in this.attempts and produces misleading log messages, making subsequent calls to getAttemptCount / canRetry return incorrect results for the intended epic.

🛡️ Proposed fix
 async handleEpicFailure(epicNum, error, context = {}) {
+  this._assertValidEpicNum(epicNum);
   const errorMessage = error instanceof Error ? error.message : String(error);

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

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.aiox-core/core/orchestration/recovery-handler.js around lines 167 - 169,
handleEpicFailure currently omits the input validation used elsewhere and can
accept non-integer epic identifiers, creating spurious keys in this.attempts;
fix it by calling the existing _assertValidEpicNum(epicNum) at the start of
handleEpicFailure (before any mutation or logging) so invalid values (null, "3",
1.5, etc.) are rejected consistently with other public epic-number APIs like
getAttemptCount and canRetry.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.aiox-core/core/orchestration/master-orchestrator.js:
- Around line 944-948: The current startDashboard() swallows errors by logging
and not signaling failure; change it to preserve non-blocking behavior but
surface outcome by returning a success flag: call await
this.dashboardIntegration.start() inside startDashboard(), on success return
true, and in the catch block use this._log(`Dashboard start failed
(non-blocking): ${error.message}`, { level: 'warn' }) and then return false (do
not rethrow). Update any callers expecting a Promise<void> to handle the
Promise<boolean> result from startDashboard() so they can detect failure.

---

Outside diff comments:
In @.aiox-core/core/orchestration/recovery-handler.js:
- Around line 167-169: handleEpicFailure currently omits the input validation
used elsewhere and can accept non-integer epic identifiers, creating spurious
keys in this.attempts; fix it by calling the existing
_assertValidEpicNum(epicNum) at the start of handleEpicFailure (before any
mutation or logging) so invalid values (null, "3", 1.5, etc.) are rejected
consistently with other public epic-number APIs like getAttemptCount and
canRetry.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0c2f7540-3be2-4037-8ca2-515ecc6d1227

📥 Commits

Reviewing files that changed from the base of the PR and between e2d3481 and 595deda.

📒 Files selected for processing (6)
  • .aiox-core/core/config/config-loader.js
  • .aiox-core/core/orchestration/master-orchestrator.js
  • .aiox-core/core/orchestration/recovery-handler.js
  • .aiox-core/install-manifest.yaml
  • tests/core/master-orchestrator.test.js
  • tests/core/recovery-handler.test.js
✅ Files skipped from review due to trivial changes (2)
  • tests/core/master-orchestrator.test.js
  • .aiox-core/install-manifest.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • .aiox-core/core/config/config-loader.js

Comment thread .aiox-core/core/orchestration/master-orchestrator.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.aiox-core/core/orchestration/recovery-handler.js:
- Around line 762-765: The TypeError message in _assertValidEpicNum uses
JSON.stringify(epicNum) which can itself throw for BigInt; change the thrown
message to use String(epicNum) instead (e.g., replace JSON.stringify(epicNum)
with String(epicNum)) so the validation error always formats the epicNum safely
when throwing from _assertValidEpicNum.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 476ce756-51c2-4f5b-a4dd-1c18c5129b06

📥 Commits

Reviewing files that changed from the base of the PR and between 595deda and 665db70.

📒 Files selected for processing (3)
  • .aiox-core/core/orchestration/recovery-handler.js
  • .aiox-core/install-manifest.yaml
  • tests/core/recovery-handler.test.js
✅ Files skipped from review due to trivial changes (1)
  • .aiox-core/install-manifest.yaml

Comment thread .aiox-core/core/orchestration/recovery-handler.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.aiox-core/core/orchestration/recovery-handler.js:
- Around line 693-707: The JSDoc for getAttemptHistory incorrectly states keys
are numbers; JavaScript object keys are strings, so update the JSDoc for
getAttemptHistory to reflect that it returns an Object<string, Array> (or add a
note that epic numbers are stringified), and ensure callers know to access
entries with string keys or convert numeric indices; reference
getAttemptHistory, this.attempts and _cloneAttempt when making the doc/type
change.
- Around line 775-781: The JSON fallback in _cloneAttempt can throw or lose data
for non-serializable fields (Error, undefined, Map/Set, circular refs, BigInt);
update _cloneAttempt to wrap the JSON.parse(JSON.stringify(attempt)) fallback in
a try/catch and, on any error or detected loss, return a safe sanitized clone
instead: create a shallow copy of attempt but replace the context with a
safeRepresentation (if context is an Error, replace with {name,message,stack};
if Map/Set convert to arrays; if BigInt convert to string; preserve
undefined-valued keys explicitly; and for circulars/other failures fall back to
stringified summary), ensuring _cloneAttempt never throws and getAttemptHistory
callers receive a stable, non-throwing data-only object.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1315e4bf-22b6-43ae-9612-daa639400ffd

📥 Commits

Reviewing files that changed from the base of the PR and between 665db70 and fbb34e4.

📒 Files selected for processing (3)
  • .aiox-core/core/orchestration/recovery-handler.js
  • .aiox-core/install-manifest.yaml
  • tests/core/recovery-handler.test.js
✅ Files skipped from review due to trivial changes (1)
  • .aiox-core/install-manifest.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/core/recovery-handler.test.js

Comment thread .aiox-core/core/orchestration/recovery-handler.js
Comment thread .aiox-core/core/orchestration/recovery-handler.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (4)
.aiox-core/core/orchestration/recovery-handler.js (4)

723-737: 💤 Low value

canRetry validation is transitive — fine, but worth a one-liner note.

canRetry doesn’t call _assertValidEpicNum directly; it relies on getAttemptCount to throw. That’s correct today, but it means anyone refactoring getAttemptCount (e.g., to return 0 for unknown epics without validating) could silently weaken canRetry’s contract. A direct this._assertValidEpicNum(epicNum) call here would make the throw contract documented in the JSDoc self-evident from the body.

♻️ Optional defensive tweak
 canRetry(epicNum) {
+  this._assertValidEpicNum(epicNum);
   return this.getAttemptCount(epicNum) < this.maxRetries;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.aiox-core/core/orchestration/recovery-handler.js around lines 723 - 737,
canRetry currently relies on getAttemptCount to validate epicNum transitively;
add an explicit validation call to make the throw contract self-evident and
robust by invoking this._assertValidEpicNum(epicNum) at the start of canRetry
(before calling this.getAttemptCount(epicNum)) so invalid epic numbers are
rejected here even if getAttemptCount is later changed.

799-840: ⚖️ Poor tradeoff

_requiresSanitizedClone traversal looks correct; one observation about reuse.

The traversal correctly handles circular refs via a per-call WeakSet and the add/process/delete pattern means siblings don’t false-positive each other. One thing to note: when a non-JSON-safe value is detected, you’ll then call _sanitizeValue which independently re-walks the same structure with its own WeakSet. For attempt records (typically small) this is fine, but if context ever grows large, the double-walk could become a hot spot. Not actionable now — flagging for future awareness if attempts/context ever balloon.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.aiox-core/core/orchestration/recovery-handler.js around lines 799 - 840,
The traversal in _requiresSanitizedClone is correct but causes a duplicate walk
because _sanitizeValue re-traverses the same object with its own WeakSet; to
avoid double work for large contexts, add an optional seen/visited WeakSet
parameter to _sanitizeValue (and any helper it calls) so you can pass the same
WeakSet returned or used by _requiresSanitizedClone instead of creating a fresh
one, update calls to _sanitizeValue to pass the existing seen set (falling back
to new WeakSet() when not provided), and ensure both methods continue to
add/remove or otherwise manage the shared set consistently to preserve
circular-detection semantics.

692-708: 💤 Low value

Behavior change worth surfacing in the JSDoc: deep clone vs. prior shallow copy.

The previous return shape exposed live attempt object references; this version returns deep clones, so callers that previously mutated entries to drive internal state (or relied on reference equality) will silently see different behavior. The JSDoc mentions stringified keys but not the reference-equality break. A short note would help downstream consumers audit their usage.

📝 Suggested doc tweak
   * Gets attempt history for all epics.
   *
   * Returns cloned attempt arrays to prevent callers from mutating internal
   * recovery state. Epic number keys are stringified because JavaScript object
-  * keys are strings.
+  * keys are strings. Note: returned attempt records are deep clones, so
+  * reference equality with internal state is intentionally not preserved.
   *
   * `@returns` {Object<string, Array>} Map of stringified epic numbers to attempt records.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.aiox-core/core/orchestration/recovery-handler.js around lines 692 - 708,
Update the JSDoc for getAttemptHistory to explicitly state that it now returns
deep-cloned attempt objects (via this._cloneAttempt) rather than the previous
live references/shallow copies, and call out that callers who mutated returned
attempts or relied on reference equality must update their usage; reference the
getAttemptHistory method and the _cloneAttempt helper so reviewers can locate
the change.

779-797: ⚡ Quick win

Output shape of getAttemptHistory() is runtime-dependent — consider documenting or normalizing.

_cloneAttempt returns three structurally different shapes depending on the input and runtime:

  • structuredClone path (Node ≥ 17, JSON-incompatible values): preserves Error, Map, Set, and BigInt instances.
  • JSON path: returns plain JSON-cloned data.
  • Sanitize path: converts Error{name, message, stack} plain object, MapArray<[k,v]>, SetArray, BigIntstring, etc.

Consumers doing attempt.context.someErr instanceof Error or attempt.context.metrics instanceof Map will see the check pass under structuredClone but fail under the sanitize fallback. Since this code path is only exercised when structuredClone throws or is unavailable, drift can hide in production until a non-cloneable value first appears. Two practical options:

  1. Document the divergence on getAttemptHistory / _cloneAttempt so callers know to treat the output as data-only and avoid instanceof checks.
  2. Always run the result through _sanitizeValue (or always re-shape Errors/Maps/Sets) so the output shape is deterministic regardless of Node version.

Option 1 is the lower-cost fix and is consistent with the “returns cloned attempt arrays … data-only” intent already in the doc.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.aiox-core/core/orchestration/recovery-handler.js around lines 779 - 797,
_cloneAttempt currently returns different shapes depending on runtime which
breaks consumers that expect deterministic types; fix it by normalizing output:
after the structuredClone or JSON.parse(JSON.stringify(...)) path (in
_cloneAttempt) pass the cloned result through this._sanitizeValue before
returning so Errors/Maps/Sets/BigInts are consistently converted, ensuring
getAttemptHistory always returns the same data-only shape regardless of Node
version; keep the fallback sanitize path as-is for when cloning fails.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In @.aiox-core/core/orchestration/recovery-handler.js:
- Around line 723-737: canRetry currently relies on getAttemptCount to validate
epicNum transitively; add an explicit validation call to make the throw contract
self-evident and robust by invoking this._assertValidEpicNum(epicNum) at the
start of canRetry (before calling this.getAttemptCount(epicNum)) so invalid epic
numbers are rejected here even if getAttemptCount is later changed.
- Around line 799-840: The traversal in _requiresSanitizedClone is correct but
causes a duplicate walk because _sanitizeValue re-traverses the same object with
its own WeakSet; to avoid double work for large contexts, add an optional
seen/visited WeakSet parameter to _sanitizeValue (and any helper it calls) so
you can pass the same WeakSet returned or used by _requiresSanitizedClone
instead of creating a fresh one, update calls to _sanitizeValue to pass the
existing seen set (falling back to new WeakSet() when not provided), and ensure
both methods continue to add/remove or otherwise manage the shared set
consistently to preserve circular-detection semantics.
- Around line 692-708: Update the JSDoc for getAttemptHistory to explicitly
state that it now returns deep-cloned attempt objects (via this._cloneAttempt)
rather than the previous live references/shallow copies, and call out that
callers who mutated returned attempts or relied on reference equality must
update their usage; reference the getAttemptHistory method and the _cloneAttempt
helper so reviewers can locate the change.
- Around line 779-797: _cloneAttempt currently returns different shapes
depending on runtime which breaks consumers that expect deterministic types; fix
it by normalizing output: after the structuredClone or
JSON.parse(JSON.stringify(...)) path (in _cloneAttempt) pass the cloned result
through this._sanitizeValue before returning so Errors/Maps/Sets/BigInts are
consistently converted, ensuring getAttemptHistory always returns the same
data-only shape regardless of Node version; keep the fallback sanitize path
as-is for when cloning fails.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 484905ba-74b4-403c-bf8d-4fa49b330fea

📥 Commits

Reviewing files that changed from the base of the PR and between fbb34e4 and f36afc2.

📒 Files selected for processing (3)
  • .aiox-core/core/orchestration/recovery-handler.js
  • .aiox-core/install-manifest.yaml
  • tests/core/recovery-handler.test.js
✅ Files skipped from review due to trivial changes (1)
  • .aiox-core/install-manifest.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/core/recovery-handler.test.js

@rafaelscosta

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.aiox-core/core/orchestration/recovery-handler.js:
- Around line 819-825: The current _sanitizeValue function strips Error
instances to only {name, message, stack}; update _sanitizeValue to preserve
enumerable own-properties (e.g., code, errno, syscall, path, status) by copying
them onto the sanitized object and also detect and recursively sanitize an
error.cause (using the same _sanitizeValue) so nested causes are preserved;
ensure you only copy own-properties (Object.getOwnPropertyNames/Object.keys) and
avoid circular references (track seen objects) so getAttemptHistory() returns
richer error diagnostics without throwing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1db81fa3-0a2a-43dc-9284-f6269dd430ba

📥 Commits

Reviewing files that changed from the base of the PR and between fbb34e4 and 485f502.

📒 Files selected for processing (3)
  • .aiox-core/core/orchestration/recovery-handler.js
  • .aiox-core/install-manifest.yaml
  • tests/core/recovery-handler.test.js
✅ Files skipped from review due to trivial changes (1)
  • .aiox-core/install-manifest.yaml

Comment thread .aiox-core/core/orchestration/recovery-handler.js
@rafaelscosta

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

…verage-20260507

# Conflicts:
#	.aiox-core/install-manifest.yaml
@rafaelscosta

Copy link
Copy Markdown
Collaborator Author

@coderabbitai resolve

@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown
✅ Actions performed

Comments resolved and changes approved.

@rafaelscosta

Copy link
Copy Markdown
Collaborator Author

Status de triagem em 2026-05-07: checks requeridos verdes e CodeRabbit aprovado. O PR está tecnicamente mergeable, mas segue bloqueado por CODEOWNER/human review (REVIEW_REQUIRED) de @Pedrovaleriolopez ou @oalanicolas. Não vou fazer admin bypass; aguardando aprovação humana para merge.

@rafaelscosta
rafaelscosta merged commit e161b9b into main May 7, 2026
42 checks passed
@rafaelscosta
rafaelscosta deleted the issue-89-docstring-coverage-20260507 branch May 7, 2026 23:12
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

Development

Successfully merging this pull request may close these issues.

chore: increase docstring coverage to 80%

1 participant