Repository navigation
Leave the built-in schemas out of the default Knowledge search scope (closes #3565) - #3576
Conversation
326e9c4 to
dd2a987
Compare
Pragmatic code review (medium depth): fresh reviewer agent,
|
Address review (commit d17612f)Addressed: 2 improvements and 4 nits
Skipped: 2
Checks
Re-Review DecisionDecision: RE-REVIEW NEEDED Rationale: The fetch window is a logic change in 🤖 Generated with Claude Code |
Re-review of d17612f (medium depth): fresh reviewer agent,
|
| Search | Max working limit before | Max working limit now | Newly failing |
|---|---|---|---|
No filters, Knowledge scope |
136 | 128 | 129-136 |
With property_filters, default scope |
45 | 37 | 38-45 |
Who can hit it. Neither the CLI's --limit nor the daemon puts an upper bound on the limit, and search_ops accepts up to 1000. So nodespace search "x" --filters … --limit 40 worked before this PR and now fails with "Search failed". The GUI path (include_title_matches) would instead quietly fall back to keyword-only results.
Root cause. The bug already existed: limits 137-1000 fail on main today. This PR moves the threshold down, and it will keep moving as more core types ship.
Fix. Clamp in search_embeddings: let k = (limit * EMBEDDING_KNN_OVERFETCH).max(limit).min(VEC0_K_MAX); with VEC0_K_MAX = 4096 next to EMBEDDING_KNN_OVERFETCH. It is one line, and it also fixes the existing failures at higher limits.
[Improvement] tests/it/embedding_service_test.rs:2236-2256: the gate test can fail even when the widening works.
- Two ways it can fail with the widening intact:
personis inKNOWLEDGE_CORE_TYPES, so it already takes one of the two result slots. Any further Knowledge-type node seeded before the note pushes the note out.- The window is N+6 rows and the note sits at index N+3, leaving 2 rows of slack. Three more non-Knowledge seeds push the note past the window, for example more core Plays.
- How it can go quiet instead: if seeding order changes or
enumerate_nodesgains anORDER BY, the test passes with or without the widening, without any warning. - Suggested fix (cheap):
- Compare the limit-2 result with the first two ids of the same untyped search at limit 1000.
- Add a precondition that the first
3 × limitrows of a rawquery_nodesenumerate are all built-in schemas, so a loss of sensitivity fails loudly instead of passing quietly.
Nitpicks
- Nit:
models/schema_node.rs:139-141. "The one definition of the test … so they cannot disagree" overstates it.from_storagestill does its ownflag("isCore")(schema_node.rs:57), and that feedsSchemaNode::is_core, whichsearch_ops,schema/mod.rsandskill_opsread. The SQL trigger (db/schema.rs:529-531) is a necessary mirror of the same test.- Option 1: in
from_storage, uselet is_core = is_core_schema(&node);. - Option 2: reword the doc to name the trigger as the SQL copy that must stay in step.
- Option 1: in
🤖 Generated with Claude Code
d17612f to
dec2bb6
Compare
Address re-review (commit dec2bb6, rebased onto main d7d2f69)Addressed: 2 improvements, 1 nit, the merge conflict
Checks
Re-Review DecisionDecision: RE-REVIEW NEEDED (focused) Rationale: The 🤖 Generated with Claude Code |
Focused re-review of dec2bb6 (medium depth): fresh reviewer agent,
|
Address focused re-review (commit 86fa937)Addressed: both nits
Checks on the final tree (86fa937, MERGEABLE against main)
Mutation summary across the PR (every one fails a test)
Re-Review DecisionDecision: NO RE-REVIEW NEEDED Rationale: Both changes are test-only (a test input and an assertion message), and both are mutation-checked. All three reviews returned APPROVE. 🤖 Generated with Claude Code |
…3565) KNOWLEDGE_CORE_TYPES no longer lists Schema. Every schema is embedded for skill and schema retrieval, so with schema in the default scope an untyped search returned type definitions (Checkbox, Horizontal Line, ...) ahead of the user's notes. A search that names the schema type still returns them (should_skip_scope_filter), and find_skills searches the schema type directly, so neither path changes. Tests: - the Knowledge-scope unit test now lists schema as outside the scope - a gate-run enumerate test: no schema in an untyped search, core and user-defined schemas when node_types names schema - an ignored live test with the real embedding model: the relevant note ranks first and no schema is returned for "Hall Nine" and "marketing budget"; node_types: ["schema"] and find_skills still find schemas Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…in ones KNOWLEDGE_CORE_TYPES lists Schema again (ADR-029 §5 keeps schema in Knowledge). matches_scope now takes the node and, for Knowledge, refuses a built-in schema: a schema node whose row carries isCore, read by the new schema_node::is_core_schema (the flag from_storage reads into SchemaNode::is_core). The ~40 built-in schemas that are now embedded no longer flood an untyped search, and a user-defined schema such as Venue is still found there by meaning. Everything, Custom and Conversations are unchanged, a search that names schema replaces the scope as before, and find_skills searches the schema type directly. Tests: - unit: a user-defined schema is in Knowledge, a built-in one is not, a non-schema node carrying isCore is unaffected, Everything keeps built-ins - gate-run enumerate: untyped returns the note and the user schema and no built-in schema; node_types: ["schema"] returns both kinds - live (real model): untyped "Hall Nine" / "marketing budget" rank the note first with no built-in schema; untyped "venue capacity" returns Venue; --type schema returns checkbox and Venue; find_skills finds Venue from a query that does not name it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…emas - schema_node::is_core_schema is now the only definition of the test. The private NodeService::is_core_schema is removed; the core-status and create guards and the store's delete refusal call the shared helper. - When the Knowledge scope applies, search_semantic adds the number of built-in schemas to its over-fetch. The scope drops them after the fetch, so a small limit (the local agent's search uses 5) could come back empty when the user's note ranked behind them. The schema list that gives user_types is now read before the fetch and also gives that count. - New gate-run test: an untyped limit-2 search still returns the user's note behind the seeded built-in schemas (it returned [] without the widening). - Doc nits: SearchScope::Knowledge mentions the exclusion, the matches_scope doc says naming schema bypasses it only when no scope is requested, and it no longer links into the crate-private schema_node. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…imit test - search_embeddings clamps k to VEC0_K_MAX (4096, sqlite-vec's SQLITE_VEC_VEC0_K_MAX). k is limit * 10 and callers over-fetch on top, so the widened window lowered the limit at which a search failed with "k value in knn query too large"; limits past ~136 already failed on main. New store test: a limit-1000 search succeeds and finds the node. - from_storage reads isCore through schema_node::is_core_schema, so every Rust reader of the flag shares the one test; its doc names the schema_core_status_fixed trigger as the SQL mirror. - The small-limit search test compares the limit-2 result with the first results of an exhaustive search instead of expecting the note, and asserts its precondition (the first 3 x limit rows are built-in schemas) so a seeding change fails it loudly rather than leaving it vacuous. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…in the small-limit test - The store test searches with a limit past sqlite-vec's cap (5000), so a clamp applied before raising k to the limit fails it too. - The small-limit search test's message names both kinds of row the scope drops (built-in schemas or system types). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
86fa937 to
71f6249
Compare
Summary
Closes #3565 (release blocker), with the acceptance criteria as revised on the issue.
Since #3538 embeds every schema for fetch-by-task, the ~40 built-in schemas ("Checkbox", "Horizontal Line", ...) flooded untyped search:
nodespace search "Hall Nine"returned the note plus 13 schemas. In v0.3.3 the core schemas were never queued for embedding, so this never showed up.The default
Knowledgescope now leaves out built-in schemas only:KNOWLEDGE_CORE_TYPESkeepsSchema. ADR-029 §5 keepsschemainKnowledge, so a user-defined schema such as "Venue" is still found there by meaning.NodeEmbeddingService::matches_scopenow takes the node. ForKnowledgeit refuses a built-in schema, identified by the newschema_node::is_core_schema: an exactschemanode whose row carriesisCore. That is the flagfrom_storagereads intoSchemaNode::is_core, and the onesearch_opsalready uses to builduser_types.find_skillsand--type schema.Unchanged:
--type schema/node_types: ["schema"]: named types replace the default scope (should_skip_scope_filter).find_skills: it searches theschematype directly and never consults the scope.everythingscope (desktop search box),CustomandConversations.Docs: NodeSpaceAI/nodespace-docs#100 adds one data-layer.md clarification. ADR-029 is not edited, and the earlier amendment PR (nodespace-docs#99) is closed.
Tests
test_knowledge_scope_admits_user_knowledge_and_excludes_system_types):Knowledgeand a built-in one is not;isCoreis unaffected;Everythingkeeps built-ins.embedding_service_test::test_search_semantic_default_scope_leaves_core_schemas_out_and_keeps_user_schemas):"*"returns the note and the user-definedVenueschema, and no built-in schema;node_types: ["schema"]returnscheckboxandVenue.default_scope_schema_search_live_test,#[ignore]likefind_skills_schema_discovery_test):Venue;--type schemareturnscheckboxandVenue;find_skillsfindsVenuefrom a query that doesn't name it, so the name-match backstop can't be what satisfies the assertion.Mutation checks (each failed as noted, then restored):
Venuemissing).find_skillsreturns no schema hits: the live test fails.is_core_schemaignores the node type: the unit test fails.Checks:
--run-ignored all.cargo clippy -p nodespace-core --all-targets -D warningsis clean, rebased on main after Fix clippy collapsible_match errors on main #3571.bun run test:changed: result to follow in a comment.🤖 Generated with Claude Code