mirror of
https://github.com/ruvnet/ruflo.git
synced 2026-09-14 14:01:28 +08:00
79d266507e
Dream-cycle #2760 (SCM routed memory 86% LongMemEval): agents retrieve dramatically better on long-memory workloads when queries are routed to the RIGHT memory tier — episodic (session recall), semantic (learned patterns), or procedural (skills/recipes). Ruflo had all three tiers writing to different namespaces but `memory search` never discriminated. v1 MVP: keyword-scored classifier. Ships two integration surfaces: 1. Standalone: `ruflo memory classify -q "…" [--intent auto|mixed|episodic| semantic|procedural]` — prints the resolved intent + confidence + suggested namespaces. `--format json` for pipelines. 2. Search hint: `ruflo memory search -q "…" --intent auto` — logs the SCM router's decision + suggests a specific --namespace value the user can rerun with. Intentionally does NOT mutate the search backend behavior in v1 — the routing is advisory so no baseline retrieval is disturbed. v2 will apply when the search backend adds a multi-namespace OR filter. Namespace map (matches ruflo's actual write conventions): episodic → sessions, session-checkpoints, trajectory, trajectories, routing-outcomes, commands, feedback semantic → patterns, learned-patterns, adr-patterns, adr-edges, reasoning-patterns, concepts procedural → skills, agents, workflow-templates, playbooks, recipes Classifier weighting: keyword length / 6 (longer phrases score higher because they're less ambiguous — "how do i" beats "how"). Confidence threshold 0.35; below → mixed fallback so a low-confidence guess doesn't misroute. E2E: "when did we last touch auth" → episodic (conf 100%) "how does JWT work" → semantic (conf 100%) "how do I onboard a new coder subagent" → procedural (conf 100%) "auth" → mixed (conf 0, no keywords) Regression tests: 7/7 pass (three canonical intents + mixed fallback + explicit override + auto delegates to classifier + mixed explicit). Refs: dream-cycle #2760 (2026-07-22). Co-Authored-By: RuFlo <ruv@ruv.net>
58 lines
2.4 KiB
TypeScript
58 lines
2.4 KiB
TypeScript
/**
|
|
* Regression guard for #2760 dream-cycle SCM (Selective Context Memory)
|
|
* query-intent classifier.
|
|
*
|
|
* v1 classifier is keyword-scored. Must correctly route the three canonical
|
|
* intent classes (episodic / semantic / procedural), degrade gracefully to
|
|
* `mixed` on ambiguous queries, and honor an explicit override.
|
|
*/
|
|
|
|
import { describe, it, expect } from 'vitest';
|
|
import { classifyQueryIntent, resolveIntent, INTENT_NAMESPACES } from '../src/memory/scm-classifier.js';
|
|
|
|
describe('#2760 SCM classifier', () => {
|
|
it('classifies episodic recall ("when did we last …")', () => {
|
|
const r = classifyQueryIntent('When did we last touch the auth module?');
|
|
expect(r.intent).toBe('episodic');
|
|
expect(r.namespaces).toEqual(expect.arrayContaining(['sessions', 'trajectory']));
|
|
expect(r.confidence).toBeGreaterThan(0.35);
|
|
});
|
|
|
|
it('classifies semantic explanation ("how does X work" / "what is Y")', () => {
|
|
const r = classifyQueryIntent('What is the design principle behind our AgentDB memory tiers?');
|
|
expect(r.intent).toBe('semantic');
|
|
expect(r.namespaces).toEqual(expect.arrayContaining(['patterns']));
|
|
});
|
|
|
|
it('classifies procedural how-to ("how do I …" / "steps to …")', () => {
|
|
const r = classifyQueryIntent('How do I set up a new subagent role with workspace-scoped permissions?');
|
|
expect(r.intent).toBe('procedural');
|
|
expect(r.namespaces).toEqual(expect.arrayContaining(['skills']));
|
|
});
|
|
|
|
it('falls back to mixed on an ambiguous query with no intent keywords', () => {
|
|
const r = classifyQueryIntent('auth');
|
|
expect(r.intent).toBe('mixed');
|
|
expect(r.namespaces).toEqual([]);
|
|
expect(r.confidence).toBeLessThan(0.35);
|
|
});
|
|
|
|
it('resolveIntent respects an explicit override', () => {
|
|
const r = resolveIntent('completely ambiguous three word query', 'procedural');
|
|
expect(r.intent).toBe('procedural');
|
|
expect(r.namespaces).toEqual([...INTENT_NAMESPACES.procedural]);
|
|
});
|
|
|
|
it('resolveIntent auto path delegates to the keyword classifier', () => {
|
|
const auto = resolveIntent('How do I onboard a new coder subagent?', 'auto');
|
|
const direct = classifyQueryIntent('How do I onboard a new coder subagent?');
|
|
expect(auto.intent).toBe(direct.intent);
|
|
});
|
|
|
|
it('resolveIntent mixed explicit returns full namespaces empty', () => {
|
|
const r = resolveIntent('anything', 'mixed');
|
|
expect(r.intent).toBe('mixed');
|
|
expect(r.namespaces).toEqual([]);
|
|
});
|
|
});
|