Files
msd-core/docs/adr/0656-research-module-seam.md
Jakub Zych a9a7a328e6 refactor: hard-fork GSD -> MSD (Make Software Done)
Mechanical rename produced by scripts/msd-rename.cjs: gsd/Gsd/GSD -> msd/Msd/MSD
across contents and paths, upstream package/repo coordinates -> @golem15/msd-core
and golem15com/msd-core. Deep links into upstream history, sibling upstream
packages, the GSD-2 import feature, CHANGELOG.md and .changeset/ are kept as-is.

Hand edits on top: MSD block-letter banner and logos, LICENSE copyright line,
package/plugin identity, regenerated lockfile, install-tree fixtures, derived
registries and benchmark baseline; migration checksum baseline re-locked
(MSD keeps its own install state, so no install had applied the old sums);
sort-order and regex-escaped expectations in tests adjusted.
2026-10-06 01:47:40 +02:00

4.8 KiB

ADR-0656: Research Module — L2-hybrid seam for cached, curated-first research

  • Status: Accepted
  • Date: 2026-06-03

Context

Research in MSD was entirely prose-duplicated. Seven researcher agents each carried their own copy of the provider waterfall (Context7, Ref, Jina, Exa, Tavily, Perplexity, Brave, Firecrawl, websearch), their own confidence-tier definitions, and their own fallback policy. Every time a new provider was added or the ordering changed, all seven files drifted independently — the exact failure mode META.RULE.brief-no-paraphrase exists to prevent.

There was no research cache. Agents checked for an existing RESEARCH.md file but had no TTL, no content-addressing, and no notion of staleness. Identical queries re-fetched from live providers across phases and projects.

Package legitimacy was a pip-install slopcheck bolt-on. When the slopcheck binary was absent or crashed, every package was silently downgraded to [ASSUMED], removing the legitimacy gate entirely rather than degrading gracefully.

Context7 was prompt-only: agents mentioned it in prose but there was no code-level integration, no cache, and no structured verdict returned to the orchestrator.

Decision

Introduce an L2-hybrid seam: code owns cache, provider policy, legitimacy verdicts, and confidence classification; MCP owns the actual network fetch (a .cjs module cannot call MCP tools directly).

Three modules are introduced under src/ compiled to msd-core/bin/lib/*.cjs per ADR-457 (generated-single-source):

Research Store (src/research-store.cts): content-addressed cache keyed by sha256(ecosystem + library + version + query + kind). getResearch() never throws — it returns { hit, stale } mirroring the graphify staleness tri-state pattern. TTL is per-source: curated-doc providers get 30 days (HIGH), medium-quality sources get 7 days (MED), web/synthesis gets 1 day (LOW). Two storage tiers: curated-doc kinds write to ~/.msd/research-cache (cross-project reuse); web and synthesis results write to .planning/research/.cache (project-local, gitignored).

Research Provider (src/research-provider.cts): single source of truth for PROVIDER_WATERFALL. Docs waterfall: Context7 → Ref → Jina → websearch. Web waterfall: Exa → Tavily → Perplexity → Brave → websearch. Scrape: Firecrawl → Jina (Firecrawl is scrape-only, not in docs/web discovery). planResearch() returns cache hits plus a fetch plan for misses. classifyConfidence() stamps HIGH | MEDIUM | LOW by provider authority + verification evidence — the tier set is unchanged (ADR-consistent), but HIGH now requires code-computed ground-truth corroboration (e.g. legitimacyVerdict: 'OK'); provider authority alone caps at MEDIUM; SLOP caps at LOW. Provider availability is driven by config flags and _API_KEY env vars; context7, jina, and websearch are always available as the terminal fallback.

Package Legitimacy (src/package-legitimacy.cts): registry-API verdicts via injectable adapters for npm, PyPI, and crates.io. Thresholds: { minAgeDays: 30, minWeeklyDownloads: 1000, requireRepo: true }. Verdict per package: OK | SUS | SLOP. slopcheck is an optional escalate-only adapter — it can only raise a verdict, never lower it — and is not the install-or-degrade gate. Absence of slopcheck leaves registry-API verdicts intact rather than downgrading everything to [ASSUMED].

All three modules are reachable via msd-tools query research-plan | research-store | package-legitimacy.

Agents return a RESEARCH.md path; they never return raw fetched content. This enforces context discipline: subagent isolation, compact provider output, fetches-to-disk, cache-returns-digest.

Consequences

Positive:

  • Provider policy lives in one tested module. Adding or reordering a provider is a one-line change that propagates to all researcher agents.
  • Content-addressed cache eliminates redundant fetches across phases and projects.
  • Package legitimacy is registry-API-first and degrades gracefully; slopcheck enriches without gating.
  • The msd-tools query interface is the test surface — behavioral tests can assert typed JSON output without source-grep.

Deferred to #657:

  • Collapsing the seven researcher agent .md files into generated-from-profiles agents (the prose waterfall duplication in those files is the primary DEFECT.RESEARCH-PROVIDER-PROSE-DRIFT site).
  • The install.js MCP tool-mapping for tavily, ref, and jina (those land where the agents declare the tools they need).

Known constraint: API context-editing primitives (clear_tool_uses, memory tool) are the conceptual model for context discipline, but they are not configurable through the Claude Code harness today. The current implementation achieves context discipline through subagent isolation and fetch-to-disk patterns.