The Problem
ConceptService (shipped via #10032 / PR #10048, extended via #10049 / PR #10052 and #10033 / PR #10078) currently exposes query APIs that return unbounded lists:
findGuideGaps(minWeight) — returns ALL gaps sorted by weight
getConceptTree(maxTier) — returns full tier-filtered hierarchy
classifyConcept(file) — returns every concept with an IMPLEMENTED_BY edge to file
getAnalogousConcepts(id) — returns every ANALOGOUS_TO edge
At the current ontology size (59 nodes / 169 edges, 17 guide gaps), this is fine. As #10050 (description enrichment), #10036 (memory core concept discovery), and #10037 (ChromaDB embedding) land, node + edge counts are explicitly expected to grow — potentially into the low thousands. Unbounded returns will flood any agent that consumes them directly in a response context.
Historical Precedent (Gold Standard to Replicate)
Session 74cfa05e (2026-01-08, summary title "Refine AI KB Hierarchy & Source Parsing with JSDoc") hit exactly this class of bug on get_class_hierarchy. The fix was making the root parameter mandatory, preventing any caller from accidentally dumping the full hierarchy into context. Same pattern applies here — we've been burned once; the structural discipline exists; applying it uniformly to ConceptService is "preventive application of an already-proven Gold Standard," not invention.
Proposed Architecture
New relevance-bounded APIs on ai/services/ConceptService.mjs:
findConceptsRelevantTo(taskDescription, {limit}) — given a natural-language task description, return top-N concepts (default 5, configurable). Phase 1 bootstraps on keyword + tier matching; Phase 2 swaps in ChromaConceptIndex cosine-similarity ranking once #10037 ships.
findGapsRelevantTo(taskDescription, {limit, minWeight}) — task-scoped version of findGuideGaps that returns only gaps semantically related to the task. Same Phase 1 → Phase 2 evolution.
classifyConcept(file, {limit}) — add optional limit parameter.
- All new APIs enforce
limit with a documented default (≤5) and hard ceiling (TBD — measure during implementation, don't hardcode per feedback_challenge_numerical_thresholds_in_acs.md).
Keep existing unbounded APIs for internal ConceptService use and internal orchestration (DreamService ingestion, visualization app in #10034 which needs full tree). Agent-facing LLM query surfaces MUST use the bounded variants.
Tooling vs Data Structure — Scope Clarity
This is a tooling/API-surface fix, NOT a data-structure redesign. JSONL + in-memory scan remains appropriate at current and projected scale. If raw scan becomes a bottleneck later, #10037's ChromaDB pathway addresses it orthogonally.
Acceptance Criteria
Scope Exclusions
- ChromaDB-backed semantic similarity (that's #10037's deliverable; this ticket lands the API surface + keyword/tier bootstrap)
- Visualization app changes (#10034) — unaffected, still uses unbounded internal APIs
Related
- Parent: #10030
- Gold Standard precedent: session
74cfa05e (2026-01-08) / get_class_hierarchy root param
- Follows from: #10033 / PR #10078 (observability lesson — line count is proxy, architectural layer is the right assertion site)
- Sub-issue: "Strengthen tech-debt-radar to detect unbounded-list APIs" (filed alongside, linked after this ticket is created)
Origin Session ID
62d6f155-e57f-4279-9b59-36c9e4ecbc5e
The Problem
ConceptService(shipped via #10032 / PR #10048, extended via #10049 / PR #10052 and #10033 / PR #10078) currently exposes query APIs that return unbounded lists:findGuideGaps(minWeight)— returns ALL gaps sorted by weightgetConceptTree(maxTier)— returns full tier-filtered hierarchyclassifyConcept(file)— returns every concept with anIMPLEMENTED_BYedge to filegetAnalogousConcepts(id)— returns everyANALOGOUS_TOedgeAt the current ontology size (59 nodes / 169 edges, 17 guide gaps), this is fine. As #10050 (description enrichment), #10036 (memory core concept discovery), and #10037 (ChromaDB embedding) land, node + edge counts are explicitly expected to grow — potentially into the low thousands. Unbounded returns will flood any agent that consumes them directly in a response context.
Historical Precedent (Gold Standard to Replicate)
Session
74cfa05e(2026-01-08, summary title "Refine AI KB Hierarchy & Source Parsing with JSDoc") hit exactly this class of bug onget_class_hierarchy. The fix was making therootparameter mandatory, preventing any caller from accidentally dumping the full hierarchy into context. Same pattern applies here — we've been burned once; the structural discipline exists; applying it uniformly to ConceptService is "preventive application of an already-proven Gold Standard," not invention.Proposed Architecture
New relevance-bounded APIs on
ai/services/ConceptService.mjs:findConceptsRelevantTo(taskDescription, {limit})— given a natural-language task description, return top-N concepts (default 5, configurable). Phase 1 bootstraps on keyword + tier matching; Phase 2 swaps inChromaConceptIndexcosine-similarity ranking once #10037 ships.findGapsRelevantTo(taskDescription, {limit, minWeight})— task-scoped version offindGuideGapsthat returns only gaps semantically related to the task. Same Phase 1 → Phase 2 evolution.classifyConcept(file, {limit})— add optionallimitparameter.limitwith a documented default (≤5) and hard ceiling (TBD — measure during implementation, don't hardcode perfeedback_challenge_numerical_thresholds_in_acs.md).Keep existing unbounded APIs for internal ConceptService use and internal orchestration (DreamService ingestion, visualization app in #10034 which needs full tree). Agent-facing LLM query surfaces MUST use the bounded variants.
Tooling vs Data Structure — Scope Clarity
This is a tooling/API-surface fix, NOT a data-structure redesign. JSONL + in-memory scan remains appropriate at current and projected scale. If raw scan becomes a bottleneck later, #10037's ChromaDB pathway addresses it orthogonally.
Acceptance Criteria
limitlimitdefault documented and justified (no hardcoded cap without derivation — perfeedback_challenge_numerical_thresholds_in_acs.md)@protectedor annotates intended callerScope Exclusions
Related
74cfa05e(2026-01-08) /get_class_hierarchyrootparamOrigin Session ID
62d6f155-e57f-4279-9b59-36c9e4ecbc5e