Skip to content

dream(intelligence): #3621 wire SONAManager.consolidateEWC() into triggerLearning() (evaluated, ACCEPT) - #3622

Draft
ruvnet wants to merge 5 commits into
mainfrom
dream/2026-10-02-intelligence
Draft

ruvnet wants to merge 5 commits into
mainfrom
dream/2026-10-02-intelligence

Conversation

@ruvnet

@ruvnet ruvnet commented Oct 2, 2026

Copy link
Copy Markdown
Owner

1. Hypothesis

Given SONAManager's continual-learning loop (trajectories → triggerLearning() → mode-specific learn()), when consolidateEWC() is wired into triggerLearning() and populates ewcState.fisher/means from the live LoRA adapter weights using the same squared-magnitude heuristic already disclosed in memory/ewc-consolidation.ts, then (a) ewcState.taskCount and fisher/means should visibly evolve across learning cycles where they were previously frozen at their initialized empty/zero state forever, and (b) a second consolidation should blend decayed old importance with fresh importance rather than overwriting or no-op'ing; subject to: no change to applyAdaptations()'s <0.05ms latency budget, no change to LoRA weight semantics/types, and explicit disclosure that loraWeights.A/.B are not yet updated by any learn() call outside of this test's direct simulation of that event.

Frozen before evaluation began; not modified afterward.

2. Candidate

