Skip to content

dream(intelligence): #3878 EWC penalty is a silent no-op in all 3 modes, even after pending #3622 lands (evaluated, ACCEPT) - #3879

Draft
ruvnet wants to merge 8 commits into
mainfrom
dream/2026-10-07-intelligence
Draft

ruvnet wants to merge 8 commits into
mainfrom
dream/2026-10-07-intelligence

Conversation

@ruvnet

@ruvnet ruvnet commented Oct 7, 2026

Copy link
Copy Markdown
Owner

1. Hypothesis

Given SONA's three EWC-consuming learning modes (balanced, research, batch), when ewcState.fisher/means are populated under LoRA-weight-space keys (${domain}:${module}) exactly as the pending, unmerged dream/2026-10-02-intelligence commit (#3622) would populate them, then each mode's own EWC penalty/loss computation (computeEWCPenalty in balanced.ts, computeEWCLoss in research.ts, the inline check in batch.ts's applyAccumulatedGradients) should be measurably shown to register zero lookup hits and a non-zero miss count — because each mode's own internal gradient/moment accumulator uses a different key namespace and dimensionality than the LoRA-weight-space fisher/means map — subject to: (1) no change to existing learn()/applyAdaptations() behavior or latency budget; (2) new instrumentation purely additive/observational; (3) all existing tests remain green; (4) deterministic, $0, zero LLM calls. A control case (hand-aligned key) must register a hit, proving the instrumentation isn't dead code.

Frozen before evaluation; not modified after.

2. Candidate

v3/@claude-flow/neural/src/modes/{balanced,research,batch}.ts (+~60 lines across 3 files, purely additive): adds ewcPenaltyLookupHits/ewcPenaltyLookupMisses private counters at each mode's existing ewcState.fisher/means lookup site, exposed via each mode's existing getStats(): Record<string, number> pattern. batch.ts also gets a disclosed-not-fixed inline comment: its EWC loop lacks the Math.min(...) bound the other two modes have, a latent NaN-injection landmine for whoever aligns the key spaces later (no observable effect today — the gradient is discarded via fill(0) immediately after regardless).

v3/@claude-flow/neural/__tests__/ewc-penalty-dead-path.test.ts (new, 4 tests, imported from built dist/modes/*.js — documented pre-existing circular-import quirk importing modes/*.ts directly under vitest/vite-node, independently reproduced, not introduced here).

Root cause (independently discovered, corrects this session's own architecture-review subagent, which had recommended simply cherry-picking #3622): that commit populates fisher/means under ${domain}:${module} keys sized to the LoRA rank, but balanced.ts looks up gradientAccumulator (keyed 'positive'/'negative', state-embedding-dimensioned — and its ewcPenalty result feeds a totalGradientNorm that's never read again, dead regardless), research.ts looks up adamM (keyed 'step_N', same dimensionality class), and batch.ts looks up accumulatedGradients (keyed by plain trajectory.domain, no module suffix). None of these three key spaces ever intersects the LoRA-weight-space keys #3622 would write.

3. Evaluation Receipt

evaluated: accepted. Real evaluator: Vitest 4.1.8, deterministic, $0, zero LLM calls.

Stash-isolated: reverting the 3 instrumented source files (keeping the new test file) → all 4 new tests fail (expected undefined to be +0 / TypeError: ... received "undefined"). Restoring the source → 4/4 pass. Full @claude-flow/neural suite: 143/143 (139 pre-existing + 4 new), 0 regressions. tsc --noEmit: clean.

4. Baseline Comparison

Baseline (pre-candidate) Candidate
balanced.ts EWC penalty lookup, realistic #3622-keyed fisher/means counters don't exist hits=0, misses>0
research.ts EWC loss lookup, same counters don't exist hits=0, misses>0
batch.ts EWC penalty lookup, same counters don't exist hits=0, misses>0
Control: hand-aligned key ('positive', matching balanced.ts's own accumulator) counters don't exist hits>0 (proves the counter is live, not hardcoded)
Full @claude-flow/neural suite 139/139 143/143 (0 regressions)
tsc --noEmit clean clean

5. Darwin Lineage

Skipped — diagnostic/observability addition, no continuous tunable parameter, no fitness gradient for Darwin's real interface (npx ruvector harness darwin <config> --execute, confirmed available) to search over. Same skip class as nearly every correctness/observability-fix night (#3110/#3160/#3184/#3221/#3243/#3266/#3302/#3330/#3378/#3385/#3395/#3754).

6. Flywheel Evidence

npx ruvector harness flywheel --help's real interface targets a signed-evidence-bundle schema not applicable to this change class (same disclosed gap as 10-01/10-02/10-05/10-06). Recorded as structured text in issue #3878 §7 (OBSERVATION/MEASUREMENT/INFERENCE/DECISION/REJECTION).

7. Reward Hack Check

No test weakened (diff is purely additive + 1 new test file, confirmed via full-repo git diff --stat); no gold data touched; no cherry-picking; no evaluator exploitation; no hardcoded outputs; no metric substitution; no seed manipulation; no cost hiding; no undocumented caching. Independently re-verified by an adversarial critic subagent with no authoring context, who re-derived the key-mismatch claim from source independently and re-ran the full stash-isolated comparison itself. Verdict: CONFIRMED-WITH-CAVEATS (caveats: scope is intentionally diagnostic-only, and the dist/ import workaround — both addressed, no corrections needed to any claim).

8. Security Review

Not security-sensitive. Adds only two private numeric counters per mode at already-existing lookup sites, exposed through each mode's already-existing getStats() pattern. No new imports/I/O/serialization/MCP surface. Blast radius confirmed via grep (not assumed) by the independent critic: BalancedMode/ResearchMode/BatchMode are constructed only inside sona-manager.ts, which never calls their getStats() — SONAManager.getStats() returns its own independently-computed shape. Zero external consumers of the two new keys.

9. Regression Analysis

Zero regressions: full @claude-flow/neural suite's passing count only grows (139→143, the 4 new tests), tsc --noEmit identical (clean both ways). No other package references these mode classes (grep-confirmed).

10. ADR

None — diagnostic/observability addition to an already-decided mechanism (EWC's existence), not a new architectural decision. Matches repo convention for this fix class.

11. Research Gist

docs/dream-cycle/dream-gist-2026-10-07.md (this branch) — full SOTA report: 5-role parallel research fan-out, competitor comparison (LangGraph/AutoGen/CrewAI/OpenAI Agents SDK/vector DBs, sourced A/B/C grades), the EWC dead-path finding, and capabilities/memory scan findings. No gh gist create available this session (GH_TOKEN invalid) — same disclosed FALLBACK as every night since 2026-09-30.

12. Issue

Closes #3878 (full 15-section report: ledger check with GitHub-verified fates for the trailing nights, research, evaluation receipt, adversarial critique, reward-hack check, security review, capabilities+memory scan findings, competitor comparison, witness).

13. Witness

Session commit:   be9c76718069e7f9592ee80e5a12aba60a2455f0
Candidate commit: a213b1e8a1205a61496d45affca75561be0dc7e6
Gist SHA-256 (pre-witness content): 73e12f90928490b9e6890f4b36a8f289b32e41232977bf0e7da70016cdae498a
Witness stamp:    eccba84db3eb0ea3bf770d7a65d1f3e75f7c57b1591b90b3393b60b996324c91

Verifier: fetch docs/dream-cycle/dream-gist-2026-10-07.md from this branch, strip the witness table's filled values back to PENDING, SHA-256 the file, concatenate with the session commit above, SHA-256 again — must equal the witness stamp.

14. Merge Policy

Human review required. Do not self-merge. Do not autonomously promote Flywheel state. This PR is opened as draft per standing Dream Cycle policy — evaluation, even a confirmed one, is not autonomous promotion.

Separately, not part of this PR's diff: do not merge PR #3622 (dream/2026-10-02-intelligence) as currently written without addressing this finding, or merge only with the gap explicitly disclosed — its "wires EWC back up" framing does not hold for the actual runtime penalty/loss value in any mode.


🤖 Generated with RuFlo

https://claude.ai/code/session_014egWR5nqV6Vs6dvFvNb4gi


Generated by Claude Code

claude and others added 7 commits October 7, 2026 06:29
…s, even after pending #3622 lands

The still-unmerged dream/2026-10-02-intelligence commit (9be1926, PR
#3622) wires SONAManager.consolidateEWC() into triggerLearning() and
populates ewcState.fisher/means under `${domain}:${module}` keys sized to
the LoRA rank. Independently verified that commit is NOT an ancestor of
main (git merge-base --is-ancestor fails) -- ewcState.fisher/means have
zero .set() writers anywhere in src/ today.

More importantly: even once #3622 lands exactly as written, EWC
regularization would still never activate in any of the 3 modes that
reference ewcState.fisher/means, because each mode looks up a different,
incompatibly-keyed, incompatibly-dimensioned private accumulator:

- balanced.ts computeEWCPenalty(): gradientAccumulator keyed only
  'positive'/'negative', dimensioned to the state-embedding space. Never
  matches a `domain:module` key. The computed ewcPenalty also feeds a
  local totalGradientNorm that is never read again -- dead regardless.
- research.ts computeEWCLoss(): adamM keyed 'step_0', 'step_1', ...,
  same dimensionality issue. Never matches.
- batch.ts applyAccumulatedGradients(): accumulatedGradients keyed by
  plain trajectory.domain (no module suffix). Never matches, and even a
  coincidental match would read out-of-bounds (no Math.min guard here
  unlike the other two modes) and inject NaN -- flagged inline, not fixed
  (no observable effect today; gradient is discarded via fill(0) right
  after regardless).

#3622's own "143/143 passing" claim is true but only asserts fisher/means
populate -- it never asserts the penalty becomes non-zero in a real
learn() path, so this gap passed review undetected.

Adds ewcPenaltyLookupHits/ewcPenaltyLookupMisses counters at each mode's
lookup site (exposed via each mode's existing getStats() pattern) so this
condition is observable instead of a silent always-zero no-op, plus 4
discriminating tests: 3 reproduce the always-0-hits condition under
#3622's exact key scheme, 1 control proves a hand-aligned key DOES
register a hit (counters are live, not dead code).

Stash-isolated: reverting the 3 source files (keeping the test file)
makes all 4 new tests fail (counters don't exist); restoring passes.
143/143 full @claude-flow/neural suite (139 pre-existing + 4 new), 0
regressions. tsc --noEmit clean. Independent adversarial critique:
CONFIRMED-WITH-CAVEATS (no corrections needed; caveats are about scope
being diagnostic-only by design, not a flaw).

Recommendation: do not merge #3622 as currently written without
addressing this, or merge only with the gap disclosed.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_014egWR5nqV6Vs6dvFvNb4gi
docs/dream-cycle/dream-gist-2026-10-07.md -- full SOTA report (ReasoningBank
failure-signal gap vs ICLR-2026 source, competitor comparison, the EWC
dead-path finding, capabilities/memory scan findings, witness).

No `gh gist create` available this session (GH_TOKEN invalid for gh CLI) --
same disclosed FALLBACK as every dream-cycle night since 2026-09-30.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_014egWR5nqV6Vs6dvFvNb4gi
…-24..10-06 note

Appends tonight's row and a backfill note for 09-24 through 10-06 (ledger
rows only reach main via PR merge, so these weren't individually
transcribed live). Confirms the 09-26 merge of #3395/#3412 and the
09-23/09-25/09-26/10-04 no-run gaps via git ls-remote + pull_request_read,
not inferred.

Also flags a process-risk finding: reading PR titles alone made the
EWC/Fisher subsystem look sequentially "fixed" across 09-22/09-27/10-02 --
git merge-base --is-ancestor shows only 09-22's fix (#3395) actually
reached main. Future entries should verify main-ancestry, not just PR
existence/title.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_014egWR5nqV6Vs6dvFvNb4gi
audit-leaf-package-publish.mjs (CI "Plugin package install-safety" check)
correctly failed: @claude-flow/neural is a non-bundled leaf that resolves
from the registry, not the release train, so a source change in it
(balanced.ts/batch.ts/research.ts, this PR) must come with a version
bump or the fix ships to nobody. Bumped neural 3.0.0 -> 3.0.1 (patch:
additive observability counters, no API/schema change) and widened the
CLI's exact pin on @claude-flow/neural to match in both the root umbrella
and v3/@claude-flow/cli, per this repo's exact-pin convention (#3414).
Regenerated v3/pnpm-lock.yaml via `pnpm install --lockfile-only` (repo
tooling, not by hand) -- one line changed, matching the version bump.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_014egWR5nqV6Vs6dvFvNb4gi
The prior commit's exact pin "@claude-flow/neural": "3.0.1" doesn't exist
on the real npm registry yet (only 3.0.0 is published), and unlike
v3/@claude-flow/cli/package.json's pin (resolved via pnpm workspace
linking, always points at local source regardless of specifier), the
root package.json's @claude-flow/neural dependency resolves through a
plain `npm install` against the real registry -- so the exact unpublished
pin broke "Audit root (critical-blocking)"'s
`npm install --package-lock-only --ignore-scripts` step outright
(ETARGET: No matching version found for @claude-flow/neural@3.0.1).

Widened to "^3.0.0": resolves to the real published 3.0.0 today (install
succeeds), while still satisfying audit-wrapper-dep-ranges.mjs's
semver.satisfies(workspaceVersion, range) check against the local
workspace's 3.0.1 (that check requires coverage, not exact equality --
confirmed by reading the script, same as audit-leaf-package-publish.mjs's
own check on v3/@claude-flow/cli/package.json's pin). Re-ran all three
related audit scripts (wrapper-dep-ranges, leaf-package-publish,
umbrella-version-lockstep) locally: all green.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_014egWR5nqV6Vs6dvFvNb4gi
"Test Suite (ubuntu-latest)" failed on the current head with
"Cannot find module '../dist/modes/balanced.js' imported from
v3/@claude-flow/neural/__tests__/ewc-penalty-dead-path.test.ts" --
scripts/ci-test-ratchet.mjs runs vitest from repo root without ever
building @claude-flow/neural first (only @claude-flow/security and
@claude-flow/cli-core get that treatment, for their own documented
reasons), and dist/ is gitignored, so a fresh CI checkout never has it.

This test imports mode classes via a relative path to its own package's
dist/ (not through node_modules, so no overlay/copy step like
cli-core's is needed) -- a workaround for a pre-existing vitest/vite-node
SSR-transform crash ("Class extends value undefined") when importing
modes/*.ts directly, independently reproduced and documented in the test
file's own header comment.

Added a build step mirroring the existing security/cli-core precedent.
Verified by simulating the exact CI scenario locally: removed dist/ AND
the gitignored tsconfig.tsbuildinfo (a stale local tsbuildinfo silently
no-ops tsc's emit even with dist/ deleted -- CI never has this file, so
this was purely a local-repro gotcha, not a second real bug), ran the
new build step fresh, confirmed dist/modes/ is produced and the full
@claude-flow/neural suite passes (143/143).

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_014egWR5nqV6Vs6dvFvNb4gi
…ll smoke)

"CLI npx-install smoke (#1147 / (22)/(24))" failed: the prior commit's
exact pin "@claude-flow/neural": "3.0.1" doesn't exist on the real npm
registry (only 3.0.0 is published), so a real `npm install` of the
packed CLI tarball -- exactly what an actual consumer does -- failed
outright with the same ETARGET as the earlier root-package.json and
Audit-root failures. `pnpm pack` rewrites `workspace:*` deps to resolved
versions but leaves a literal version string untouched, so this exact
pin shipped straight into the tarball unlike the (also-fixed) root
package.json case.

Same fix as the root package.json commit: widen to "^3.0.0" so a real
`npm install` of the standalone tarball resolves to the actually-
published 3.0.0, while still satisfying audit-leaf-package-publish.mjs's
and audit-wrapper-dep-ranges.mjs's semver.satisfies(workspaceVersion,
range) checks against the local workspace's 3.0.1 (coverage, not exact
equality -- confirmed by reading both scripts). Regenerated
v3/pnpm-lock.yaml via `pnpm install --lockfile-only`. Verified by
running the actual scripts/smoke-cli-npx-install.mjs locally end-to-end
(pack -> npm install -> cli --version): passes clean.

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

ruvnet commented Oct 8, 2026

Copy link
Copy Markdown
Owner Author

Status for 3.56.0: not included. This is a draft with merge conflicts, and it depends on #3622 (also unmerged); it only adds counters and a test that demonstrate the EWC penalty is not reached, it does not fix it. Leaving it open for the EWC decision.

…lligence

# Conflicts:
#	package-lock.json
#	package.json
#	v3/@claude-flow/cli/package.json

ruvnet commented Oct 8, 2026

Copy link
Copy Markdown
Owner Author

CI: "Audit v3 (critical-blocking)" failure is not this PR's — root-caused, not a flake.

npm audit --audit-level=critical in v3/ now reports 1 critical advisory (handlebars 4.0.0–4.7.9, GHSA-xw65-4hp5-5hc7 / GHSA-8r5x-fm3f-whwj / GHSA-p8wg-vrv2-v86f — JS injection via unsafe precompiled-template embedding / AST type confusion / own-property-check bypass). This PR's diff never touches handlebars or anything in its dependency chain.

Confirmed the diff isn't the cause: v3/package-lock.json on this branch is byte-identical to main's (diff against origin/main:v3/package-lock.json → 0 lines). main's own last "Audit v3" run (head 60de63863, 2026-10-08T19:40Z) passed against that exact same file. Re-running npm audit --audit-level=critical locally against that identical lockfile right now reproduces the same 1-critical finding — the GitHub Advisory Database added this advisory for handlebars sometime in the ~1h49m between main's last audit run and this PR's, with zero lockfile change on either side. main would fail the identical check if re-audited right now.

No fix exists within this PR's scope: handlebars is a deep transitive dependency (not something this diff's 3 instrumented files, 1 test file, or 2 version-pin bumps touch or could reasonably patch), and npm audit fix's suggested remediation would mean regenerating the root lockfile across the whole dependency tree — out of proportion for a scoped diagnostic PR and better handled as its own security surface fix (same class as every other transitive-CVE-in-devDependency finding this repo's nightly audits have logged). Re-running this one job now in case the advisory was since corrected/pulled; will keep watching either way.


🤖 Generated with RuFlo

https://claude.ai/code/session_014egWR5nqV6Vs6dvFvNb4gi


Generated by Claude Code

@ruvnet

ruvnet commented Oct 9, 2026

Copy link
Copy Markdown
Owner Author

Backlog sweep 2026-10-09: one consolidated plan for the EWC cluster (#3621/#3622, #3878/#3879), checked against current main (5f709e3). Nothing here is implemented; this is the plan to review before either PR moves.

Verified on main

  • SONAManager.consolidateEWC() (sona-manager.ts:566) has zero callers, and nothing writes ewcState.fisher or ewcState.means, so every mode iterates an empty map.
  • Even with the maps populated, the penalty cannot reach the output: balanced.ts:156-157 adds ewcPenalty to a local totalGradientNorm that is never read (learn() returns only improvementDelta); research.ts:408 lets it into a batch loss, but no mode writes a step back into the LoRA weights; batch.ts:386-392 does gradient[i] -= λ·F·(g−μ) and then gradient.fill(0).
  • Each mode looks the key up in a private accumulator (gradientAccumulator keyed positive/negative, adamM keyed step_N, accumulatedGradients keyed by domain), none of which can match a domain:module Fisher key. batch.ts also has no Math.min length bound, so a shorter array reads undefined and yields NaN.
  • batch.ts compares a gradient to a parameter mean. EWC needs the weight deviation (θ − θ*), not (g − μ).

Plan (one PR, supersedes #3622 and #3879; neither should merge alone)

  1. Decide the premise first (needs rUv): LoRA adapters are currently frozen after init. If SONA learning is not meant to update weights, EWC has nothing to protect and the right change is to document/remove it. If it is, continue.
  2. One parameter space. θ = the flattened LoRA A‖B vector per ${domain}:${module}. Put the key builder and a single ewcPenaltyGrad(theta, key, state, lambda) in modes/base.ts and call it from all three modes, so key and dimension alignment live in one place. Bound every loop with Math.min(fisher.length, means.length, theta.length) and skip (do not read past the end) on a mismatch; guard with Number.isFinite.
  3. The penalty must change the learned output. Each learning step becomes θ ← θ − η·(g + λ·F ⊙ (θ − θ*)), written back to the module's LoRA weights. The scalar ½λΣF(θ−θ*)² is for reporting only. Remove the dead totalGradientNorm += ewcPenalty and the fill(0) discard.
  4. Populate state in consolidateEWC(), and call it from triggerLearning() after each task: θ* ← θ, F ← decay·F + (1−decay)·g² (clamped at minFisher), taskCount++. Today it only multiplies by decay.
  5. Tests that would have caught this: a differential test through the real learn() path in each of the three modes, same trajectories with ewcLambda = 0 versus a large lambda after one consolidated task, asserting ‖θ − θ*‖ is strictly smaller with EWC; an assertion that the key-miss count is 0; a shorter-array case in batch that must not produce NaN. The existing tests only assert that the maps get populated.
  6. Release: this is a @claude-flow/neural change, an unbundled leaf, so it needs a standalone neural publish plus a cli pin bump. Both PRs also bump package.json/lockfile versions and are DIRTY; rebuild the work on a fresh branch from main rather than rebasing them.

Separate: #3475/#3476 (CLAUDE_FLOW_FISHER_DECAY) targets the other EWC implementation, cli/src/memory/ewc-consolidation.ts (fisherDecayRate), which is independent of this plan. It is still valid, needs a rebase, and adds a new env var (minor bump plus the audit-env-var-precedence entry).

This branch has not been deployed

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

Labels

None yet

Projects

None yet

2 participants