Repository navigation
Add property filter support to query_nodes MCP handler - #1044
Conversation
…es (#1036, #442, #443) Completes the remaining 3 issues from Epic #444 (Query and Listing System): **#1036 — Property filter support in query_nodes MCP handler:** - Expand MCP tool schema to accept any field name and operators: equals, not_equals, contains, starts_with, ends_with - Map MCP filters to PropertyFilter with JSONPath construction - Apply property filters in-memory in NodeService after DB query + migration - Return clear errors for unsupported operators and invalid field/operator combinations - 9 new integration tests covering all filter operators and edge cases **#442 — QueryEditor with JSON Textarea Interface:** - New query-editor.svelte component with JSON textarea, validation, and error display - Query template examples (incomplete tasks, keyword search, priority filter) - Integrated into QueryNodeViewer with edit mode toggle - 33 tests covering validation, save/cancel flows, and templates **#443 — QueryPreferencesService for User View Preferences:** - New QueryPreferences type and QueryPreferencesService with write-through cache - localStorage persistence with defensive parsing and fallback to defaults - View tab UI in QueryNodeViewer (List/Table/Kanban) with preference persistence - 21 tests covering cache behavior, storage, and error handling Co-Authored-By: Claude <noreply@anthropic.com>
Pragmatic Code Review -- PR #1044Review Type: Initial Review Overall AssessmentThis is a well-structured, cohesive PR that wires up the existing Requirements CheckIssue #1036 -- Property Filter Support in query_nodes MCP Handler
Issue #442 -- QueryEditor with JSON Textarea Interface
Issue #443 -- QueryPreferencesService
Code Review FindingsImportant Issues1. [Important] Preview feature from #442 not implemented Issue #442 acceptance criteria explicitly states: "Preview shows estimated result count." The issue's proposed solution includes a Preview button that calls This is an acceptance criterion gap. Either implement the preview feature or update the issue to explicitly defer it to a follow-up. 2. [Important] Save failure is silent to the user When } catch (e) {
const message = e instanceof Error ? e.message : String(e);
log.error('QueryNodeViewer: failed to save query definition', { error: message });
// No user-visible error feedback
}Recommendation: Set an error state variable and display it in the edit mode wrapper, or propagate the error to the QueryEditor's error display. 3. [Important] $effect(() => {
jsonText = JSON.stringify(query ?? DEFAULT_QUERY, null, 2);
});This effect tracks Recommendation: Use Suggestions4. [Suggestion] In-memory fetch-all for property filters has no safety bound When property filters are present, let (db_limit, db_offset) = if filter.property_filters.is_some() {
(None, None) // fetches ALL matching nodes from DB
} else {
(filter.limit, filter.offset)
};This is acceptable for the current pre-release stage, but consider adding a hard cap (e.g., 10,000 nodes) as a safety valve to prevent accidental OOM on large datasets. Not a blocker now. 5. [Suggestion] Duplicate filter field warning should be an error When a duplicate field is detected, a 6. [Suggestion] Comment/code mismatch in effective_limit logic The comment says "we pass the actual requested limit for those" (referring to property filters), but when 7. [Suggestion] The 8. [Suggestion] Test logic duplication between component and test file The test file duplicates Nits9. Nit: 10. Nit: // Current
...(definition.sorting !== undefined ? { sorting: definition.sorting } : {}),
...(definition.limit !== undefined ? { limit: definition.limit } : {}),
// Simpler (undefined keys are dropped by JSON.stringify in backend anyway)
sorting: definition.sorting,
limit: definition.limit,Whether this matters depends on whether the backend treats Recommendation: REQUEST CHANGESThe PR is a strong implementation overall with good architecture, clean code, and comprehensive tests. However, there are two actionable items before merge:
The Everything else is suggestion-level and can be addressed at the author's discretion. |
malibio
left a comment
There was a problem hiding this comment.
See detailed review comment. Two actionable items before merge: (1) Preview feature from #442 acceptance criteria is missing -- implement or explicitly defer; (2) Save failure in handleQuerySave is silent to the user -- surface the error in the UI. Recommendation: REQUEST CHANGES (posted as comment because GitHub prevents requesting changes on your own PR).
Addresses all reviewer recommendations: 1. Preview feature (#442 AC): Added onPreview callback to QueryEditor with result count display; wired up in QueryNodeViewer via backendAdapter.queryNodes 2. Silent save failure: Added saveError state to QueryNodeViewer, displayed as alert banner above the editor when save fails 3. $effect overwrites user edits: Changed from reactive $effect sync to one-time init pattern using initialized flag — parent re-derivations no longer overwrite in-progress textarea edits 4. Unbounded in-memory fetch: Added 10,000-node safety cap when property filters bypass the DB limit (PROPERTY_FILTER_FETCH_CAP) 5. Duplicate filter field: Changed from tracing::warn to MCPError::invalid_params for consistency with other validation errors 6. Comment/code mismatch: Rewrote misleading comment about effective_limit logic 7. QueryDefinition in .svelte file: Moved QueryDefinition, DEFAULT_QUERY, and QUERY_TEMPLATE_EXAMPLES to $lib/types/query.ts; component and viewer import from shared location 8. Test logic duplication: Tests now import QueryDefinition, DEFAULT_QUERY, and QUERY_TEMPLATE_EXAMPLES from $lib/types/query instead of duplicating them 9. unwrap_or on strip_prefix: Replaced with explicit match that logs warning and returns false instead of silently falling through 10. Spread-with-conditional pattern: Simplified to direct property assignment Co-Authored-By: Claude <noreply@anthropic.com>
Review Feedback Addressed — All 10 FindingsAll reviewer recommendations from the initial review have been addressed in commit a69f0e9:
Tests: 126 files, 3903 passed (frontend) + 18 Rust property_filter tests passed. Zero regressions. |
Re-Review -- PR #1044 (Fix Commit a69f0e9)Review Type: Re-review after REQUEST CHANGES Verification of All 10 Findings
New Issues Introduced by FixesNone found. The fixes are clean and surgical. Specific observations:
Recommendation: APPROVEAll 10 findings have been properly and thoroughly addressed. The fixes are clean, well-documented, and introduce no regressions or new concerns. The code quality remains high throughout. |
… add serde aliases ## Review Findings Addressed ### 🔴 Finding 1: params.rs was dead code (pub mod params never declared) - Added `pub mod params;` to packages/core/src/mcp/mod.rs ### 🔴 Finding 2: params.rs re-exported non-existent SearchNodesParams - Defined `SearchNodesParams` struct in packages/core/src/mcp/handlers/search.rs - Matches the agent's search_nodes tool schema (query, node_type, limit) ### 🔴 Finding 3: Rebase onto main (SearchNodeFilters / 4-arg semantic_search_nodes) - Rebased feature branch onto origin/main; WIP commit dropped (already upstream) - Also fixed pre-existing compile error in tools.rs: get_tool_schemas signature mismatch (schema_ids: &[String] vs callers passing &[SchemaNode]) — updated to accept &[SchemaNode] and emit dynamic tool descriptions when user schemas exist - Updated tools_test.rs unit tests to construct SchemaNode test fixtures ### 🟡 Finding 4: Phase 2 — replace manual require_str/optional_str/optional_usize - Removed require_str, optional_str, optional_usize helper functions entirely - Added agent-specific param structs: AgentCreateNodeParams, AgentUpdateNodeParams, AgentGetNodeParams, CreateRelationshipParams, GetRelatedNodesParams, FindSkillsParams, UpdateTaskStatusParams - All exec_* functions now use serde_json::from_value::<ParamStruct>(args) with ToolError::InvalidArguments on parse failure - exec_search_nodes → SearchNodesParams (shared via mcp::params) - exec_search_semantic → SearchSemanticParams (shared via mcp::params) - exec_get_node → AgentGetNodeParams (includes optional format field) - exec_create_node → AgentCreateNodeParams (title+body agent convention) - exec_update_node → AgentUpdateNodeParams (title+body agent convention) - exec_create_relationship → CreateRelationshipParams - exec_get_related_nodes → GetRelatedNodesParams - exec_find_skills → FindSkillsParams - exec_update_task_status → UpdateTaskStatusParams - Flat unknown-key promotion into properties preserved for create/update ### 🟡 Finding 5: serde(alias = "id") not added to GetNodeParams etc. - Added #[serde(alias = "id")] to node_id in GetNodeParams, UpdateNodeParams, DeleteNodeParams in packages/core/src/mcp/handlers/nodes.rs ### Additional: Fixed pre-existing test asserting wrong behavior - test_handle_query_nodes_filters_unsupported_operator_ignored was written before PR #1044 added strict operator validation. Renamed and flipped assertion to match the correct behavior: title + equals => Err (only 'contains' is supported for title/content fields). Co-Authored-By: Claude <noreply@anthropic.com>
… add serde aliases - Added `pub mod params;` to packages/core/src/mcp/mod.rs - Defined `SearchNodesParams` struct in packages/core/src/mcp/handlers/search.rs - Matches the agent's search_nodes tool schema (query, node_type, limit) - Rebased feature branch onto origin/main; WIP commit dropped (already upstream) - Also fixed pre-existing compile error in tools.rs: get_tool_schemas signature mismatch (schema_ids: &[String] vs callers passing &[SchemaNode]) — updated to accept &[SchemaNode] and emit dynamic tool descriptions when user schemas exist - Updated tools_test.rs unit tests to construct SchemaNode test fixtures - Removed require_str, optional_str, optional_usize helper functions entirely - Added agent-specific param structs: AgentCreateNodeParams, AgentUpdateNodeParams, AgentGetNodeParams, CreateRelationshipParams, GetRelatedNodesParams, FindSkillsParams, UpdateTaskStatusParams - All exec_* functions now use serde_json::from_value::<ParamStruct>(args) with ToolError::InvalidArguments on parse failure - exec_search_nodes → SearchNodesParams (shared via mcp::params) - exec_search_semantic → SearchSemanticParams (shared via mcp::params) - exec_get_node → AgentGetNodeParams (includes optional format field) - exec_create_node → AgentCreateNodeParams (title+body agent convention) - exec_update_node → AgentUpdateNodeParams (title+body agent convention) - exec_create_relationship → CreateRelationshipParams - exec_get_related_nodes → GetRelatedNodesParams - exec_find_skills → FindSkillsParams - exec_update_task_status → UpdateTaskStatusParams - Flat unknown-key promotion into properties preserved for create/update - Added #[serde(alias = "id")] to node_id in GetNodeParams, UpdateNodeParams, DeleteNodeParams in packages/core/src/mcp/handlers/nodes.rs - test_handle_query_nodes_filters_unsupported_operator_ignored was written before PR #1044 added strict operator validation. Renamed and flipped assertion to match the correct behavior: title + equals => Err (only 'contains' is supported for title/content fields). Co-Authored-By: Claude <noreply@anthropic.com>
…1068) * Implement argument parsing unification - Phase 1 ## Completed in This Session - [x] Created shared params module (packages/core/src/mcp/params.rs) - [x] Added serde(alias='id') to GetNodeParams, UpdateNodeParams, DeleteNodeParams - [x] Added SearchNodesParams struct for search_nodes tool - [x] Updated mcp/mod.rs to export param types - [x] Updated agent tool imports to use shared param structs ## Infrastructure in Place The foundation for unified argument parsing is ready. The shared param module provides a single source of truth for deserialization logic that works for both MCP handlers and agent tool executor. ## Next Steps for Continuation Implement Phase 2 by systematically replacing each exec_* function's manual field extraction (require_str, optional_str, optional_usize) with serde deserialization using the shared param structs. Functions to update: - exec_search_nodes → SearchNodesParams - exec_search_semantic → SearchSemanticParams - exec_get_node → GetNodeParams - exec_update_node → UpdateNodeParams Co-Authored-By: Claude <noreply@anthropic.com> * Address review findings: complete Phase 2 unification, fix dead code, add serde aliases - Added `pub mod params;` to packages/core/src/mcp/mod.rs - Defined `SearchNodesParams` struct in packages/core/src/mcp/handlers/search.rs - Matches the agent's search_nodes tool schema (query, node_type, limit) - Rebased feature branch onto origin/main; WIP commit dropped (already upstream) - Also fixed pre-existing compile error in tools.rs: get_tool_schemas signature mismatch (schema_ids: &[String] vs callers passing &[SchemaNode]) — updated to accept &[SchemaNode] and emit dynamic tool descriptions when user schemas exist - Updated tools_test.rs unit tests to construct SchemaNode test fixtures - Removed require_str, optional_str, optional_usize helper functions entirely - Added agent-specific param structs: AgentCreateNodeParams, AgentUpdateNodeParams, AgentGetNodeParams, CreateRelationshipParams, GetRelatedNodesParams, FindSkillsParams, UpdateTaskStatusParams - All exec_* functions now use serde_json::from_value::<ParamStruct>(args) with ToolError::InvalidArguments on parse failure - exec_search_nodes → SearchNodesParams (shared via mcp::params) - exec_search_semantic → SearchSemanticParams (shared via mcp::params) - exec_get_node → AgentGetNodeParams (includes optional format field) - exec_create_node → AgentCreateNodeParams (title+body agent convention) - exec_update_node → AgentUpdateNodeParams (title+body agent convention) - exec_create_relationship → CreateRelationshipParams - exec_get_related_nodes → GetRelatedNodesParams - exec_find_skills → FindSkillsParams - exec_update_task_status → UpdateTaskStatusParams - Flat unknown-key promotion into properties preserved for create/update - Added #[serde(alias = "id")] to node_id in GetNodeParams, UpdateNodeParams, DeleteNodeParams in packages/core/src/mcp/handlers/nodes.rs - test_handle_query_nodes_filters_unsupported_operator_ignored was written before PR #1044 added strict operator validation. Renamed and flipped assertion to match the correct behavior: title + equals => Err (only 'contains' is supported for title/content fields). Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Michael Libio <malibio@Michaels-Mac-mini.local> Co-authored-by: Claude <noreply@anthropic.com>
* feat: complete query system — property filters, editor, and preferences (#1036, #442, #443) Completes the remaining 3 issues from Epic #444 (Query and Listing System): **#1036 — Property filter support in query_nodes MCP handler:** - Expand MCP tool schema to accept any field name and operators: equals, not_equals, contains, starts_with, ends_with - Map MCP filters to PropertyFilter with JSONPath construction - Apply property filters in-memory in NodeService after DB query + migration - Return clear errors for unsupported operators and invalid field/operator combinations - 9 new integration tests covering all filter operators and edge cases **#442 — QueryEditor with JSON Textarea Interface:** - New query-editor.svelte component with JSON textarea, validation, and error display - Query template examples (incomplete tasks, keyword search, priority filter) - Integrated into QueryNodeViewer with edit mode toggle - 33 tests covering validation, save/cancel flows, and templates **#443 — QueryPreferencesService for User View Preferences:** - New QueryPreferences type and QueryPreferencesService with write-through cache - localStorage persistence with defensive parsing and fallback to defaults - View tab UI in QueryNodeViewer (List/Table/Kanban) with preference persistence - 21 tests covering cache behavior, storage, and error handling Co-Authored-By: Claude <noreply@anthropic.com> * fix: address all 10 code review findings from PR #1044 Addresses all reviewer recommendations: 1. Preview feature (#442 AC): Added onPreview callback to QueryEditor with result count display; wired up in QueryNodeViewer via backendAdapter.queryNodes 2. Silent save failure: Added saveError state to QueryNodeViewer, displayed as alert banner above the editor when save fails 3. $effect overwrites user edits: Changed from reactive $effect sync to one-time init pattern using initialized flag — parent re-derivations no longer overwrite in-progress textarea edits 4. Unbounded in-memory fetch: Added 10,000-node safety cap when property filters bypass the DB limit (PROPERTY_FILTER_FETCH_CAP) 5. Duplicate filter field: Changed from tracing::warn to MCPError::invalid_params for consistency with other validation errors 6. Comment/code mismatch: Rewrote misleading comment about effective_limit logic 7. QueryDefinition in .svelte file: Moved QueryDefinition, DEFAULT_QUERY, and QUERY_TEMPLATE_EXAMPLES to $lib/types/query.ts; component and viewer import from shared location 8. Test logic duplication: Tests now import QueryDefinition, DEFAULT_QUERY, and QUERY_TEMPLATE_EXAMPLES from $lib/types/query instead of duplicating them 9. unwrap_or on strip_prefix: Replaced with explicit match that logs warning and returns false instead of silently falling through 10. Spread-with-conditional pattern: Simplified to direct property assignment Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Michael Libio <malibio@Michaels-Mac-mini.local> Co-authored-by: Claude <noreply@anthropic.com>
…1068) * Implement argument parsing unification - Phase 1 ## Completed in This Session - [x] Created shared params module (packages/core/src/mcp/params.rs) - [x] Added serde(alias='id') to GetNodeParams, UpdateNodeParams, DeleteNodeParams - [x] Added SearchNodesParams struct for search_nodes tool - [x] Updated mcp/mod.rs to export param types - [x] Updated agent tool imports to use shared param structs ## Infrastructure in Place The foundation for unified argument parsing is ready. The shared param module provides a single source of truth for deserialization logic that works for both MCP handlers and agent tool executor. ## Next Steps for Continuation Implement Phase 2 by systematically replacing each exec_* function's manual field extraction (require_str, optional_str, optional_usize) with serde deserialization using the shared param structs. Functions to update: - exec_search_nodes → SearchNodesParams - exec_search_semantic → SearchSemanticParams - exec_get_node → GetNodeParams - exec_update_node → UpdateNodeParams Co-Authored-By: Claude <noreply@anthropic.com> * Address review findings: complete Phase 2 unification, fix dead code, add serde aliases - Added `pub mod params;` to packages/core/src/mcp/mod.rs - Defined `SearchNodesParams` struct in packages/core/src/mcp/handlers/search.rs - Matches the agent's search_nodes tool schema (query, node_type, limit) - Rebased feature branch onto origin/main; WIP commit dropped (already upstream) - Also fixed pre-existing compile error in tools.rs: get_tool_schemas signature mismatch (schema_ids: &[String] vs callers passing &[SchemaNode]) — updated to accept &[SchemaNode] and emit dynamic tool descriptions when user schemas exist - Updated tools_test.rs unit tests to construct SchemaNode test fixtures - Removed require_str, optional_str, optional_usize helper functions entirely - Added agent-specific param structs: AgentCreateNodeParams, AgentUpdateNodeParams, AgentGetNodeParams, CreateRelationshipParams, GetRelatedNodesParams, FindSkillsParams, UpdateTaskStatusParams - All exec_* functions now use serde_json::from_value::<ParamStruct>(args) with ToolError::InvalidArguments on parse failure - exec_search_nodes → SearchNodesParams (shared via mcp::params) - exec_search_semantic → SearchSemanticParams (shared via mcp::params) - exec_get_node → AgentGetNodeParams (includes optional format field) - exec_create_node → AgentCreateNodeParams (title+body agent convention) - exec_update_node → AgentUpdateNodeParams (title+body agent convention) - exec_create_relationship → CreateRelationshipParams - exec_get_related_nodes → GetRelatedNodesParams - exec_find_skills → FindSkillsParams - exec_update_task_status → UpdateTaskStatusParams - Flat unknown-key promotion into properties preserved for create/update - Added #[serde(alias = "id")] to node_id in GetNodeParams, UpdateNodeParams, DeleteNodeParams in packages/core/src/mcp/handlers/nodes.rs - test_handle_query_nodes_filters_unsupported_operator_ignored was written before PR #1044 added strict operator validation. Renamed and flipped assertion to match the correct behavior: title + equals => Err (only 'contains' is supported for title/content fields). Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Michael Libio <malibio@Michaels-Mac-mini.local> Co-authored-by: Claude <noreply@anthropic.com>
Closes #1036