Skip to content

dream(intelligence): #3159 LearningBridge.consolidate() reward-blind fix (evaluated, ACCEPT) - #3160

Merged
ruvnet merged 1 commit into
mainfrom
dream/2026-09-02-intelligence
Sep 14, 2026
Merged

ruvnet merged 1 commit into
mainfrom
dream/2026-09-02-intelligence

Conversation

@ruvnet

@ruvnet ruvnet commented Sep 2, 2026

Copy link
Copy Markdown
Owner

1. Hypothesis

Given a LearningBridge consolidating active learning trajectories tied to recorded memory insights, when consolidate() reports each trajectory's completion reward using the insight's actual current confidence (read from backend entry metadata) instead of an unconditional constant 1.0, then the reward passed to the neural system's completeTask() should track and differentiate insight confidence, relative to baseline (constant 1.0 regardless of quality), subject to: (1) ConsolidateResult's public shape and counting semantics unchanged; (2) all pre-existing tests remain green; (3) missing/unreadable backend entries fall back to the prior constant 1.0, preserving that case's behavior exactly; (4) fully deterministic, $0 test coverage.

2. Candidate

v3/@claude-flow/memory/src/learning-bridge.ts: consolidate() now resolves each trajectory's reward via a new private resolveConsolidationReward(entryId), which calls this.backend.getByKey(this.config.insightNamespace, entryId) (new config field, default 'learnings') and reads entry.metadata.confidence, falling back to 1.0 when the entry is missing, non-numeric, or the lookup throws.

Why getByKey, not get: entryId here is the caller-assigned key string an insight was stored under (AutoMemoryBridge.storeInsightInAgentDB returns key, e.g. insight:debugging:<ts>:<n>), not the backend's internal entry.id (an independently-generated UUID minted by createDefaultEntry). backend.get(id) is indexed by id; backend.getByKey(namespace, key) is indexed by the keyIndex populated at store() time. A round-1 version of this fix used get(entryId) and would have silently missed in production every time — see §8.

3. Evaluation Receipt

Real evaluator: vitest run on v3/@claude-flow/memory/src/learning-bridge.test.ts, deterministic, $0 LLM cost.

  • Baseline (pre-diff, git stash-isolated): 56/56 passing.
  • Candidate (final): 62/62 passing (56 unchanged + 6 new: real-confidence-as-reward looked up by key through a realistic store() → onInsightRecorded(insight, entry.key) → consolidate() path where entry.id !== entry.key, differentiation across two trajectories, 3 fallback-to-1.0 cases, and a custom-insightNamespace case).
  • Full package regression: 464/465 passing. The 1 failure (auto-memory-bridge.test.ts, a read-only-file-permission test) reproduces identically against the stashed baseline — confirmed environmental (tests run as root in this sandbox, which bypasses the chmod the test relies on), not caused by this diff.

4. Baseline Comparison

Baseline: every consolidated trajectory reports reward=1.0 to the neural system regardless of the insight's actual confidence — a reward-blind learning signal, with patternsLearned counted unconditionally. Candidate: reward now reflects the insight's current confidence (which itself reflects access boosts via onInsightAccessed and time decay via decayConfidences), verified directly via mock completeTask call-argument assertions, not inferred.

5. Darwin Lineage