v3/@claude-flow/neural/src/sona-manager.ts, +~70/-7 lines, one file, one conceptual change:

  • triggerLearning() now calls this.consolidateEWC() after each learning cycle (previously: never called anywhere in the repo).
  • consolidateEWC(domain) now blends fresh importance + means from the live loraWeights.A for that domain (squared-magnitude heuristic, matching memory/ewc-consolidation.ts's already-disclosed proxy) before applying its existing decay step — previously it only ever decayed a map nothing populated, so the loop body ran zero times, forever.
  • New getEWCState() read-only accessor for observability/tests.

Root cause: every mode's private computeEWCPenalty() (e.g. modes/balanced.ts:282) reads ewcState.fisher/means every learning cycle; both were permanently empty Maps, so the EWC regularization term was always exactly 0 regardless of ewcLambda (2000-2500).

Disclosed, explicitly out of scope tonight: no mode's learn() writes its computed gradient back into loraWeights.A/.B — the LoRA adapters are frozen after initializeLoRAWeights(). This fix makes EWC real for whenever weights do move (simulated directly in the new tests); it does not wire the move itself (see Recommended Next Steps #1 in the issue).

New test file: v3/@claude-flow/neural/__tests__/sona-ewc-consolidation.test.ts (4 tests). Imports the built dist/sona-manager.js, not src/ — importing any modes/*.ts file directly crashes this package's vitest/vite-node transform with Class extends value undefined (RealTimeMode extends BaseModeImplementation via the modes/index.ts barrel). Confirmed still present, pre-existing (first flagged in __tests__/persistence.test.ts's own comment before tonight), and test-tooling-only: plain node dist/sona-manager.js and the package's own tsc build are both clean.

3. Evaluation Receipt

evaluated: accepted, verdict ACCEPT.

  • Stash-isolated (baseline = git stash on just sona-manager.ts, dist/ rebuilt): baseline 4/4 new tests fail (mgr.getEWCState is not a function). Candidate: 4/4 pass.
  • Full @claude-flow/neural suite: 143/143 (139 pre-existing + 4 new), 0 regressions, both with and without the fix for the pre-existing 139.
  • tsc --noEmit clean.
  • No cross-package blast radius: grep -rln "sona-manager\|SONAManager" v3/@claude-flow/cli/src/ → zero hits.

4. Baseline Comparison

Metric Baseline (reverted, rebuilt dist) Candidate
triggerLearning() → ewcState.taskCount after 1 cycle stays 0 forever (method never called) 1
consolidateEWC() → ewcState.fisher/means size 0 forever (nothing ever populates it) >0, matches live LoRA weight keys
New discriminating tests (4) 4/4 fail 4/4 pass
@claude-flow/neural full suite 139/139 (no new tests exist yet) 143/143
tsc --noEmit clean clean

5. Darwin Lineage

Ran a bounded, repository-local 3-generation × 4-candidate search over hardcoded EWCConfig.decay (0.9) — @metaharness/darwin is installed but wiring an adapter for one scoped parameter wasn't worth tonight's budget. Fitness = retention of a frozen task-A LoRA state vs. capture of a drifted task-B state after two consolidations.

First draft was non-reproducible run-to-run — caught in this session's own adversarial pass: initializeLoRAWeights() seeds with unseeded Math.random(), so each candidate was silently evaluated against a different random task-A baseline. Fixed by sharing one frozen, seeded task-A snapshot across all candidates (verified bit-identical across two repeated runs). Once fair, the metric was degenerate: means is an unconditional snapshot of current weights on every consolidation (correct EWC semantics), so a means-only metric can't be sensitive to decay, which governs only fisher. All 12 candidates scored identically.

INCONCLUSIVE for decay-tuning specifically — not a mark against the candidate itself (ACCEPT rests on the unit tests). Script committed: scripts/dream-cycle-ewc-decay-darwin.mjs. Shipped default (0.9) retained unchanged.

6. Flywheel Evidence

No separate signed receipt file this session (ruvector harness flywheel gate/verify needs a JSON evidence schema not populated this session — same disclosed gap as 2026-10-01). Recorded as structured text in issue #3621 §7: OBSERVATION (zero call sites, confirmed independently by two research roles) / MEASUREMENT (stash-isolated results + Darwin lineage above) / INFERENCE (generalizes to the broader frozen-LoRA-weights gap) / DECISION (ACCEPT, ship as draft) / REJECTION (Darwin's decay sub-question, INCONCLUSIVE not REJECT).

7. Reward Hack Check

No test/benchmark weakening, no gold-data alteration, no cherry-picking, no evaluator exploitation, no hardcoded outputs, no metric substitution, no selective task removal, no hidden preprocessing, no error suppression, no cost hiding, no seed manipulation to pass a test (Darwin's seeding is for reproducibility of the exploration only; the unit tests are deterministic or statistically-robust independent of it). No reward-hack CLI subcommand applicable (no MCP surface touched; metaharness mcp-scan doesn't apply here).

8. Security Review

Not security-sensitive: no auth/credential/network/filesystem/MCP-surface exposure. Internal learning-pipeline wiring bug, confined to @claude-flow/neural.

9. Regression Analysis

All 139 pre-existing tests pass identically with and without this change. The 4 new tests are the only behavioral delta. No other package references SONAManager (confirmed via grep), so no further regression surface exists.

10. ADR

Not created — scoped wiring/parameter-population fix at one call site plus one method body, same scope class as prior non-ADR dream-cycle nights (e.g. 2026-09-01, 2026-09-11, 2026-10-01).

11. Research Gist

Committed at docs/dream-cycle/dream-gist-2026-10-02.md (this branch) and embedded in full in issue #3621 §13 (no gh gist create available this session — same fallback as 2026-10-01).

12. Issue

Closes the evidence trail documented in #3621 (full 15-section report: ledger check, deep-dive findings, hypothesis, evaluation receipt, Darwin results, Flywheel evidence, reward-hack check, security review, capabilities+memory scan findings, competitor comparison, embedded gist, witness, recommendation).

13. Witness

Session commit:  9e4fd175b51fab476889723893fed6ae776b00af
Report SHA256:   98cddb179ff8a99b0914d3c3e9b251487b8adf82f0a02e161982d06252c5c16a
Witness stamp:   85c802eb8aefff7dd2798306ff4e7b277f51076f98ca0694a9b59e2871fcda9f

Full verifier procedure and the report itself are in issue #3621.

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.


🤖 Generated with RuFlo

https://claude.ai/code/session_0126wiQ3gH55fgJNBgFgXiYv


Generated by Claude Code

claude and others added 5 commits October 2, 2026 06:31
…ggerLearning(), populate Fisher/means from live LoRA weights

consolidateEWC() had zero callers anywhere in the repo, and even called
directly its ewcState.fisher/means maps were never populated from
anywhere -- every mode's computeEWCPenalty() therefore always read an
empty map and returned exactly 0 regardless of ewcLambda. EWC++ guarded
a data structure nothing ever wrote to, decoupled from the LoRA adapter
weights it was declared to protect.

- triggerLearning() now calls consolidateEWC() after each learning cycle
- consolidateEWC() blends fresh importance + means from the live LoRA
  weights before decaying, using the same squared-magnitude heuristic
  already disclosed in memory/ewc-consolidation.ts
- new getEWCState() read-only accessor for observability/tests
- new test file imports the built dist/ output (vite-node's SSR
  transform crashes on this file's modes/ import graph under direct
  src/ import -- confirmed still present, pre-existing, test-tooling-only,
  same workaround persistence.test.ts documents)
- bounded repository-local Darwin exploration of EWCConfig.decay
  (scripts/dream-cycle-ewc-decay-darwin.mjs): INCONCLUSIVE, fitness
  metric proved insensitive to the parameter (disclosed in the gist)

143/143 @claude-flow/neural tests (139 pre-existing + 4 new), 0
regressions, stash-isolated baseline-fails/candidate-passes confirmed,
tsc clean.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_0126wiQ3gH55fgJNBgFgXiYv
…it-leaf-package-publish)

audit-leaf-package-publish.mjs correctly flagged that this PR's source
change (sona-manager.ts) is a non-bundled leaf: a release would still
ship the stale 3.0.0 registry copy. Bumped:

- v3/@claude-flow/neural/package.json: 3.0.0 -> 3.1.0 (minor: adds the
  new public getEWCState() accessor, backward compatible)
- v3/@claude-flow/cli/package.json's exact pin on @claude-flow/neural:
  3.0.0 -> 3.1.0, matching the workspace version (resolved via pnpm's
  prefer-workspace-packages, not the registry -- confirmed working)
- root package.json's "@claude-flow/neural": widened 3.0.0 -> ^3.0.0.
  An exact pin here would break `npm install` immediately (root
  resolves from the real registry, not the workspace, and 3.1.0 isn't
  published yet); a caret range keeps root's install working against
  the currently-published 3.0.0 while satisfying
  audit-wrapper-dep-ranges.mjs's check that the range covers the
  workspace's actual version.
- package-lock.json / v3/pnpm-lock.yaml: regenerated via `npm install`
  / `pnpm install` (never hand-edited), 1 line each.

Actual publish of @claude-flow/neural@3.1.0 to npm remains a separate,
later, human-gated release action -- not done here.

Verified locally: audit-leaf-package-publish.mjs, audit-wrapper-dep-ranges.mjs,
audit-plugin-packages.mjs, audit-package-dep-overlap.mjs,
audit-umbrella-version-lockstep.mjs all clean; @claude-flow/neural
143/143 tests, tsc clean.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_0126wiQ3gH55fgJNBgFgXiYv
…Suite job (CI red)

Test Suite (ubuntu-latest) failed: __tests__/sona-ewc-consolidation.test.ts
imports ../dist/sona-manager.js by relative path (the workaround for
vite-node's SSR crash on this package's modes/ import graph -- see the
test file's own header comment), but `npm ci` at repo root only links
workspace packages, it never builds them, so dist/ didn't exist when
the root-level `vitest run` (via scripts/ci-test-ratchet.mjs) hit this
test file.

Added a build step for @claude-flow/neural mirroring the existing
cli-core/security steps already in this same job for the identical
reason (dist/ needed at runtime, not just type-checked). Unlike
cli-core's step, no "overlay onto node_modules" copy is needed here --
this test's import path is a direct relative path within the workspace
package itself, not a root node_modules resolution.

Verified locally: deleted dist/ and the stale tsconfig.tsbuildinfo,
reran the exact `npm run build --prefix v3/@claude-flow/neural` command
CI will run, confirmed dist/sona-manager.js is produced.

Co-Authored-By: RuFlo <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_0126wiQ3gH55fgJNBgFgXiYv
…0.0 (CI: npx-install smoke)

CLI npx-install smoke (#1147) failed: the prior commit's exact pin
"@claude-flow/neural": "3.1.0" 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
ETARGET. This is the same failure class CLAUDE.md already documents
happening once before with a different package
(memory@3.0.0-alpha.27, 2026-09-28): an exact pin to an unpublished
workspace version breaks installs, as distinct from the #3414 problem
(a non-exact range silently resolving to a stale OLDER copy) that
exact-pinning was originally introduced to prevent.

v3/.npmrc's prefer-workspace-packages=true means pnpm already resolves
@claude-flow/neural from the workspace regardless of whether the CLI's
declared range is exact or a caret range -- #3414's concern doesn't
apply here. Widened to "^3.0.0": satisfies audit-leaf-package-publish.mjs
(workspace 3.1.0 is covered) and audit-wrapper-dep-ranges.mjs, while a
real `npm install` of the packed tarball now resolves to the actually-
published 3.0.0 and succeeds.

Verified locally: ran the exact scripts/smoke-cli-npx-install.mjs CI
runs (packs the CLI, npm installs the tarball into a scratch project,
runs `cli --version`) -- passes. Also re-verified
audit-leaf-package-publish.mjs, audit-wrapper-dep-ranges.mjs,
audit-plugin-packages.mjs, audit-package-dep-overlap.mjs,
audit-umbrella-version-lockstep.mjs, and the full @claude-flow/neural
suite (143/143) all still clean.

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

ruvnet commented Oct 2, 2026

Copy link
Copy Markdown
Owner Author

CI: Test Suite (ubuntu-latest) failed on 12aaf89 — v3/@claude-flow/hooks/__tests__/statusline/statusline-collision.test.ts ("Statusline Collision Zone Avoidance should have clear collision zone in safe multi-line output", Error: STACK_TRACE_ERROR).

Not this PR's: the diff only touches @claude-flow/neural, docs/dream-cycle/, and CI/package-manifest plumbing — nothing in @claude-flow/hooks/statusline. Treating as a candidate flake (the job log shows this run was forced onto Node 24 despite the NODE_VERSION: 20 config, a known source of stack-trace-format-sensitive flakiness) and triggered the one allowed re-run of the failed jobs. Will report back if it fails again on re-run.


Generated by Claude Code

@ruvnet

ruvnet commented Oct 9, 2026

Copy link
Copy Markdown
Owner Author

Backlog sweep 2026-10-09: superseded by the consolidated EWC plan on #3879 (comment above). Do not merge this one alone: it populates Fisher/means that no mode's penalty can reach, and it is DIRTY with package.json/lockfile version edits. Keeping it open until the unified PR exists.

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

Development

Successfully merging this pull request may close these issues.

2 participants