* fix(graph): handle qualified C++ symbols Signed-off-by: trvon <me@trevon.dev> * refactor(vector)!: remove dead transition APIs Split Simeon vector and retrieval linkage so consumers inherit only the runtime surface they use. BREAKING CHANGE: remove unused async/model/config helpers and the undefined vector-backend factory from public headers. Signed-off-by: trvon <me@trevon.dev> * fix(vector): harden PQ snapshot lifecycle Validate and atomically install persisted generations, revalidate after races, and fall back to exact search when a snapshot is stale. Add the exact-scan reference lane and distinguish observed work counters from unavailable backend metrics. Signed-off-by: trvon <me@trevon.dev> * feat(topology): expose route coordinates Persist chart distortion and local-dimension evidence, retain pre-scalarization route axes, and emit detailed trace rows without treating unavailable vector work as zero. Signed-off-by: trvon <me@trevon.dev> * feat(xplan): add vector decision gates Track binary and corpus identity, preserve missing-work semantics, and add repeated PQ routing, crossover, and rerank plans with quality and cost summaries. Signed-off-by: trvon <me@trevon.dev> * feat(topology): formalize coordinate routing Separate chart geometry from route-risk admission and prove candidate-stage recall and strict-focus boundaries. Record the measured atlas limitations and literature basis for the next construction experiment. Signed-off-by: trvon <me@trevon.dev> * fix(search): serialize SymSpell readers SQLite-backed lookups reuse mutable prepared statements, so shared-reader locking is unsafe. Advance the owned SymSpell revision for batched delete insertion and cover concurrent root lookups under TSAN. Signed-off-by: trvon <me@trevon.dev> * fix(vector): make mutations atomic Preserve stored vectors on replacement and commit failures, reject cross-thread writes during explicit transactions, and restore exact reranking for quantized-primary PQ rows. * fix(search)!: surface SymSpell write errors Propagate persistent dictionary failures through metadata ingestion, roll back failed batches, and make clear remove stored terms. BREAKING CHANGE: SymSpellSearch mutations now return Result values. * feat(topology): formalize candidate expansion * feat(vector): add coordinate-aware embeddings * feat(topology): preserve protected relation cover * feat(search): add graph candidate rescue * feat(daemon): expose retrieval hydration telemetry * feat(xplan): add rescue and hydration gates * refactor(content): dedupe media handlers Reuse shared graph presenter helpers and centralize repeated audio/video/archive buffer processing, validation, lifecycle, and stats helper logic. * feat(lifecycle): prune stale corpus data Keep cleanup preview-first while retention stays opt-in and long prune work no longer holds IPC responses open. * feat(search): add topology candidate rescue Preserve high-value routed candidates across vector backends while keeping exact rerank diagnostics explicit. * feat(search): expose evidence paths * docs: define mobile host contract * formal: model bounded candidate rescue * fix(prune): inspect extensionless signatures * fix(prune): tolerate stale extensions --------- Signed-off-by: trvon <me@trevon.dev>
131 lines
5 KiB
C++
131 lines
5 KiB
C++
// Copyright (c) 2025 YAMS Contributors
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
#include <catch2/catch_test_macros.hpp>
|
|
|
|
#include <yams/cli/graph_explore_presenter.h>
|
|
|
|
#include <filesystem>
|
|
#include <sstream>
|
|
|
|
TEST_CASE("Graph explore presenter maps daemon response to app response", "[cli][graph]") {
|
|
yams::daemon::GraphExploreResponse in;
|
|
in.query = "processTask";
|
|
in.totalSymbolsConsidered = 2;
|
|
in.totalFilesConsidered = 1;
|
|
in.emittedChars = 32;
|
|
in.snippetRenderMicros = 123;
|
|
in.snippetsRendered = 1;
|
|
in.kgAvailable = true;
|
|
in.truncated = false;
|
|
in.warnings.push_back("fallback warning");
|
|
|
|
yams::daemon::GraphExploreSymbol symbol;
|
|
symbol.nodeKey = "function:demo::processTask@/repo/src/process.cpp";
|
|
symbol.label = "processTask";
|
|
symbol.qualifiedName = "demo::processTask";
|
|
symbol.kind = "function";
|
|
symbol.filePath = "/repo/src/process.cpp";
|
|
symbol.startLine = 10;
|
|
symbol.endLine = 12;
|
|
symbol.score = 99.0;
|
|
symbol.exactMatch = true;
|
|
in.entrySymbols.push_back(symbol);
|
|
|
|
yams::daemon::GraphExploreSnippet omittedSnippet;
|
|
omittedSnippet.filePath = symbol.filePath;
|
|
omittedSnippet.language = "cpp";
|
|
omittedSnippet.mode = "omitted";
|
|
omittedSnippet.startLine = 10;
|
|
omittedSnippet.endLine = 12;
|
|
omittedSnippet.heading = "process.cpp";
|
|
omittedSnippet.content = "";
|
|
omittedSnippet.truncated = true;
|
|
omittedSnippet.symbols.push_back(symbol);
|
|
in.files.push_back(omittedSnippet);
|
|
|
|
yams::daemon::GraphExploreSnippet fullSnippet;
|
|
fullSnippet.filePath = "/repo/src/helper.cpp";
|
|
fullSnippet.language = "cpp";
|
|
fullSnippet.mode = "full";
|
|
fullSnippet.startLine = 20;
|
|
fullSnippet.endLine = 24;
|
|
fullSnippet.heading = "helper.cpp";
|
|
fullSnippet.content = "20\tint helper() {\n21\t return 0;\n22\t}\n";
|
|
in.files.push_back(fullSnippet);
|
|
|
|
yams::daemon::GraphExploreRelation relation;
|
|
relation.relation = "calls";
|
|
relation.sourceNodeKey = symbol.nodeKey;
|
|
relation.sourceLabel = "processTask";
|
|
relation.targetNodeKey = "function:demo::helper@/repo/src/helper.cpp";
|
|
relation.targetLabel = "helper";
|
|
relation.weight = 1.0F;
|
|
relation.confidence = 1.0;
|
|
relation.provenance = "treesitter";
|
|
in.relationships.push_back(relation);
|
|
|
|
const auto out = yams::cli::mapGraphExploreResponseFromDaemon(in);
|
|
|
|
CHECK(out.query == in.query);
|
|
CHECK(out.snippetRenderMicros == 123);
|
|
CHECK(out.snippetsRendered == 1);
|
|
REQUIRE(out.entrySymbols.size() == 1);
|
|
CHECK(out.entrySymbols.front().qualifiedName == "demo::processTask");
|
|
REQUIRE(out.files.size() == 2);
|
|
CHECK(out.files.front().mode == yams::app::services::GraphContextSnippetMode::Omitted);
|
|
CHECK(out.files.back().mode == yams::app::services::GraphContextSnippetMode::Full);
|
|
REQUIRE(out.relationships.size() == 1);
|
|
REQUIRE(out.relationships.front().provenance.has_value());
|
|
CHECK(*out.relationships.front().provenance == "treesitter");
|
|
}
|
|
|
|
TEST_CASE("Graph explore presenter renders json and markdown", "[cli][graph]") {
|
|
yams::app::services::GraphExploreResponse response;
|
|
response.query = "processTask";
|
|
|
|
yams::app::services::GraphContextSymbol symbol;
|
|
const auto cwd = std::filesystem::current_path();
|
|
symbol.nodeKey = "function:demo::processTask@" +
|
|
(cwd / "src" / "cli" / "commands" / "graph_command.cpp").string();
|
|
symbol.label = "processTask";
|
|
symbol.qualifiedName = "demo::processTask";
|
|
symbol.kind = "function";
|
|
symbol.filePath = (cwd / "src" / "cli" / "commands" / "graph_command.cpp").string();
|
|
symbol.startLine = 1;
|
|
symbol.endLine = 2;
|
|
symbol.exactMatch = true;
|
|
response.entrySymbols.push_back(symbol);
|
|
|
|
yams::app::services::GraphContextSnippet snippet;
|
|
snippet.filePath = symbol.filePath;
|
|
snippet.language = "cpp";
|
|
snippet.mode = yams::app::services::GraphContextSnippetMode::Full;
|
|
snippet.startLine = 1;
|
|
snippet.endLine = 2;
|
|
snippet.heading = "graph_command.cpp";
|
|
snippet.content = "1\tint processTask() {\n2\t return 1;\n";
|
|
snippet.symbols.push_back(symbol);
|
|
response.files.push_back(snippet);
|
|
|
|
yams::app::services::GraphContextRelation relation;
|
|
relation.relation = "calls";
|
|
relation.sourceLabel = "processTask";
|
|
relation.targetLabel = "helper";
|
|
response.relationships.push_back(relation);
|
|
response.snippetRenderMicros = 123;
|
|
response.snippetsRendered = 1;
|
|
|
|
const auto json = yams::cli::makeGraphExploreJson(response);
|
|
CHECK(json["files"][0]["mode"] == "full");
|
|
CHECK(json["entrySymbols"][0]["label"] == "processTask");
|
|
CHECK(json["snippetRenderMicros"] == 123);
|
|
CHECK(json["snippetsRendered"] == 1);
|
|
|
|
std::ostringstream out;
|
|
yams::cli::renderGraphExploreMarkdown(out, response, cwd);
|
|
const auto rendered = out.str();
|
|
CHECK(rendered.find("Graph Explore") != std::string::npos);
|
|
CHECK(rendered.find("processTask --calls--> helper") != std::string::npos);
|
|
CHECK(rendered.find("src/cli/commands/graph_command.cpp") != std::string::npos);
|
|
}
|