fix: port search and dimension safety guardrails

- enforce canonical dimensions across app and MCP surfaces
- prioritize title-first node search in UI and query tools
- add regression tests for ranking and dimension validation
This commit is contained in:
“BeeRad”
2026-03-23 10:47:18 +11:00
parent c1eda905d3
commit 545dc6e2e8
18 changed files with 250 additions and 78 deletions
@@ -2,6 +2,34 @@
const { query, transaction, getDb } = require('./sqlite-client');
function normalizeDimensionName(value) {
return String(value || '').trim().replace(/\s+/g, ' ');
}
function getUnknownDimensions(dimensions) {
if (!Array.isArray(dimensions) || dimensions.length === 0) return [];
const normalized = dimensions
.map(normalizeDimensionName)
.filter(Boolean);
if (normalized.length === 0) return [];
const placeholders = normalized.map(() => '?').join(', ');
const rows = query(`SELECT name FROM dimensions WHERE name IN (${placeholders})`, normalized);
const existing = new Set(rows.map(row => normalizeDimensionName(row.name)));
return normalized.filter(value => !existing.has(value));
}
function formatUnknownDimensionsError(values) {
if (values.length === 1) {
return `Unknown dimension: "${values[0]}". Create it first or use an existing dimension.`;
}
return `Unknown dimensions: ${values.map(value => `"${value}"`).join(', ')}. Create them first or use existing dimensions.`;
}
/**
* Get nodes with optional filtering.
*/
@@ -116,6 +144,10 @@ function createNode(nodeData) {
} = nodeData;
const title = sanitizeTitle(rawTitle);
const unknownDimensions = getUnknownDimensions(dimensions);
if (unknownDimensions.length > 0) {
throw new Error(formatUnknownDimensionsError(unknownDimensions));
}
const now = new Date().toISOString();
const db = getDb();
@@ -174,6 +206,13 @@ function updateNode(id, updates, options = {}) {
throw new Error(`Node with ID ${id} not found. Use rah_search_nodes to find nodes by keyword.`);
}
if (Array.isArray(dimensions)) {
const unknownDimensions = getUnknownDimensions(dimensions);
if (unknownDimensions.length > 0) {
throw new Error(formatUnknownDimensionsError(unknownDimensions));
}
}
transaction(() => {
const setFields = [];
const params = [];