Commit graph

1,853 commits

Author SHA1 Message Date
trvon
5b88c11e8f
fix(metadata): scope migration 40 deletes to symbol extractor output
Migration 40 deleted every kg_edges row whose relation was calls,
implements, includes, ... and every node of a symbol type such as class or
interface. Graph ingest through MCP and IPC accepts any type and relation,
so an upgrade from v0.19 irreversibly removed user-modelled data like a
'class' node or an 'implements' edge.

Select the extractor's nodes by provenance and key shape instead
(<kind>:<qualified>@<file>[@snap:<hash>], symbol_ref:<name>, or nodes its
kg_doc_entities rows reference) and delete edges that are tagged with the
extractor or touch those nodes. The new test fails before the change.
2026-09-23 17:26:09 -06:00
Trevon
d3f1eede21
Merge pull request #188 from trvon/experimental
chore: promote experimental to main
2026-09-23 08:05:37 -06:00
trvon
fe7aad5ee7
docs(agents): document PR stacking, promotion, and submodule merge rules
A stacked PR (#187) merged into its parent branch after the parent (#186)
had already merged into experimental, so its commits missed experimental
and the promotion PR until #189 carried them over. Record the merge order,
a git cherry check for stranded commits, and the no-squash rule for
submodule PRs.
2026-09-23 08:03:02 -06:00
Trevon
eb9ff8c26a
Merge pull request #189 from trvon/feat/graph-topology
perf: carry #187 (SIMD fixes, x86 AVX2 dispatch, opt-in ONNX, Linux fixes) into experimental
2026-09-23 08:02:09 -06:00
Trevon
14caf491af
Merge pull request #187 from trvon/perf/simd-release-build
perf: SIMD fixes (ARM ODR, x86 AVX2 runtime dispatch), opt-in ONNX, Linux fixes
2026-09-23 07:59:45 -06:00
Trevon
b0ffee74c5
Merge pull request #186 from trvon/feat/graph-topology
Some checks failed
Fuzz Smoke / libFuzzer smoke (push) Has been cancelled
feat(graph): mutual-kNN semantic edges, topology clusters in the KG, relational richness gate
2026-09-23 07:59:32 -06:00
Trevon
4bd0d84267
Merge branch 'experimental' into feat/graph-topology 2026-09-23 07:59:17 -06:00
Trevon
21bcd0aeed
Merge pull request #183 from trvon/fix/topology-release-hardening
Some checks failed
Secret scan / gitleaks (push) Failing after 11s
Mirror source to Forgejo / mirror (push) Has been skipped
Release Candidate / Validate promotion and preview release (push) Has been cancelled
Tests / Static Analysis (push) Has been cancelled
Tests / Warm Conan & ccache (linux-arm64) (push) Has been cancelled
Tests / Warm Conan & ccache (macos-arm64) (push) Has been cancelled
Tests / Warm Conan & ccache (linux-x86_64) (push) Has been cancelled
Tests / Warm Conan & ccache (macos-x86_64) (push) Has been cancelled
Tests / Warm Conan & ccache (windows-x86_64) (push) Has been cancelled
Tests / warm (push) Has been cancelled
Tests / Tests (linux-arm64) (push) Has been cancelled
Tests / Tests (macos-arm64) (push) Has been cancelled
Tests / Tests (linux-x86_64-asan) (push) Has been cancelled
Tests / Tests (linux-x86_64-ubsan) (push) Has been cancelled
Tests / Tests (linux-x86_64) (push) Has been cancelled
Tests / Tests (windows-x86_64) (push) Has been cancelled
fix(topology): harden snapshot codec, store, migrations and routing for v0.20
2026-09-23 07:58:24 -06:00
trvon
2cff3982c6
fix(vector): bump sqlite-vec-cpp for safe error formatting
0fec31c: sqlite3_mprintf error paths used %zu, which SQLite parses as a
string conversion and crashed on invalid vec0 dimensions or corrupt HNSW
persistence; also corrects a RaBitQ test expectation.
2026-09-22 18:58:23 -06:00
trvon
76a0fd7e65
test(daemon): remove host-dependent assumptions from two Linux failures
- config_resolver: several [tuning] defaults derive from core count (e.g.
  onnx_max_concurrent is 7 on a 48-thread host), so a reload is now checked
  against the values observed before the override instead of != sentinel.
- service_manager: write the P2P trust files with explicit permissions so
  a umask of 002 (Debian user-private groups) no longer trips the
  group-writable check before the symbolic-link check under test.
2026-09-22 18:56:36 -06:00
trvon
b2a5d6131b
fix(integrity): publish verifier pool shutdown under the queue mutex
~VerificationThreadPool set stop without holding queueMutex, so a worker
that had evaluated the wait predicate but not yet blocked could miss
notify_all() and join() hung forever (integrity_catch2 timed out under
parallel load; gdb showed join() waiting on workers parked in wait()).
Set the flag under the mutex. The new shutdown churn test hung in 35/40
parallel runs before the fix and passes 200/200 full-suite runs after.
2026-09-22 18:56:24 -06:00
trvon
66e59d305f
build: make the deprecated ONNX Runtime integration opt-in
Default builds no longer pull onnxruntime: conan enable_onnx and the conan
profiles default to False, meson enable-onnx defaults to disabled and skips
onnxruntime discovery entirely, and setup.sh/setup.ps1 only enable ONNX
(and the ONNX-backed Glint plugin) with --with-onnx / -WithOnnx.
YAMS_DISABLE_ONNX=true still forces it off.

This removes a build blocker on hosts where conan tries to build
onnxruntime from source (e.g. ROCm/MIGraphX on Linux). Release, Docker, and
Arch packaging pass the flag explicitly, so shipped artifacts are unchanged
until ONNX is dropped.
2026-09-22 18:56:22 -06:00
trvon
2824cb8a44
perf(vector): use AVX2 on x86 through runtime CPU dispatch
Bump simeon to 38a0b39 (active_simd_tier() checks CPUID; pq.cpp kernels
follow it) and sqlite-vec-cpp to d4ce0cc (target-attribute AVX2+FMA float
distance kernels behind a CPUID check).

x86 builds now compile simeon's arch/avx2.cpp alone with -mavx2 -mfma and
define SIMEON_HAS_AVX2; every other TU keeps the baseline ISA, so generic
x86-64 binaries stay portable while AVX2 hosts use SIMD for encoding, PQ
training, and sqlite-vec distances (3-4x per distance call at 384d on a
Zen 2 host). Full unit suite on Linux x86 (clang, Debug): no new failures.
2026-09-22 18:21:08 -06:00
trvon
15df1a5064
test(policy): drop environment keys removed with the symbol pipeline
YAMS_POST_SYMBOL_CONCURRENT and YAMS_SYMBOL_WEIGHT no longer have
readers, so configuration_authority_policy flagged them as stale.
2026-09-22 17:39:17 -06:00
trvon
95f3c3ee92
test(policy): drop environment keys removed with the symbol pipeline
YAMS_POST_SYMBOL_CONCURRENT and YAMS_SYMBOL_WEIGHT no longer have
readers, so configuration_authority_policy flagged them as stale.
2026-09-22 17:39:17 -06:00
trvon
c42bd41221
refactor(graph): drop helpers orphaned by the code-symbol graph removal
looksLikePathQuery and nodeWithinScope in graph_context_service.cpp lost
their last callers with the symbol ABI and only produced -Wunused-function.
2026-09-22 17:26:14 -06:00
trvon
30d383aebf
refactor(graph): drop helpers orphaned by the code-symbol graph removal
looksLikePathQuery and nodeWithinScope in graph_context_service.cpp lost
their last callers with the symbol ABI and only produced -Wunused-function.
2026-09-22 17:26:14 -06:00
trvon
cddb9e7283
perf(vector): score Simeon PQ candidates with batched ADC scans
Bump simeon to c846635:
- fix(tokenizer): locale-independent character classes
- perf(pq): batched/gathered inner-product scans, bit-identical
- perf(projection): Achlioptas apply visits only non-zero sketch buckets
  (5.6x on short texts at 4096->384, bit-identical)

The PQ search scan now calls inner_product_batch/_gather instead of one
pimpl call per code. Scores are bit-identical, so ranking and tie-breaks
are unchanged (1.6-2.0x lower ADC scan time in simeon's pq_query_bench).
2026-09-22 17:22:44 -06:00
trvon
6cf2aea837
build(setup): profile optimized, uninstrumented builds
./setup.sh Profiling configured Meson as buildtype=debug (-O0) and turned
TSAN on by default, so Tracy zone timings described unoptimized,
instrumented code. Use debugoptimized and leave TSAN off unless --tsan is
passed.
2026-09-22 17:22:41 -06:00
trvon
c74629dd4e
fix(vector): build the topology ANN index with sqlite-vec's SIMD defines
static_cosine_ann_index.cpp instantiates sqlite-vec's header-only HNSW and
inner_product_distance templates but was compiled in yams_vector without
SQLITE_VEC_ENABLE_NEON, while the sqlite-vec library and backend TU were
compiled with it. The inline kernels therefore had two bodies (an ODR
violation): the ANN TU's copy was a serial scalar fadd chain, and the
linker could keep either. Build it in yams_sqlite_vec_backend, which
already carries the feature defines, and fail the build on AArch64 if the
defines are missing.
2026-09-22 17:22:36 -06:00
trvon
4ff7279c90
fix(search): report the scoring mode of the wired cross reranker
cross_rerank_simeon_outer_maxsim read SearchEngineConfig, but the daemon
resolves the Simeon scoring mode from reranker policy (ConfigResolver), so
the stat said 0 for every outer-MaxSim run and the scifact ablation could
not tell which scorer ran. setCrossReranker() now takes the mode label the
wiring resolved; the engine reports it as cross_rerank_scoring_mode and
derives the outer-MaxSim stat from it.
2026-09-22 17:22:30 -06:00
trvon
24b376aa6b
fix(daemon): drop stale symbol extractor adapter include
The header was deleted with the symbol_extractor_v1 ABI; builds only
passed where an installed copy under the system include path shadowed it.
2026-09-22 17:05:17 -06:00
trvon
f9a4ccce57
fix(daemon): drop stale symbol extractor adapter include
The header was deleted with the symbol_extractor_v1 ABI; builds only
passed where an installed copy under the system include path shadowed it.
2026-09-22 17:05:16 -06:00
trvon
e7bf9a05be
fix(bench): drop removed symbolRank option and tolerate duplicate-content corpora
nfcorpus contains byte-identical documents that collapse to one content
hash; skip repeats when building topology inputs instead of aborting.
2026-09-22 16:29:46 -06:00
trvon
e3f7e42492
feat(search): optional graph-rerank community signal from topology
Graph rerank recomputes connected components over mutual
semantic_neighbor edges among the candidates on every query, reading
each candidate's edges from the KG. The resident topology snapshot
already holds corpus-wide clusters built from those edges.

Add search.topology.graph_community_source (reciprocal_edges |
topology_snapshot). topology_snapshot scores candidates that share a
snapshot cluster with the same normalization and falls back to the edge
walk when no snapshot is loaded; graph_community_source in debug stats
reports which ran. The default stays reciprocal_edges until a benchmark
gate shows the snapshot source is at least as good. The retrieval bench
can set it for ablations.
2026-09-22 15:50:37 -06:00
trvon
14057afbf8
feat(topology): materialize clusters in the knowledge graph
Topology wrote back only an opaque compressed snapshot node, so yams
graph, explore and graph expansion could not traverse clusters, and
every rebuild added another full snapshot node that was never removed.

Each stored snapshot now replaces the cluster graph in one KG write
batch before the latest pointer flips: topology:cluster:<id> nodes with
size, level, persistence, cohesion and density; document member_of
edges weighted by persistence, medoid_of edges, overlaps edges for
boundary-spill memberships, and subcluster_of hierarchy edges. After the
pointer flips, snapshot nodes beyond the newest three are removed; the
one the pointer names is always kept.
2026-09-22 15:38:33 -06:00
trvon
1a74d4c73e
fix(graph): make semantic_neighbor reciprocity mean mutual kNN
Every kNN edge was also written in reverse, so each semantic_neighbor
edge looked reciprocal and topology's reciprocalOnly filtering, SGC
smoothing over reciprocal edges, and graph reciprocity metrics filtered
nothing. Write only the edge from a document to each of its k nearest
neighbours; a reverse edge now exists exactly when the pair is mutual.

Semantic maintenance pruned one-way edges on the assumption that edges
were symmetric; with directed kNN edges that would erase every
non-mutual neighbour, so it now removes only self-loops (and no longer
reports a fabricated community size for the scoped pass).

Edge reads truncated by insertion order: getEdgesBidirectional sorted by
id and getEdgesTo had no order, so a LIMIT kept arbitrary or oldest
edges. Both now return strongest first, and the topology extractor reads
a document's own neighbours separately from its incoming edges so a
hub's many incoming edges cannot evict them.

Existing databases keep their mirrored edges until the semantic
neighbour graph is rebuilt.
2026-09-22 15:32:59 -06:00
trvon
e31eda4b55
fix(search): measure graph richness from relational edges only
kgEdgeDensity counted every kg_edges row, including the ~16
semantic_neighbor edges per document that 8-NN embedding neighbours
write in both directions, plus file-lineage and entity-attachment edges.
Any embedded corpus therefore looked like a rich knowledge graph, so the
SCIENTIFIC-profile gate that should zero the KG leg on flat prose never
fired and graph richness scaled from embedding edges.

Add kgRelationalEdgeCount/Density, which exclude embedding kNN, file
lineage and layout, text segments, and entity-to-document attachments,
and use it for hasRichGraphTopology() and the tuner's richness scaling.
kgEdgeCount keeps its meaning as the reconciled-graph signal. The live
overlay carries the reconciled relational count forward.
2026-09-22 15:23:45 -06:00
trvon
899f0a1550
Merge branch 'test/p2p-fuzz-lane' into release/v0.20-integration 2026-09-22 15:15:06 -06:00
trvon
c766a17bd1
Merge branch 'refactor/remove-symbol-abi' into release/v0.20-integration
# Conflicts:
#	tests/scripts/portability_allowlist.txt
2026-09-22 15:14:55 -06:00
trvon
66689cd7a3
refactor(daemon): remove the symbol_concurrent tuning knob and stage metrics
The retired symbol stage never activates, so tuning.post_ingest.
symbol_concurrent and YAMS_POST_SYMBOL_CONCURRENT had no effect, and
status reported symbol_* queue metrics and post_symbol_limit that were
always zero. Remove the config key and environment override, the status
keys and CLI rows, the metrics snapshot fields, and the event-bus symbol
counters. The internal stage slot stays so budget and limiter indices
are unchanged; benchmark JSON keeps its symbol fields at zero.
2026-09-22 14:41:05 -06:00
trvon
0707331a8d
perf(graph): stop loading document content for completion-only jobs
Since v0.20 the graph stage only records knowledge-graph completion
(NL entities are written by the post-ingest title+NL stage), yet it
still read every supported document from the content store and copied
it into a job that ignored it, and logged each one at info level. Queue
the completion job without content.

The isolated-node graph query now defaults to the semantic_neighbor
relation instead of the removed calls edges, so it reports documents
with no embedding neighbours.
2026-09-22 14:41:00 -06:00
trvon
ac070d8d2a
refactor(search): remove symbol-based result ranking
SymbolEnricher boosted results by looking up code-symbol nodes that no
longer exist since v0.20, so it ran KG queries for the top results and
never boosted anything. Remove it, the YAMS_SYMBOL_WEIGHT variable, the
symbol_rank_* search stats, and the symbolRank option on the service,
retrieval, MCP and CLI request types. The IPC field stays in the wire
format but is ignored, and --symbol-rank/--no-symbol-rank remain
accepted as hidden no-ops so existing scripts keep working.
2026-09-22 14:40:52 -06:00
trvon
25d171dd62
ci(fuzz): run the fuzz lane on the self-hosted Forgejo runner
Fuzzing belongs on our own compute rather than GitHub-hosted runners.
Move the smoke job to .forgejo/workflows on the self-hosted label, pin
actions to code.forgejo.org, and add a preflight that fails clearly when
the runner lacks clang's libFuzzer runtime or the build tools.

Note that once .forgejo/workflows exists, Forgejo runs only those
workflows and stops executing the .github/workflows copies.
2026-09-22 14:17:16 -06:00
trvon
c6e960eed7
Merge branch 'test/p2p-fuzz-lane' into release/v0.20-integration 2026-09-22 13:55:37 -06:00
trvon
e6de1978c2
Merge branch 'refactor/remove-symbol-abi' into release/v0.20-integration 2026-09-22 13:55:37 -06:00
trvon
34b9f7346e
docs(p2p): describe the fuzz lane, its oracles and remaining gaps 2026-09-22 13:55:12 -06:00
trvon
6065798e65
ci(fuzz): run the P2P and topology harnesses on relevant changes
Build the libFuzzer targets with clang through setup.sh Fuzzing and run
each for 60 seconds over the generated seeds on pull requests touching
the P2P, codec or fuzzing sources, nightly, and on demand. Reproducers
are uploaded when a harness fails.
2026-09-22 13:55:10 -06:00
trvon
2a63eb1af8
test(fuzz): add a direct-P2P fuzz lane and a topology codec harness
New libFuzzer/AFL++ harnesses, each trapping on an oracle violation:
p2p_json (nesting bound, round trip), p2p_connstr (endpoint round trip,
pin idempotence, no silent re-pin), p2p_handshake (validators and the
full acceptor bind the TLS peer), p2p_delta_stream (batch, session and
bootstrap receive paths stay inside count and byte budgets), p2p_inbound
(the manager inbound session through registry update) and
topology_codec (decoded snapshots re-encode losslessly). p2p_delta now
draws its exchange limits from the input.

p2p_seeds.py writes framed-session seeds for FuzzedDataProvider layouts,
dicts/p2p.dict carries the frame vocabulary, generate_corpus.sh accepts a
local seedgen, and fuzz.sh no longer fails on macOS bash 3.2 when a
target has no dictionary. The generated corpus stays untracked.
2026-09-22 13:55:04 -06:00
trvon
b702cb47e1
fix(p2p): validate connection-string hosts
Found by fuzz_p2p_connstr. Hosts containing brackets, spaces, control
or non-ASCII bytes were accepted and could not be rendered back through
endpoint(). Accept only hostname/IPv4 characters for plain hosts and
hex, ':', '.' plus an optional %zone inside brackets.
2026-09-22 13:53:10 -06:00
trvon
de9e3245f8
fix(p2p): admit only non-negative integers in control frames
Found by fuzz_p2p_handshake. A pre-authentication hello whose integer
field exceeded uint64 was stored by the JSON parser as a double and then
read with get<std::uint32_t>(), an undefined float-to-integer
conversion. A value just above 2^32 was also silently truncated, so
protocol 2^32+4 passed the protocol-4 version check, and negative values
wrapped to huge sizes.

Reject any frame containing a float or a negative number at the frame
boundary (the wire carries none), which also keeps the memory-sync
record deserializers defined, and read p2p numeric fields through a
range-checked unsignedField<T>.
2026-09-22 13:53:04 -06:00
trvon
dc8dc76ce3
refactor(p2p): run handshake and delta exchange over a FrameSource
The handshake, delta-exchange and inbound-session state machines read
frames straight from the TLS P2pConnection, so none of the multi-frame
parsing or payload accounting could be exercised without a socket.
Introduce a private FrameSource seam with a production adapter over
P2pConnection and an in-memory BufferFrameSource, pass TLS-derived
identity values explicitly, and expose the validators and receive paths
in p2p_fuzz.h. Behaviour is unchanged; the transport, protocol, delta,
registry and manager suites pass.
2026-09-22 13:34:36 -06:00
trvon
5cf6e7119f
build(fuzz): build fuzzers with plain clang and libFuzzer
The fuzzing subdirectory skipped itself unless the compiler was AFL++,
so fuzzers could only be built inside the Docker image and no quick
local or CI run was possible. With -Dbuild-fuzzers=true a clang that
links the libFuzzer runtime now selects libFuzzer: project objects get
-fsanitize=fuzzer-no-link plus ASan and UBSan unless b_sanitize is set.
AFL++ builds keep their previous flags.

fuzz.sh gains a 'local' command that runs a libFuzzer target over its
seed corpus and passes a dictionary when tools/fuzzing/dicts has one.
2026-09-22 13:34:20 -06:00
trvon
ac60e84f57
fix(build): include <algorithm> where std algorithms are used
db_salvage.cpp and the SQLite KG store used std algorithms through
transitive includes that a plain clang libFuzzer build does not
provide.
2026-09-22 13:34:14 -06:00
trvon
8faa5f43c3
docs: describe the graph without code-symbol nodes
README, AGENTS.md, the yams skill guide and the engineering prompt
still promised symbol extraction, calls/defined_in edges, and graph
--impact for callers. Point callers and blast radius at yams grep and
describe the relations the graph still holds.
2026-09-22 13:30:52 -06:00
trvon
ec5b78980e
refactor(graph): resolve explore through graph nodes and report removed impact
Graph explore looked up entry points in symbol_metadata, which no longer
exists. Resolve them from node labels, aliases, and ingest path nodes
instead, gather a wider candidate pool, and rank it by query-term
coverage per file before applying the symbol budget so broad early
terms cannot crowd out the file that answers the query.

Impact and affected-test analysis walked code-symbol edges that are no
longer produced; they now return a warning saying the symbol graph was
removed in v0.20 instead of silently empty results.
2026-09-22 13:30:46 -06:00
trvon
c39ed84bf7
refactor(plugins)!: remove the symbol_extractor_v1 plugin ABI
Migration 40 dropped the symbol tables and graph edges, but the daemon
could still adopt an external symbol_extractor_v1 plugin (for example a
0.19 plugin left in a user plugin directory) and rebuild them.

Remove the interface header, the ABI adapter, plugin adoption, the
EntityGraphService extract-and-write path, the post-ingest symbol
channel, the write-coordinator symbol ops, and the KG-store symbol
metadata and extraction-state API. The ABI loader and the external host
now refuse a plugin that declares the interface (or the legacy
symbol_extraction capability) with a one-time warning. Status no longer
reports symbol extractors, generated configs no longer write
[plugins.symbol_extraction] (an existing section still parses), and
the benchmark suite drops symbol_extraction_bench.

Tests that exercised the dropped tables or the v39 schema template are
removed; the rest run against the current schema.

BREAKING CHANGE: code-symbol extraction is gone. Removed: the
symbol_extractor_v1 plugin ABI, `yams config grammar` (list, download,
path, auto-enable, auto-disable), the grammar steps in `yams init`, the
meson option `plugin-symbols`, the conan option
`enable_symbol_extraction`, and the Windows installer SymbolPlugin
component. Databases migrate to schema 41; a v0.19 binary refuses to
open them, and symbol data dropped by migration 40 cannot be recovered.
2026-09-22 13:30:35 -06:00
trvon
8912d70e5c
perf(bench): expose BQ routing work and new topology levers to benchmarks
Report topology_route_bq_used / topology_route_bq_candidates in search
debug stats so benchmarks can tell BQ-shortlisted routing apart from
ANN and exhaustive routing. Let retrieval_quality_bench write
bq_candidate_limit, bq_prefix_dim, sgc_hops and
simeon_rerank_outer_maxsim into its per-run config from YAMS_BENCH_*
harness settings, read through getenv_optional.
2026-09-22 12:51:12 -06:00
trvon
4bc65fe3b8
perf(topology): emit profiling zones from topology and vector code
src/topology and src/vector were compiled without TRACY_ENABLE even in
-Denable-profiling builds, so their YAMS_* zones were no-ops. Mirror the
src/search setup in both libraries, then add zones for snapshot
encode/decode, store and load, SGC smoothing, route-index build and
routing, BQ search, the snapshot cache, and outer MaxSim, plus plots for
snapshot payload bytes, membership count and BQ index bytes.
2026-09-22 12:37:44 -06:00
trvon
7eb64fa8a2
test(search): include the BQ index header in the snapshot cache test
The BQ prefix assertions dereference centroidBqIndex, which the routing
headers only forward-declare.
2026-09-22 12:37:38 -06:00