Ships a deterministic one-shot harvester so a fresh ruflo install can
bootstrap its self-learning system from the repo's own GitHub history
(git commits + gh issues) instead of starting empty.
Each item flows through the same code paths real-time learning uses —
distillAndSerialise (ADR-076) → recordTrajectory (ADR-074) → neural
store seed. Closes the ADR-075 consistency note "globalStats moved but
neural store stayed empty" by writing to both stores from the same script.
Measured (this checkout):
• 80/80 trajectories trained from 50 commits + 30 issues
• +95 trajectoriesRecorded, +85 patternsLearned, +80 neuralPatternCount
• 3.36 ms avg per trajectory
• 100% retrieval match rate across 10 sample queries
• 7.67 ms avg query latency at N=95
Adds:
- scripts/pretrain-from-github.mjs (harvester, env-configurable)
- scripts/benchmark-pretrained-retrieval.mjs (after-pretrain validator)
- __tests__/pretrain-from-github.test.ts (CI guard, embedded fixture,
no git/gh shell-outs — auto-picked-up by v3-ci.yml line 610)
- v3/docs/adr/ADR-077-pretrain-from-history.md (decision + measured
proof + honest limits)
- v3/docs/learning/self-learning-usage.md (copy-paste guide for all
three learning paths plus this pretrain flow)
Co-Authored-By: RuFlo <ruv@ruv.net>
The self-learning subsystem reported success but persisted nothing queryable.
Three surface-level bugs, all in the ruflo CLI's wiring of the existing
ruvector primitives (no upstream changes needed):
1. hooks_task-completed {trainPatterns:true} was a stub — hardcoded
patternsLearned:0, ignored the trainPatterns flag entirely, noted in the
response itself that work was "delegated to the intelligence pipeline
(#1916 follow-up)." Now synthesizes a one-step trajectory from
{taskId, success, quality, content} and calls recordTrajectory() on the
real SONA + EWC++ path. Returns honest deltas and a learningPath field
('trajectory-pipeline' | 'recorded-only') so callers know what happened.
2. signalsProcessed was a dead counter — initialized in 3 places, read once,
incremented zero times. Added recordSignalProcessed() to intelligence.ts
(throttled persist) and wired it into bridgeStoreEntry. Also fixed
loadPersistedStats() to restore patternsLearned + signalsProcessed across
restarts (was only restoring trajectoriesRecorded — a separate "resets
learning history on every restart" bug surfaced by the audit).
3. hooks_pretrain stored a 1-row summary bundle in the `pretrain` namespace
but never wrote per-pattern rows to the neural store, so neural_patterns
list returned [] right after pretrain "succeeded" with N patterns
extracted. Now also calls storeNeuralPatterns() per pattern; surfaces
patternsBundled + patternsIndexed + sources.stores in the response so
callers can see exactly which stores were written.
Plus per the goal-condition "soften where possible to indicate multiple
paths":
- Every learning-adjacent handler returns explicit fields naming the path
taken and the store(s) written.
- task-completed description lists the three paths (one-step train,
multi-step trajectory, store-only).
- "[OK] Outcome recorded" theater replaced with notes that say what didn't
happen as well as what did.
Plus OWASP ASI06 (Memory/Context Poisoning) hardening — basic input
sanitization on task-completed content before it feeds SONA: strip ASCII
control chars, cap to 4 KB. Surfaced by #2241 (Dream Cycle 2026-05-30).
Tests: __tests__/self-learning-2245.test.ts — 9 tests across EASY (primitives)
/ MEDIUM (MCP surfaces) / COMPLEX (multi-step + persistence) categories.
Proof harness: scripts/benchmark-self-learning.mjs — 5 sections (A–E),
writes a run JSON. Latest run: signalsProcessed +10, trained=10/10 via
trajectory pipeline at ~18 ms/call, pretrain stored=10/listed=10,
multi-step persisted=5/sonaUpdate=5. Overall: PASSED. See
docs/learning/self-learning-2245-proof.md for copy-paste reproduction.
Closes#2245.
Co-Authored-By: RuFlo <ruv@ruv.net>