Files
msd-core/tests/debugger-semantic-recall.test.cjs
Tom Boucher dd5a2211c9 enhance(#1964): semantic knowledge-base recall via MemPalace (keyword fallback) (#2416)
* test(#1964): add failing-first semantic-recall contract tests

Epic #1957 Phase 3C (final). Source-text-is-the-product contract tests:
semantic recall via MemPalace (top-k meaning-similar prior resolutions, catches
same-root-cause/different-wording cases), indexing resolved sessions at archive,
graceful degradation to keyword matching when MemPalace is absent,
knowledge-base.md stays the durable plain-text source of truth, agent Phase 0 /
Matching Logic is semantic-first (the stale 'keyword overlap, not semantic
similarity' claim must go), and no new embedding/vector infra (reuse MemPalace).

Failing-first: reference, the Matching Logic reframe, the Phase 0 consolidation,
and the archive indexing step do not yet exist.

* feat(#1964): semantic knowledge-base recall via MemPalace (keyword fallback)

Epic #1957 Phase 3C (FINAL). Replaces keyword-overlap matching with semantic
recall: at Phase 0 the debugger queries MemPalace with the current symptoms
and surfaces the top-k meaning-similar prior resolutions, catching the
same-root-cause/different-wording cases keyword overlap missed (the self-noted
'keyword overlap, not semantic similarity' limitation). Resolved sessions are
indexed into MemPalace at archive (symptoms + root_cause(s) + fix + recurrence
guard). knowledge-base.md remains the durable plain-text source of truth; when
MemPalace is absent the debugger falls back to keyword-overlap matching
(logged, never a silent skip). No new embedding/vector infrastructure —
MemPalace is reused.

Size-neutral agent edits: the Matching Logic section reframed (keyword-only ->
semantic-first + keyword-fallback + @-include); Phase 0's three keyword bullets
consolidated into one semantic-first bullet; one MemPalace-indexing step added
at archive. Agent at 57222 B (122 B headroom — final phase). Full rules in
gsd-core/references/debugger-semantic-recall.md. INVENTORY + manifest +
agent-size baseline + install-parity goldens + AGENTS.md updated.

* fix(#1964): address orthogonal review (invocation mechanism, index Resolution-not-symptoms + redaction, fallback detail)

- HIGH: the 'query MemPalace' instruction was WHAT-level only; the agent has
  no MCP tools. Added an Invocation section naming the Bash CLI
  (mempalace search --wing <wing>) + MCP-when-registered + wing resolution
  (config.mempalace.wing -> project_code -> project dir), matching every other
  MemPalace integration. Without this the feature silently degraded to keyword
  matching even when MemPalace was present.
- MEDIUM (security x2): index the agent-authored Resolution summary
  (root_cause + fix + recurrence_guard), NOT raw user-supplied Symptoms —
  excludes attacker-controlled prose from the cross-session index AND reduces
  secret/PII leakage. Redact secret-shaped values before indexing. Stated the
  write order (KB append + commit MUST succeed before indexing).
- LOW: restored 'identifiers' + 'case-insensitive' to the keyword fallback;
  added a test asserting the fallback mechanics survived the Phase 0
  consolidation (Error patterns field, 2+ token overlap, identifiers,
  case-insensitive).

* chore(#1964): ratchet agent-size baseline downward (leaner archive bullet shrank gsd-debugger.md 57222->57197)

* chore(#1964): backfill changeset pr number (PR #2416)
2026-07-18 19:01:04 -04:00

105 lines
5.7 KiB
JavaScript

// allow-test-rule: source-text-is-the-product (see #1964)
// Agent .md + reference .md files — their text IS what the runtime loads.
// Testing text content tests the deployed semantic-recall contract.
// Per CONTRIBUTING.md exception matrix. Covers epic #1957 Phase 3C (#1964).
'use strict';
const { describe, test } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const ROOT = process.cwd();
const AGENT = path.join(ROOT, 'agents/gsd-debugger.md');
const REFERENCE = path.join(ROOT, 'gsd-core/references/debugger-semantic-recall.md');
describe('semantic knowledge-base recall via MemPalace (#1964, epic #1957 Phase 3C)', () => {
describe('reference extract exists and is wired in', () => {
test('gsd-core/references/debugger-semantic-recall.md exists', () => {
assert.ok(fs.existsSync(REFERENCE), 'debugger-semantic-recall.md reference must exist');
});
test('gsd-debugger.md @-includes the semantic-recall reference', () => {
const content = fs.readFileSync(AGENT, 'utf8');
assert.ok(
content.includes('@~/.claude/gsd-core/references/debugger-semantic-recall.md'),
'gsd-debugger.md must @-include the semantic-recall reference from the knowledge_base_protocol / Matching Logic'
);
});
});
describe('semantic recall via MemPalace (criterion 1 — same root cause, different wording)', () => {
test('reference documents querying MemPalace for meaning-similar prior resolutions', () => {
const content = fs.readFileSync(REFERENCE, 'utf8');
assert.ok(/mempalace/i.test(content), 'must name MemPalace as the semantic-memory surface');
assert.ok(/semantic/i.test(content), 'must document semantic (not keyword) recall');
assert.ok(/top-?k|meaning.similar|semantically.similar/i.test(content),
'must document surfacing top-k meaning-similar prior resolutions');
});
test('reference states the payoff: catches lexically-different / same-root-cause cases', () => {
const content = fs.readFileSync(REFERENCE, 'utf8');
assert.ok(/different.{0,30}word|lexically|same root cause|wording/i.test(content),
'must state that semantic recall catches same-root-cause/different-wording cases keyword overlap misses (the self-noted limitation)');
});
test('reference documents indexing resolved sessions into MemPalace at archive', () => {
const content = fs.readFileSync(REFERENCE, 'utf8');
assert.ok(/index|archive|store|capture/i.test(content),
'must document that resolved sessions are indexed into MemPalace at archive time');
});
});
describe('graceful degradation — MemPalace absent falls back to keyword matching (criterion 2)', () => {
test('reference documents the keyword fallback when MemPalace is unavailable', () => {
const content = fs.readFileSync(REFERENCE, 'utf8');
assert.ok(/fall[\s-]?back|fallback|when.*(?:absent|unavailable)|degrade/i.test(content),
'must document degradation to keyword matching when MemPalace is absent');
assert.ok(/keyword/i.test(content), 'must name keyword matching as the fallback');
});
test('the keyword-fallback mechanics survived the Phase 0 consolidation', () => {
// Guards against the consolidation dropping the extraction detail the
// fallback path needs. The reference must still document the Error
// patterns field scan + the 2+ token threshold + identifiers + case-insensitive.
const content = fs.readFileSync(REFERENCE, 'utf8');
assert.ok(/error patterns/i.test(content), 'fallback must scan the Error patterns field');
assert.ok(/2\+ token overlap/i.test(content), 'fallback must keep the 2+ token overlap threshold');
assert.ok(/identifier/i.test(content), 'fallback must keep identifier extraction (highest-signal token)');
assert.ok(/case-insensitive/i.test(content), 'fallback must keep the case-insensitive qualifier');
});
test('knowledge-base.md remains the durable plain-text source of truth', () => {
const content = fs.readFileSync(REFERENCE, 'utf8');
assert.ok(/knowledge-base\.md|durable|plain[\s-]?text|source of truth/i.test(content),
'must state that knowledge-base.md remains the durable plain-text source of truth (semantic recall is layered on top)');
});
});
describe('agent wiring — Phase 0 / Matching Logic is semantic-first', () => {
test('gsd-debugger.md knowledge_base_protocol describes semantic-first matching', () => {
const content = fs.readFileSync(AGENT, 'utf8');
// The Matching Logic section must now lead with semantic recall, not "keyword overlap, not semantic"
assert.ok(/semantic/i.test(content), 'agent must reference semantic recall');
assert.ok(/mempalace/i.test(content), 'agent must name MemPalace');
assert.ok(/fall[\s-]?back|fallback/i.test(content), 'agent must describe the keyword fallback');
});
test('the stale "keyword overlap, not semantic similarity" claim is gone', () => {
const content = fs.readFileSync(AGENT, 'utf8');
assert.ok(
!/keyword overlap, not semantic similarity/i.test(content),
'the old "Matching is keyword overlap, not semantic similarity" claim must be removed (it is now semantic-first)'
);
});
});
describe('no new embedding/vector infrastructure (Choose Boring / Zawinski)', () => {
test('reference states MemPalace is reused — no new vector store', () => {
const content = fs.readFileSync(REFERENCE, 'utf8');
assert.ok(/reuse|no new|without adding|existing.*(?:semantic|memory|capability)/i.test(content),
'must state MemPalace is reused rather than adding new embedding/vector infrastructure');
});
});
});