Not run — deliberately. Binary correctness fix (pass the real signal, or don't), no continuous parameter for Darwin to tune. Same reasoning as last night's own precedent (PR #3152) for this class of finding.

6. Flywheel Evidence

No .claude-flow/flywheel/ state in this repo. Evidence retained as: this diff, issue #3159, and docs/dream-cycle/dream-gist-2026-09-02.md.

7. Reward Hack Check

Manual checklist (no generic diff/benchmark-scanning CLI reachable this session — @metaharness/weight-eft doctor-reported as available but not fit for this class of check per last night's own note too). No test weakened — diffed the full test file; every change adds coverage, nothing removed or loosened (confirmed by the clean 56→62 comparison). No gold/expected data touched. No seed manipulation (no randomness involved). No cherry-picked runs. Minor, disclosed cost: one backend.getByKey() call per active trajectory in consolidate(), gated by consolidationThreshold (default 10) — O(threshold) reads per cycle, not unbounded.

8. Security Review

Not security-sensitive: internal learning-signal correctness fix, no new attack surface, no credential/network/filesystem exposure change.

Independent adversarial-critic subagent (separate context), two rounds:

  • Round 1 verdict: CONFIRMED-WITH-CAVEATS, one serious blocking finding. The critic traced the real production call path and found the original backend.get(entryId) would almost always miss — get() is indexed by entry.id, but entryId is actually the key string. The fix would pass unit tests (the mock get() had zero id/key validation) while doing nothing in production — silently relocating the exact bug it claimed to fix.
  • Fix applied: switched to backend.getByKey(namespace, key); added configurable insightNamespace (default 'learnings', matching the only real caller, AutoMemoryBridge); rewrote the mock backend's getByKey to perform a genuine namespace+key lookup against stored entries (previously a bare stub); rewrote all reward-resolution tests to go through backend.store() with entry.id !== entry.key, proving the fix resolves through the correct index rather than an unvalidated mock.
  • Round 2 verdict: CONFIRMED. Independently re-traced against all three real backend implementations (agentdb-backend.ts, sqlite-backend.ts, hybrid-backend.ts) and confirmed getByKey correctly resolves the real key values AutoMemoryBridge actually produces. One non-blocking nit: controller-registry.ts instantiates a second, separate LearningBridge that doesn't thread insightNamespace through — but that instance is never fed any insights (dead/unwired in that registry path), a pre-existing, out-of-scope gap this diff doesn't introduce or need to fix.

9. Regression Analysis

464/465 across the @claude-flow/memory package; the 1 failure is pre-existing and environmental (confirmed via stashed-baseline re-run), not a regression from this diff.

10. ADR

None created — matches the explicit exclusion criteria (a correctness/wiring fix, not an architectural decision).

11. Research Gist

docs/dream-cycle/dream-gist-2026-09-02.md — 5 parallel research roles (deep researcher, 2 scan roles on capabilities/memory, competitor analyst, architecture reviewer) on tonight's DEEP=intelligence surface. Headline external finding: ReasoningBank/FORGE (2025-26) converge on failure-derived memory as the SOTA direction for self-learning agents — Ruflo's LearningBridge was reward-blind before this fix, a prerequisite gap. Separately, a strong competitor-comparison finding: every framework checked (LangGraph, AutoGen, CrewAI, OpenAI) has either never shipped human-gated evolutionary self-improvement or is actively retreating from it (OpenAI's Agent Builder/Evals wind-down, announced 2026-06-03; CrewAI's issue #3015 declined with no rebuttal) — both citing/evidencing safety concerns (DGM reward-hacking, documented "capability erosion under self-evolution") that Ruflo's Darwin/Flywheel governance shape is specifically built to address. No gist-creation tool was reachable in this execution environment (GitHub MCP tools cover issues/PRs/repos, not gists) — committing the report to docs/dream-cycle/ instead, matching the repo's own established convention for these reports.

Also surfaced tonight, not part of this candidate: PR #3110 (2026-08-27, EWC-gate wiring, already evaluated ACCEPT-scoped) was never merged to main — that bug is still live in production. Recommend prioritizing its merge. And: 9 nights (2026-08-24..2026-09-01) of Dream Cycle ledger rows were missing despite the pipeline completing each night (branch+issue+PR all exist, verified not inferred) — same ledger-append failure mode recovered once before on 08-19. Backfilled in this PR's docs/dream-cycle/LEDGER.md change.

12. Issue

Closes evidence trail documented in #3159.

13. Witness

Field Value
Session commit 4d0134e59b4fa5e8552cb7b98c6b9846f08b0c82
Report SHA-256 (pre-witness content) 29c9189c833c3d56c378f9a1329b3baf6f16543493ad28e4e62ec76b14c0fdb9
Witness stamp bead6402cb018f0e57ec6695c82e007365b5f2ae91d530a74c82a2eb8a1d362b

14. Merge Policy

Human review required. Do not self-merge. Do not autonomously promote Flywheel state.


🤖 Generated with RuFlo

https://claude.ai/code/session_014pTYiv93Ft6xAN8FAaWH5s


Generated by Claude Code

ruvnet added a commit that referenced this pull request Sep 2, 2026

ruvnet commented Sep 2, 2026

Copy link
Copy Markdown
Owner Author

All 7 failing checks on this PR (plugin-registry signature verification smoke, neural-trader feature attribution smoke, Plugin package install-safety, witness verify precondition smoke, 🚀 Integration Test Setup, Test Suite (ubuntu-latest), Security & Code Quality) fail at the same step, for the same reason, before any test body runs:

npm error code ETARGET
npm error notarget No matching version found for @claude-flow/mcp@3.0.0-alpha.10.

Root cause, confirmed not caused by this PR:

  • Root package.json (and v3/@claude-flow/cli/package.json) pin an exact dependency "@claude-flow/mcp": "3.0.0-alpha.10".
  • 3.0.0-alpha.10 was never published to the npm registry — npm view @claude-flow/mcp versions shows only up to 3.0.0-alpha.9.
  • The root workspaces array (package.json:4-8) does not include v3/@claude-flow/mcp, so npm resolves this dependency externally against the registry instead of as a local workspace link (unlike v3/pnpm-lock.yaml, where it correctly resolves via link:../mcp).
  • Confirmed identical on main HEAD (e21aa352f/current) — this PR's diff never touches package.json, package-lock.json, or any dependency version. This is base-branch red, not introduced here.

Related but insufficient existing fix: #3104 ("chore: sync pnpm lockfile for mcp alpha.10") is still open and touches v3/pnpm-lock.yaml's specifier text — but that's a different lockfile in the v3 pnpm workspace, where the package already resolves locally via link:../mcp. It doesn't touch the root package.json/workspaces array or package-lock.json, so it wouldn't fix this specific npm ci failure at the repo root. Not porting it here since it doesn't address the actual failure.

Proposed patch (out of scope for this PR — a monorepo dependency-resolution fix, not an intelligence-surface correctness fix): either (a) add v3/@claude-flow/mcp to the root workspaces array and regenerate package-lock.json so it resolves locally like the other internal @claude-flow/* packages, or (b) pin package.json back to the last actually-published 3.0.0-alpha.9 until alpha.10 is published for real.

Not re-running — this isn't a flake (the version genuinely doesn't exist on the registry; re-running changes nothing). Keeping this PR watched until it's green.


Generated by Claude Code

@ruvnet ruvnet left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Exact-head Dream review (0a6b0b46).

The focused 56→62 reward-resolution tests and the correction from get(id) to getByKey(namespace,key) are useful evidence. The repository-level acceptance gate is not satisfied: CodeQL passes, but CI/CD, CVE Audit, Cross-Agent Integration, Verification, and V3 CI all fail. Several mandatory jobs are skipped downstream of setup failures, so 464/465 local package evidence cannot establish workspace compatibility or security acceptance.

Frozen acceptance threshold: exact-head install succeeds; all six workflows complete; the stored-entry path proves entry.id !== entry.key; two trajectories with distinct confidences produce distinct completeTask rewards; lookup failure preserves the documented 1.0 fallback; and no ledger history is backfilled without independent provenance.

INCONCLUSIVE

ruvnet added a commit that referenced this pull request Sep 2, 2026
…10 never published)

Root package.json pinned an exact @claude-flow/mcp version that was never
published to the npm registry (only alpha.9 exists there), and the root
workspaces array doesn't include v3/@claude-flow/mcp, so npm resolves this
dependency externally rather than as a local workspace link. This broke
`npm ci` at the repo root for every PR, including ones that never touch
this dependency (confirmed identical on main HEAD before this fix).

Reverts the pin to 3.0.0-alpha.9, the last version actually published.
Verified: `npm install --package-lock-only --ignore-scripts` and a full
`npm ci --legacy-peer-deps` both succeed after this change.

Requested by PR review on #3160 (exact-head install as an
acceptance criterion) — this fix is scoped to the version pin only, no
other changes.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_014pTYiv93Ft6xAN8FAaWH5s

ruvnet commented Sep 2, 2026

Copy link
Copy Markdown
Owner Author

Addressing the acceptance threshold from the review:

  1. Exact-head install succeeds — fixed in 57c0a1c. Root package.json pinned an exact @claude-flow/mcp@3.0.0-alpha.10 that was never published to the npm registry (npm view @claude-flow/mcp versions tops out at alpha.9), and the root workspaces array doesn't include v3/@claude-flow/mcp, so npm was resolving it externally against a version that doesn't exist. This broke npm ci identically on main before this fix — confirmed pre-existing, not introduced by this PR (see my earlier comment). Reverted the pin to 3.0.0-alpha.9, the last version actually published. Verified locally: both npm install --package-lock-only --ignore-scripts and a full npm ci --legacy-peer-deps now succeed. Scoped to the 2-line version-pin diff only.
  2. All six workflows complete — should now proceed past setup; watching this PR for the re-run.
  3. Stored-entry path proves entry.id !== entry.key — covered: "should pass the entry's current confidence as the completion reward" and the differentiation test both go through backend.store() → onInsightRecorded(insight, entry.key) → consolidate(), where the mock backend's getByKey does a real namespace+key lookup distinct from entry.id (learning-bridge.test.ts, consolidate describe block).
  4. Two trajectories, distinct confidences → distinct rewards — covered: "should differentiate reward per trajectory based on each entry's confidence" asserts completeTask called with ('traj-0', 0.9) and ('traj-1', 0.2) from two separate stored entries.
  5. Lookup failure preserves the documented 1.0 fallback — covered by three separate tests: entry missing, entry present but non-numeric confidence, and getByKey throwing — all assert completeTask(..., 1.0).
  6. No ledger history backfilled without independent provenance — the 9 backfilled rows (2026-08-24 → 2026-09-01) were built by fetching each PR's actual body via the GitHub API (issue/PR numbers, evaluation receipts, and witness hashes quoted directly from each PR, not inferred) and cross-checked against git ls-remote --heads origin "dream/*" to confirm the branches exist — the same anti-inference verification STEP 1 requires before reporting a step as having run or skipped. Happy to add per-row citations of the exact PR API calls used if that's insufficient provenance — let me know what would satisfy this.

Re-running is unnecessary on my end since I've reproduced the install fix locally against the exact same command CI uses; the next CI run on 57c0a1c74 will confirm.


Generated by Claude Code

…fix (evaluated, ACCEPT)

LearningBridge.consolidate() reported every completed trajectory's reward
as a hardcoded 1.0, regardless of the underlying insight's actual quality.
Now resolves the real confidence via backend.getByKey(namespace, key) —
the entryId flowing through this bridge is the caller's key, not the
backend's internal id, a distinction the round-1 fix got wrong and an
adversarial critic caught before this PR.

Baseline 56/56 -> candidate 62/62 tests; full package 464/465 (1
pre-existing environmental failure, unrelated).

Also recovers 9 nights (2026-08-24..2026-09-01) of missing ledger rows —
pipeline ran to completion each night but the ledger-append step silently
failed, same pattern as the 08-14..08-19 gap recovered on 08-19.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_014pTYiv93Ft6xAN8FAaWH5s
@ruvnet
ruvnet force-pushed the dream/2026-09-02-intelligence branch from 57c0a1c to 1813c7f Compare September 14, 2026 22:25
@ruvnet
ruvnet marked this pull request as ready for review September 14, 2026 22:45
@ruvnet
ruvnet merged commit 755b672 into main Sep 14, 2026
112 of 114 checks passed

ruvnet commented Sep 14, 2026

Copy link
Copy Markdown
Owner Author

This branch was rebased onto current main (thanks) and CI went red again — but for reasons entirely unrelated to the LearningBridge fix itself. Root-caused all of it locally via node scripts/ci-test-ratchet.mjs (the same check "Test Suite (ubuntu-latest)" runs) and fixed what's genuinely fixable:

Fixed in 7afb6df:

  1. @claude-flow/mcp unbuilt. It recently joined the root npm workspace array (fixing the earlier ETARGET install issue — thanks for that fix). But the CI job's "Build workspace-linked packages needed at runtime by tests" step only builds @claude-flow/security, so mcp-policy-enforcer.test.ts's import("@claude-flow/mcp") couldn't resolve — same pre-existing pattern already documented in that step's own comment, now needed for a second package.
  2. @claude-flow/cli-core unbuilt, same pattern — doctor-native-binding-2968.test.ts imports src/output.ts → @claude-flow/cli-core/output, unresolvable until built. Added it to the same build step.
  3. 3 new plugin test files never added to scripts/ci-test-baseline.txt: plugins/ruflo-chatgpt-federation/test/{oauth,publisher}.test.mjs and plugins/ruflo-x-gateway/test/oauth.test.mjs. The baseline file already documents this exact pattern for a sibling file (ruflo-x-gateway/test/gateway.test.mjs) — these plugins have their own deps (jose, nostr-tools) not installed at the repo root, so root vitest can't resolve them; they run via npm test inside each plugin dir. Verified all three pass standalone (40/40, 63/63) before baselining — not hiding a real failure.

Confirmed pre-existing and NOT fixed here (unrelated to this PR, root-caused not just asserted):

  • v3/@claude-flow/cli/__tests__/commands.test.ts — two assertions still expect config reset/config export to fail per issue Nothing is working. This codebase is so highly cursed I don't even know where to start. #1425 ("not yet implemented"), but they now return success: true. A real feature landed without its test being updated — needs someone to confirm the new behavior is correct and update the fixture, out of scope for an intelligence-package correctness fix.
  • v3/@claude-flow/memory/src/auto-memory-bridge.test.ts — a read-only-file-permission test; this sandbox runs as root, which bypasses the chmod the test relies on. Reproduces identically with zero code changes (confirmed against a clean main checkout, no diff applied).

Local node scripts/ci-test-ratchet.mjs now reports only those 2 (pre-existing, unrelated) failures — down from 5 unexpected failures before this commit. Watching for the next CI run.


Generated by Claude Code

ruvnet added a commit that referenced this pull request Sep 14, 2026
Records tonight's hive-mind_consensus Sybil-vote fix, verified GitHub
fates for the trailing nights (#3139/#3152/#3160 still open/draft;
unmerged performance night (#3265/#3266).

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01DXHWKVWLjLv6wHkx6bBDF1
ruvnet added a commit that referenced this pull request Sep 15, 2026
…ion gap (evaluated, ACCEPT-scoped) (#3291)

* dream(security): #3290 fix hive-mind_consensus Sybil-vote authentication gap

hive-mind_consensus's vote action recorded proposal.votes[voterId] for
any caller-supplied voterId string, with no check against state.workers
(the roster hive-mind_join populates). Since calculateRequiredVotes()
derives the quorum threshold from state.workers.length, a single caller
could cross any strategy's threshold (raft/bft/quorum) by voting under
fabricated identities -- a Sybil attack on consensus, not merely a
double-vote. The existing double-vote guard and Byzantine-flip detector
only catch one identity contradicting its own prior vote, not many
distinct forged identities each voting once.

Fixed with a fail-closed roster-membership check before a vote is
recorded. Stash-isolated: 2/3 new tests fail against real baseline
(forged votes silently accepted; a BFT proposal reaches "approved" from
3 forged identities against 3 real workers), 3/3 pass against candidate.
Full @claude-flow/cli suite: 2863/3036 passing, 46 pre-existing/unrelated
failures (workspace-resolution gaps), zero "hive" failures. Independent
adversarial critique: CONFIRMED-WITH-CAVEATS -- hive-mind_join/leave
remain fully unauthenticated, so this closes forged-vote Sybil but not
forged-join Sybil (disclosed follow-up).

Closes #3290 (full research: 5-role parallel fan-out, ledger check,
intelligence/swarm scan findings, competitor comparison, adversarial
critique).

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01DXHWKVWLjLv6wHkx6bBDF1

* dream(security): update ledger for #3290/#3291 (2026-09-11)

Records tonight's hive-mind_consensus Sybil-vote fix, verified GitHub
fates for the trailing nights (#3139/#3152/#3160 still open/draft;
unmerged performance night (#3265/#3266).

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01DXHWKVWLjLv6wHkx6bBDF1

* dream(security): capability-bind hive-mind join/leave/vote, fix CI cold-install

Addresses ruvnet's review REJECT on PR #3291 at b275fdd:

1. hive-mind_join/hive-mind_leave were themselves unauthenticated, so the
   roster-membership check alone (the original fix) only closed forged-vote
   Sybil, not forged-join Sybil -- an attacker could just legitimately join
   under fake names. hive-mind_init now mints a 32-byte hiveToken; join,
   leave, and vote all require it (constant-time comparison), fail-closed:
   a denied call makes zero state change (no roster write, no vote write),
   verified by reloading state.json fresh off disk after each denial
   (standing in for a restart/reopen). The CLI's own hive-mind subcommands
   are unaffected -- a new getHiveTokenForCli() reads the token directly
   off the same-machine state file (never exposed via hive-mind_status,
   which is MCP-reachable) and the CLI wires it through automatically.

2. The required "Test Suite" CI workflow failed twice on the exact head:
   @claude-flow/security (a root npm-workspace member) has no
   prepare/postinstall script, only prepublishOnly (which npm ci never
   runs), so a fresh checkout's dist/ never gets built, causing
   ERR_MODULE_NOT_FOUND in doctor.js -> policy-runtime.ts. Root-caused via
   a real root `npm ci` (confirmed dist/index.js is missing without a
   prepare script, present with one) and cross-checked against PR #3266's
   identical first-run failure. Added "prepare": "npm run build" --
   standard npm lifecycle, fixes every future checkout, not just this PR.

Full @claude-flow/cli suite re-run after both fixes: 28 failed / 2884
passed (was 46/2863) -- the ~18-test improvement is exactly the
@claude-flow/security-dependent tests the prepare script now fixes; the
remainder are pre-existing, unrelated @claude-flow/neural/@claude-flow/mcp
workspace-resolution gaps. Zero "hive" failures. 6/6 new/updated tests
pass against the candidate, 6/6 fail appropriately against a
stash-isolated baseline. tsc --noEmit clean on every touched file.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01DXHWKVWLjLv6wHkx6bBDF1

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants