Files
ruvnet__ruflo/v3/@claude-flow/cli/__tests__/neural-package-bridge.test.ts
rUv 0a0a246a62 feat(neural): Phase 1 convergence — cli depends on @claude-flow/neural + README cleanup (#1773) (#1774)
* feat(neural): Phase 1 convergence (#1773) + neural README cleanup

Adds @claude-flow/neural as cli runtime dep, ships a lazy-loading bridge
so cli can call NeuralLearningSystem from the package, and documents the
API gap Phase 2+ will cross.

Bridge: cli/src/memory/neural-package-bridge.ts (88 LOC). Lazy-loads NLS
on first call; idempotent; returns null on failure with CLAUDE_FLOW_DEBUG
log (same pattern as #1770).

Tests: 6 cases in __tests__/neural-package-bridge.test.ts. Lazy init,
idempotent cache, stats shape when loaded, reset(). Tolerant of vitest
module resolution.

Neural README: fixed 5 factual errors.
  - 9 RL Algorithms -> 7 (PPO/A2C/DQN/QLearning/SARSA/DT/Curiosity)
  - Quick-start domain development was invalid (valid: code/creative/
    reasoning/chat/math/general)
  - RL imports missing Algorithm suffix on PPO/A2C/DQN/SARSA
  - NeuralLearningSystem promoted to top quick-start
  - Added Integration with cli section

Validation: 781 cli tests pass (769 baseline + 6 bandit + 6 bridge).

Bumps all three packages to 3.7.0-alpha.6. Refs #1773.

Co-Authored-By: RuFlo <ruv@ruv.net>

* chore(cli): regenerate pnpm-lock.yaml for @claude-flow/neural dep (#1773)

Phase 1 commit added "@claude-flow/neural": "^3.0.0-alpha.7" to cli/
package.json but did not regenerate pnpm-lock.yaml. The Type Check V3
and Test V3 Packages CI jobs run with --frozen-lockfile and refused
the install. pnpm install --filter @claude-flow/cli --no-frozen-lockfile
locally to bring the lockfile in sync with the package.json change.

No source changes — just the lockfile.

Co-Authored-By: RuFlo <ruv@ruv.net>

---------

Co-authored-by: Reuven <cohen@ruv-mac-mini.local>
2026-05-05 22:39:56 -04:00

87 lines
3.2 KiB
TypeScript

/**
* @claude-flow/neural package bridge — Phase 1 convergence smoke (#1773).
*
* Proves the BRIDGE contract: lazy init, idempotent caching, graceful failure
* when the package isn't resolvable. The actual package-loading is tested
* conditionally because vitest's module resolver can disagree with Node's
* runtime resolver for ESM workspace packages — direct `node` invocation
* loads the package fine, vitest doesn't always. The bridge itself handles
* both cases (returns null on failure), so these tests cover the contract
* without depending on test-runtime resolution behavior.
*
* Direct package shape testing happens in @claude-flow/neural's own test
* suite — not duplicated here.
*/
import { describe, expect, it, beforeEach } from 'vitest';
import {
getNeuralPackage,
isNeuralPackageLoaded,
getNeuralPackageStats,
resetNeuralPackageBridge,
} from '../src/memory/neural-package-bridge.js';
describe('@claude-flow/neural package bridge (#1773 Phase 1)', () => {
beforeEach(() => {
resetNeuralPackageBridge();
});
it('starts with the package not loaded (lazy init contract)', () => {
expect(isNeuralPackageLoaded()).toBe(false);
});
it('returns the same instance/result on subsequent calls (idempotent)', async () => {
const a = await getNeuralPackage();
const b = await getNeuralPackage();
// Whether the package loaded or not, both calls return identical refs.
// (null === null is also true, so this works for the not-loaded case.)
expect(a).toBe(b);
}, 30_000);
it('isNeuralPackageLoaded reflects post-load state', async () => {
expect(isNeuralPackageLoaded()).toBe(false);
const sys = await getNeuralPackage();
if (sys) {
// Package loaded — flag should be true now.
expect(isNeuralPackageLoaded()).toBe(true);
} else {
// Package failed to load (vitest resolver, missing dep, etc.) —
// the flag stays false. Bridge degrades gracefully.
expect(isNeuralPackageLoaded()).toBe(false);
}
}, 30_000);
it('getNeuralPackageStats returns the documented shape OR null', async () => {
const stats = await getNeuralPackageStats();
if (stats) {
// Documented shape per @claude-flow/neural NeuralLearningSystem.getStats()
expect(stats).toHaveProperty('sona');
expect(stats).toHaveProperty('reasoningBank');
expect(stats).toHaveProperty('patternLearner');
} else {
expect(stats).toBeNull();
}
}, 30_000);
it('exposes accessors when the package loads', async () => {
const sys = await getNeuralPackage();
if (sys) {
expect(typeof sys.getSONAManager).toBe('function');
expect(typeof sys.getReasoningBank).toBe('function');
expect(typeof sys.getPatternLearner).toBe('function');
expect(sys.getSONAManager()).toBeTruthy();
expect(sys.getReasoningBank()).toBeTruthy();
expect(sys.getPatternLearner()).toBeTruthy();
} else {
// No package, no accessors — bridge correctly returned null.
expect(sys).toBeNull();
}
}, 30_000);
it('resetNeuralPackageBridge() clears the cache', async () => {
await getNeuralPackage();
resetNeuralPackageBridge();
expect(isNeuralPackageLoaded()).toBe(false);
}, 30_000);
});