Semantic-neighbor maintenance aborted the daemon during startup:
std::clamp<float> asserted '!(__hi < __lo)' because the adaptive cosine
threshold could exceed 1.0 for a legal pair of identical vectors. The same
work was also admitted while the daemon was still initializing, so the
300-second starvation guard forced a pass before readiness.
+ semanticInverseNorm/semanticCosineSimilarity/semanticExplicitThreshold in the
existing embed policy header, shared by the streamed and corpus branches;
non-finite vectors or scores produce no neighbor and legal scores are bounded
before ranking and threshold selection
+ startupMaintenanceEligible() requires ServiceManagerState::Ready, not
stopping and idle, and is evaluated before
ResourceGovernor::admitDeferrable(), so an elapsed deferral deadline can no
longer manufacture missing readiness
+ StartupPhaseTracker (two atomics, no mutex) plus a warn-level init-waiter
line reporting state, phase and phase_elapsed_ms instead of a debug-level
state number; a 53-minute startup previously logged nothing actionable
+ regression through the public backfill and corpus-rebuild APIs using a 64-D
vector whose old float inverse norm rounds its self-cosine above 1
- removed the duplicated inverse-norm and cosine lambdas from EmbeddingService
- renamed __test_setModelProvider/__test_setKgStore to testing_* because they
used reserved identifiers, across 11 files
- re-anchored 13 pre-existing raw-environment allowlist entries to their new
line numbers; no new exception was added
Startup is still blocked by corpus-sized memory-sync scans; this change stops
the abort and names the blocking phase, it does not make startup fast.
Signed-off-by: trvon <git@trevon.dev>
Publish owning data-directory snapshots under a mutex and route bootstrap
and fallback publications through the same synchronization boundary.
Keep polling completion locked through its final notification so shutdown
cannot destroy the condition variable while the notifier still uses it.
Exercise both publication windows with deterministic event-based tests and
use the repository thread-compatibility wrapper.
Preserve Failed in status rendering instead of reporting initialization.
Reanchor the existing metrics environment-read entry; add no exceptions.
This does not close the separate queued-collector and embedding-borrower
lifetime audits or establish universal bounded callback shutdown.
Signed-off-by: trvon <git@trevon.dev>
Ingest resolves symlinks before it writes a path (populatePathDerivedFields
calls weakly_canonical), and buildGraphCwdScopePathRanges resolves the scope
the same way. The graph dispatcher fixture wrote node keys and labels from an
unresolved temp_directory_path(). On macOS that is /var/folders/..., a
symlink to /private/var/folders/..., so the scoped listByType sections found
0 nodes there and passed on Linux. The fixture now resolves its temp dir,
like the stored paths it stands in for.
Signed-off-by: trvon <me@trevon.dev>
* test(graph): cover --topology-clusters --scope-cwd outside src/include
Follow-up to #280. --topology-clusters --scope-cwd still built its
scope from <cwd>/src and <cwd>/include, so a corpus laid out any other
way had no scoped members:
- The scope path set must hold every stored path under the cwd, also
when the cwd is reached through a symlink, and not a sibling that
shares the prefix.
- GraphTopologySupport must build the set from its own metadata repo.
The existing cluster-scope case built its path set by hand from the
old src/include contract. It now builds the set from /repo, so
/repo/tests/ is in scope and a document outside /repo is not.
Signed-off-by: trvon <me@trevon.dev>
* fix(graph): scope --topology-clusters --scope-cwd to the whole cwd
Follow-up to #280. Topology views built their --scope-cwd path set from
<cwd>/src and <cwd>/include only, a leftover from the code-symbol graph
removed in v0.20. Corpora laid out any other way reported no scoped
members.
Build the set from the same roots as the --list-type ranges: the cwd,
lexical and symlink-resolved. Keep only paths really under a root, since
the prefix query matches with LIKE. The topology view no longer prunes
build/, packages and IDE paths, so it matches --list-type.
GraphTopologySupport now builds the set from its own metadata repo
instead of the CLI app context. Remove buildGraphCodeScopePathSet and
buildGraphCurrentScopePathSet, which have no other callers, and use one
"Scoped to paths under the current directory" note for both views.
Signed-off-by: trvon <me@trevon.dev>
* test(cli): cover graph --name on a document with a path:file: node
Follow-up to #280. yams graph --name <path> tried path:file:<path>
node keys before the document lookup. v0.20 ingestion writes
path:logical: and path:<snapshot>: keys, so the probe misses on a
fresh corpus; after `yams doctor repair --graph` (or NL entity
extraction) the key exists, and --name printed a raw traversal of the
file node instead of the document's related documents.
Smoke cases through the in-process CLI:
- --name on a file with a path:file: node returns its hash and its
semantic neighbours (red).
- A relative --name resolves under the cwd even though a newer copy
with the same relative path exists in another checkout.
- A bare file name still resolves by name.
The portability allowlist entry for this file moves with its getenv
line.
Signed-off-by: trvon <me@trevon.dev>
* fix(cli): resolve graph --name through the document lookup only
Follow-up to #280. yams graph --name first traversed path:file:<path>
node keys. v0.20 ingestion writes path:logical:<path> and
path:<snapshot>:<path>, which only chain to blob versions; semantic
neighbours hang off doc:<hash>. On a fresh corpus the probe always
missed. After `yams doctor repair --graph` or NL entity extraction it
hit and printed a raw file-node traversal instead of the related
documents.
Drop the probe and go straight to the document lookup, which starts at
doc:<hash> since #280. Resolving to path:logical: instead would show
the version chain, not related documents; --node-key still traverses
any node.
Send --name as the path ingestion stores: relative to the client cwd,
lexically normalized, symlinks resolved. The daemon resolved relative
names against its own cwd and fell back to a suffix match, which picks
the newest copy across checkouts. Fall back to the name as given when
it does not resolve, so bare file names and suffixes still work.
build_graph_file_node_candidates had no other callers; its tests are
replaced by tests for buildGraphDocumentNameCandidates.
Signed-off-by: trvon <me@trevon.dev>
* test(graph): cover --limit on the document graph lookup
Follow-up to #280. yams graph <hash> and --name ignored --limit: the
daemon capped the graph query at 100 nodes and the same-directory
fallback at 20 documents.
Smoke cases through the in-process CLI:
- --limit 1 returns one related document by name and by hash, and the
first of the uncapped list.
- --limit 1 caps the same-directory fallback for a document without
graph edges.
- Without --limit the related lists are unchanged.
Signed-off-by: trvon <me@trevon.dev>
* fix(graph): pass --limit through the document graph lookup
Follow-up to #280. yams graph <hash> and --name sent --depth but not
--limit; DocumentService fixed the graph query at 100 nodes and the
same-directory fallback at 20 documents.
Add GetRequest.graph_limit (proto field 27) and
RetrieveDocumentRequest::graphLimit, and pass them from the CLI through
the dispatcher. A limit caps the related list, graph neighbours, path
history and the same-directory fallback alike. The graph query never
asks for fewer than 100 nodes, since path and blob nodes count toward
that limit.
The CLI sends a limit only when --limit is given; 0 keeps the old caps,
so default output is unchanged. The binary GetRequest layout appends
the field and still reads payloads without it.
Unit tests cover the proto round trip, the dispatcher mapping and the
DocumentService caps.
Signed-off-by: trvon <me@trevon.dev>
---------
Signed-off-by: trvon <me@trevon.dev>
* test(graph): cover document lookup and --scope-cwd listing (#280)
Red tests for the two graph CLI bugs in #280:
- GetResponse graph fields and file name must survive the proto wire.
- DocumentService graph lookup by hash and by name must return the
same documents as traversing the doc:<hash> node.
- The document graph JSON must name the document when only name is set.
- KG path-range scoping must match document nodes by label and
snapshot path nodes by $.path.
- listByType --scope-cwd must keep document and path nodes under the
client cwd (including via a symlinked cwd) outside src/ and include/.
The existing code-roots dispatcher case now places its out-of-scope
file outside the scope root, since the scope is the whole cwd.
Signed-off-by: trvon <me@trevon.dev>
* fix(graph): fill document lookup and scope --scope-cwd to the cwd (#280)
yams graph <hash> and --name returned graphEnabled=false with no
related documents while --node-key doc:<hash> listed neighbours:
- The GetResponse proto binding dropped graphEnabled, related and
fileName, so socket clients never saw graph data.
- DocumentService started traversal at blob:<hash>, which has no
outgoing edges, and filtered out semantic_neighbor. Start at the
doc:<hash> node and allow semantic_neighbor.
- GraphQueryService did not hydrate documentHash for doc: nodes, so
reached documents were skipped.
- The presenter read only fileName for "document"; fall back to name.
--list-type <t> --scope-cwd returned nothing:
- The daemon scoped to <cwd>/src and <cwd>/include only. Scope to the
whole cwd, lexical and symlink-resolved.
- The KG range predicate ignored document labels and $.path on
snapshot path nodes. Match both.
Signed-off-by: trvon <me@trevon.dev>
* docs(skills): correct graph document lookup examples
--format dot is only rendered for node traversal, so show it with
--node-key doc:<hash>. Describe --scope-cwd as cwd-scoped.
Signed-off-by: trvon <me@trevon.dev>
---------
Signed-off-by: trvon <me@trevon.dev>
* chore(warnings): default-initialize aggregate members left out of brace-init
GCC -Wmissing-field-initializers fired about 1300 times where tests and
src build structs with a short positional brace list. Give the omitted
members an empty default member initializer. Value-initialization from
{} is what aggregate init already did for them, so behavior is
unchanged.
Signed-off-by: trvon <me@trevon.dev>
* chore(warnings): remove or annotate unused parameters, functions and variables
- init_command, s3_signer test, request_handler_lifetime test: comment out
the unused parameter name.
- video_content_handler: path is only read under YAMS_HAVE_FFPROBE.
- vector_memory_scaling_bench: main() takes no arguments.
- daemon_status_render: delete unused neutralText helper.
- DaemonMetrics: mallocStackLoggingNoCompactEnabled is only called in the
__APPLE__ block, so define it there.
- SocketServer: einval_streak only matters in the __APPLE__ EINVAL path.
- onnx_plugin_harness test, vector_backend_engine_compare bench,
work_coordinator test: mark platform-conditional or busy-loop
variables [[maybe_unused]].
Signed-off-by: trvon <git@trevon.dev>
* chore(warnings): fix sign-compare, range-loop, nodiscard and dangling-ref in tests
- search_admission_controller: count started workers in std::uint32_t so
the comparison with the uint32 cap is same-signed.
- p2p_manager: iterate the invalid-host list by const reference.
- integrity_verifier: discard the nodiscard results the tests do not
inspect with static_cast<void>.
- post_ingest_queue: findNode/findEdge take std::string_view so no
temporary std::string is created for the call (GCC
-Wdangling-reference false positive on the returned reference).
- topology_ablation_bench: the doc comment ended a line with a backslash,
which continues the comment (-Wcomment); show the env export on its own
line.
Signed-off-by: trvon <git@trevon.dev>
* chore(warnings): drop redundant std::move of a const captured json
The coroutine lambda is not mutable, so the captured optional<json> is
const and std::move(*progressToken) already selected the copy assignment.
Drop the std::move so the code states what actually happens.
Signed-off-by: trvon <git@trevon.dev>
* chore(warnings): mark __int128 use in db_integrity_stamp as an extension
-Wpedantic flags __int128. Use a __extension__ typedef, the GCC/Clang
idiom for an intentional extension, instead of the bare keyword.
Signed-off-by: trvon <git@trevon.dev>
* chore(warnings): remove always-true unsigned >= 0 comparisons
GCC -Wtype-limits flagged 51 test assertions of the form
CHECK(unsignedValue >= 0) that can never fail, plus a dead
`totalCount.value() < 0` clause in the graph listByType handler (the
count is std::size_t). In the tests keep the evaluation as a
static_cast<void> so the accessors are still exercised; in the handler
drop the dead clause and keep the range check.
Signed-off-by: trvon <me@trevon.dev>
---------
Signed-off-by: trvon <me@trevon.dev>
Signed-off-by: trvon <git@trevon.dev>
When the daemon holds background work back for a busy host, a user
looking at a stale topology or an unfinished repair scan needs to see
why. Status had no host signal at all.
Carry the governor's host sample through the metrics snapshot and
StatusResponse: source (psi, loadavg), whether the host counts as busy,
the PSI cpu/io/memory "some avg10" values or the per-CPU load average,
and which deferrable work is currently held back. The proto transport
piggybacks the fields on request_counts like the other string status
fields; the native serializer appends them as optional tail fields.
`yams daemon status` (brief and detailed) shows a Host Load row, e.g.
"Busy cpu 72% · io 3% · mem 0% · deferring topology, repair", and
`yams status --json` gains a host_pressure object. Document the new
[tuning.resource] keys next to the other typed tuning sections.
Signed-off-by: trvon <me@trevon.dev>
v0.20 stopped producing code-symbol nodes and calls/references/
inherits/implements edges, and migration 40 deleted the existing ones.
GraphContextService::impact() and affectedTests() had already been
reduced to returning a "code-symbol graph was removed" warning, so
`yams graph --impact`, `--affected-tests` and `--test-pattern` (and the
MCP graph actions `impact` and `affected_tests`) could only ever print
that warning.
Remove the dead surface:
- CLI: the three flags, their execute/render helpers, and the --impact
mention in the --global help text.
- MCP: the two actions, their schema entries and the changed_files /
test_pattern request fields. Unknown graph actions now fail with
InvalidArgument instead of silently running a default query.
- Service: impact()/affectedTests() and their request/response structs.
The GraphImpact/GraphAffectedTests IPC messages stay for wire
compatibility. The dispatcher now answers them directly with the same
removal warning, without touching the metadata repository.
Kept: --lookup (with --at-file) and --trace/--to. Both resolve graph
node labels and aliases and walk edges of any relation, so they still
work on document, path, version and entity graphs.
Tests: the service-level impact/affected-tests case moves to the
dispatcher graph suite; the sandbox smoke test now expects --impact to
be rejected. docs/prompts/PROMPT-eng-codex.md points related-test and
blast-radius questions at `yams grep`.
Signed-off-by: trvon <me@trevon.dev>
Every store added two metadata rows per snapshot to the document,
snapshot_id:<id> and snapshot_time:<id>, and nothing removed them.
Content-addressed documents are shared across paths and passes, so they
piled up: on one 93 GB database they were 43.1M of 44.0M metadata rows,
one file carried 52,367 snapshots, and 'yams list' timed out hydrating
2.69M rows for its 100 documents. Meanwhile findDocumentsBySnapshot read
only the latest snapshot_id, so a re-stored document dropped out of every
earlier snapshot.
- Migration 42 creates document_snapshots(document_id, snapshot_id,
snapshot_time), keyed on (document_id, snapshot_id). It only creates the
table, so startup stays fast.
- The insert transaction records membership; stores keep only the latest
snapshot_id/snapshot_time keys.
- findDocumentsBySnapshot and file history read membership, falling back
to legacy keys through one shared parser (legacySnapshotEntries).
- getMetadataForDocuments skips legacy keys via idx_metadata_doc_key: on
the 93 GB database, 0.21 s instead of 18.5 s for list's 100 documents.
- RepairService moves legacy keys in the background, 1000 snapshots per
batch, 200 ms apart, only below Warning pressure.
Signed-off-by: trvon <me@trevon.dev>
repairMissingEmbeddings built its database path as dataPath/'vectors.db'
with create_if_missing, so an unresolved (empty) data directory created a
stray vectors.db in the process working directory. The dispatcher passes
an empty path when the service manager has not resolved its data dir.
Return an error instead.
Tests that exercised repair or placed plugins under the unresolved data
directory now give the service manager a temp dir; the dispatcher suite
no longer leaves vectors.db or dispatcher_external_* in the working
directory.
Isolated mode lost its function/calls default in v0.20 and instead
required a node type, but MCP only sets the node type through list_type,
which also switched the request to list-by-type (checked first), so
isolated:true either errored or silently listed nodes.
Default isolated mode to document nodes (with the existing
semantic_neighbor relation default: documents no other document lists as
an embedding neighbour), keep MCP list_type from overriding isolated mode,
and update the tool schema examples that still named calls edges.
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.
* fix(memory-sync): keep replication responsive
Skip poisoned blobs without wedging backfill and publish immutable status snapshots so daemon IPC does not block on an active reconciliation cycle.
Reconstructed from ff62d3f8, 8e232721, and e60f513d.
Signed-off-by: trvon <git@trevon.dev>
* feat(p2p): add authenticated direct transport
Require TLS 1.3 mutual identity and bind the authenticated certificate, node, corpus, and epoch before replication state is exchanged.
Reconstructed from 96f9db88 and c46de77b.
Signed-off-by: trvon <git@trevon.dev>
* feat(p2p): exchange causal deltas
Persist peer pins and causal resume state before visibility so reconnects continue bounded authenticated replication without becoming remote query proxies.
Reconstructed from 1b8be146 and 41883338.
Signed-off-by: trvon <git@trevon.dev>
* feat(p2p): integrate direct sync services
Wire listener, reconnect, IPC, CLI, peer persistence, and daemon lifecycle around the authenticated direct transport.
Reconstructed from a19aaa44.
Signed-off-by: trvon <git@trevon.dev>
* fix(p2p): apply replicated document state
Index authenticated remote winners, propagate document tombstones, and keep service/test wiring complete across daemon paths.
Reconstructed from 4ca660c6, 443253f1, 3e9db24d, and 4b2ddcc5.
Signed-off-by: trvon <git@trevon.dev>
* test(p2p): register direct sync gates
Replace the superseded internal contract with operator-facing direct-sync guidance and register daemon integration coverage in the standard test layout.
Reconstructed from 360b9115 and 89416ccb.
Signed-off-by: trvon <git@trevon.dev>
* fix(p2p): require bounded peer enrollment
Bound aggregate sessions and worker lifetimes, create owner-only identity keys atomically, and reject unknown inbound peers until operators pin their mTLS identity.
Reconstructed from f91be528, 79de8e48, 4fb1e4b8, dc30a6c0, and b329edd3.
Signed-off-by: trvon <git@trevon.dev>
* fix(p2p): persist quarantine and deletes
Persist full-history writer commitments before invalidating forked state, and order local deletion through a durable authenticated outbox so tombstones survive crashes and reconnects.
Reconstructed from 74ce01e0 and 19888604.
Signed-off-by: trvon <git@trevon.dev>
* feat(p2p): add authenticated cold bootstrap
Install bounded zero-state snapshots only from previously operator-pinned peers, bind them to exact frontiers and full-history commitments, and recover staged journals before replication can resume.
Reconstructed from 8a9f5acf.
Signed-off-by: trvon <git@trevon.dev>
---------
Signed-off-by: trvon <git@trevon.dev>
Rebuild macOS TSan outputs so compiler instrumentation and compiler-rt come from one Apple toolchain generation. Preserve daemon fixture dependencies until ServiceManager teardown so sanitizer lanes do not exercise a test-only use-after-free.
* refactor(tuning): prune dormant runtime knobs
Remove unused tuning controls and adaptive search budget state. Extract testable ONNX slot reconciliation and search telemetry hydration seams, and correct the Apple Silicon sysctl value width.
* refactor(vector): retire dormant backends
Remove unused FAISS, model-infrastructure, VectorIndexManager, and TurboQuant paths while keeping Simeon PQ and float-vector storage.
Migrate unused V2.2 quantized schemas transactionally to V2.3. Databases containing non-default quantized formats or packed-code blobs fail closed with recovery guidance and no mutation.
* refactor(plugins): remove legacy SDK tooling
Remove the Ghidra and SDK submodules, their bespoke tests, and stale scan exclusions. Retain generic PluginProcess coverage against the repository mock fixture.
* build(vector): remove stale FAISS overrides
* refactor(ipc): remove dormant FSM snapshots
* refactor(ingest): unify bounded KG enqueue
* fix(plugins): harden allocation contracts
Keep ABI outputs null or fully initialized when allocations fail, and represent empty object payloads without relying on malloc(0).
* fix(storage): define empty object reads
Return a null buffer with zero length for successful empty-object reads instead of treating malloc(0) as an allocation contract.
* refactor(storage): trim through string views
* test: prune dormant harness helpers
Remove unused readiness and fixture helpers, and keep benchmark client-count doubling in a wide integer domain.
* chore(cpp): initialize analyzer-facing locals
* build(simeon): advance analyzer cleanup
* refactor(config): drop dead daemon client keys
* refactor(ipc): inject daemon client transport
Socket and in-process execution now share DaemonClient request dispatch, so CLI fallbacks do not need alternate service paths.
* refactor(retrieval): route CLI through daemon
Search and grep now use the same daemon handlers over socket or in-process transport. Live grep remains an explicit freshness inspection.
* refactor(graph): move CLI scope into daemon
Graph CLI requests now share daemon handlers across socket and in-process transports. CWD filtering occurs before pagination so scoped totals and pages stay correct.
* build: isolate test-only runtime controls
* refactor(metadata): decouple lock telemetry
* fix(services): return on request timeout
* fix(retrieval): enforce workspace scope
* fix(graph): scope nodes before pagination
* fix(grep): drop binary result lines
* fix(daemon): bound checkpoint wait
* fix(repair): use KG health checks
* fix(storage): always verify compressed CRC
* perf(vector): skip unchanged PQ snapshots
* build(zyp): exclude bench from default build
* build: protect Meson files from formatter
* refactor: remove unreachable production APIs
* refactor(daemon): remove dormant runtime state
* fix(vector): harden daemon provider calls
Timed-out coroutines must own their client and requests. Provider discovery must also fail instead of returning synthetic embeddings in production.
* refactor(storage): remove unusable adapters
* fix(cli): unify in-process add requests
* fix(tsan): preserve async worker progress
* test(cli): isolate daemon-dependent suites
* fix(repair): expose queue backoff progress
* 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>
* fix(core): add assertions and harden daemon lifecycle
* fix(daemon): tighten shutdown lifecycle invariants
* fix(test): add PressureLimitedPoller capability-sleep shutdown test
* refactor(vector): harden backend contracts
Split the vector backend surface into narrower contracts and
move entity read paths to Result<> so callers can distinguish
backend errors from empty results.
Also fixes the TurboQuant lazy-init race, makes VectorDatabase
error state thread-safe, wires eager index preparation, and
adds regression plus memory-scaling benchmark coverage.
* fix(vector): address audit findings plus hardening
Fix the five confirmed bugs: coroutine lifetime hazard in
embedding_generator (await_with_timeout outliving captured state),
VectorSystemManager init retry latch poisoning, incremental HNSW
persistence race, vec0 registry key collisions, and simeon PMI
output_dim/config() drift.
Add invariant hardening: PostIngestQueue stage validation +
numChunks assertion, VectorSystemManager success postcondition,
VectorDatabase TurboQuant non-null assertions on all quantize/
dequantize paths, and sqlite_vec_backend Simeon PQ range and
divisibility preconditions.
Also adds regression coverage for init-retry and lock-busy paths
and a locked node snapshot helper for safe incremental HNSW
persistence.
* fix(test): add PressureLimitedPoller wake-timer shutdown tests
* chore(vector): resolve clang-tidy hygiene across queue, vdb, and sqlite-vec
Replace unprotected std::getenv calls with locked getenvCopy helpers,
add debug-level logging to previously-silent catch blocks in
PostIngestQueue, VectorDatabase, and SqliteVecBackend, wrap
PostIngestQueue timer-detach allocations in try-catch, and fix
unsigned-widening / optional-deref / unused-include hygiene.
Verified with LSP diagnostics and rebuilds of catch2_vector_submodule
and catch2_daemon_component_submodule.
* fix(daemon): harden PressureLimitedPoller inFlightCounter null check
* fix(daemon): harden PostIngestQueue wait_until deadline checks
* fix(daemon): clean up recovery sentinels regardless of salvage count
Previously the .recovered-* sentinel and .corrupt-* file cleanup
was gated on documentsSalvaged > 0, leaving quarantine
artifacts to accumulate on repeated crash/recovery cycles where
the corrupt DB had 0 salvageable docs.
Move cleanup outside the salvage-count guard so stale
quarantine artifacts are always removed after examination.
* feat(vec0): restore persisted ANN index on daemon restart
After vec0 rebuild, probe for vectors_<dim>_hnsw_meta shadow tables
and load a previously-persisted HNSW index. When found, the ANN
index is set directly on the vec0 table, skipping the O(n log n)
rebuild that was previously paid on every daemon restart.
Removes the stub restoreVec0AnnIndexIfPresentUnlocked() in favor
of inline restoration in rebuildVec0DimUnlocked(), which has
the vec0 table already created and ready for ann_index assignment.
* fix: enable DCHECK by default and assert metadata counter consistency
* fix(services): assert document deletion ordering content-before-metadata
* fix(test): add fork-based assertion validation tests
* fix(test): expand assertion tests for POSTCONDITION, DCHECK, UNREACHABLE
* fix(ci): enable -Denable-dcheck=true in all test CI lanes
* fix(test): address assertion review findings
* refactor(daemon): isolate poller config assertion checks
* refactor(services): reduce deletion and poller complexity
* style(ci): normalize test workflow spacing
* refactor(services): extract list query helpers
* refactor(services): extract list hydration helpers
* refactor(daemon): extract metrics snapshot helpers
* refactor(daemon): extract metrics counter snapshot
* refactor(daemon): extract metrics resource snapshot
* feat(graph): add daemon-backed explore
Wire graph explore through daemon IPC, keep CLI rendering stable,
and fall back locally when daemon transport or protocol skew fails.
Also add graph context service/tests, improve graph follow-up hints,
and keep release builds working with the current JSON library.
* fix(services): prevent prune delete metadata orphans
* fix(cli): honor data-dir in graph explore
- bypass daemon explore when the CLI pins a data dir so\n temp fixtures do not resolve against a long-lived global DB\n- guard snippet truncation and ratio math to remove Fold-\n reported underflow and div-by-zero paths\n- clamp zero-based symbol lines before rendering graph\n context
* fix(daemon): make lifecycle stop completion safe
* fix(daemon): gate db-open startup against shutdown
* fix(metadata): serialize FTS indexed counter updates
* test(daemon): simulate vector lock contention cross-process
* test(daemon): isolate post-ingest identity batch
* style(daemon): normalize test include order
* refactor(graph): split cli graph helpers
Keep graph command paths smaller and easier to evolve.
Lock graph explore/topology contracts into focused presenter,
dispatcher, and support tests so follow-up refactors keep the
current IPC and hot-path behavior intact.
* fix(config): harden expand_tilde and fix Windows XDG state path doubling
- expand_tilde: handle bare ~ and ~/ explicitly to avoid std::out_of_range
on substr(2) for single-char input
- getXDGStateHome: on Windows, go up two levels from %LOCALAPPDATA%\yams\state
to %LOCALAPPDATA% so callers' xdg/yams append doesn't double the yams segment
* fix(static-analysis): add logging to empty catch blocks and fix nosemgrep syntax
* fix(static-analysis): strip trailing text from nosemgrep comments
* fix(doctor): add logging to daemon_check empty catch blocks
* fix(db_repair): add logging to empty catch blocks
* fix(chunking): add precondition checks for rabin polynomial and windowSize
* fix(safety): add null precondition checks for Fold proved null-deref sites
* fix(daemon): widen init-failure dispatch, add null/dbz preconditions
InitializationFailedEvent races with shutdown — async init
fails after teardown begins. Widen canDispatch to accept
Ready/ShuttingDown. Add static_assert on SnapshotRing N>0.
Add YAMS_PRECONDITION null guards on hex/stages/data/bytes.
* ci(ubsan): use restricted build targets to avoid runner timeout
* fix(daemon): detach wake timers on Windows stop path to prevent SIGSEGV
* fix(config): cast parseTomlU32 to size_t for batch fields
* fix(ci): use static helper for Windows timer leak, restrict ubsan tests
* fix(storage): add MutexPool and hash-length precondition assertions
* fix(storage): add reference counter lifecycle assertions
- YAMS_PRECONDITION on increment size constraints
- YAMS_DCHECK on decrement zero-row detection
- YAMS_ASSERT on transaction AUTOINCREMENT ID
- YAMS_PRECONDITION on transaction active guard
- YAMS_POSTCONDITION on commit/rollback state
* fix(storage): add atomic-write and store postcondition assertions
* fix(ingestion): add batch concurrency and accounting assertions
* fix(storage): add remove counter and retrieve-read DCHECKs
* fix(daemon): add WorkCoordinator double-start precondition
* bench(storage): add store/retrieve throughput microbenchmark
* fix(daemon): replace WorkCoordinator double-start throw with precondition
* test(daemon): add fork-based double-start precondition test
* test(ingestion): add batch concurrency edge-case tests
* fix(storage): remove overly-aggressive decrement DCHECK, fix transaction test
* test(daemon): fork-based precondition test — known Catch2 SIGABRT artifact
Catch2 v3.12.0 signal handler catches child SIGABRT via abort(),
marking test as FAILED even though CHECK passes correctly.
Affects all fork-based assertion tests. Test logic is correct.
* refactor(test): remove duplicate fork test, defer to assert_catch2
* fix(review): downgrade transactionId to DCHECK, use unique benchmark hashes
* fix(review): document PRECONDITION+throw pattern, downgrade exists() to DCHECK
* test(ingestion): add maxConcurrent early-return branch coverage
* perf(storage): eliminate zero-init in retrieveRaw, fix benchmark build
retrieveRaw bottleneck: 73% of samples in vector<std::byte>(N) zero-init
that gets overwritten by file.read(). Use make_unique_for_overwrite (C++20)
to allocate uninitialized, then assign into vector.
Before/after (TSan, 1MB Retrieve): 41.7ms -> 9.0ms (4.6x faster)
Also fix Google Benchmark build: add benchmark_main_dep (provides main())
to all 7 benchmark targets. Fix storage benchmark to use valid hex hashes.
0 assertion violations across all benchmark runs.
* perf(storage): skip redundant temp-dir check, use hex table for names
Two micro-optimizations in getTempPath() hot path:
1. Drop ensureDirectories() call — temp dir already created at ctor,
stat() on every store was wasted work.
2. Replace 16 fmt::format("{:x}") calls with pre-computed hex char
table + push_back. Avoids format machinery per store.
0 opengrep findings.
* assert(storage): DCHECK temp dir, buffer, size invariants in hot paths
Coverage for the three changed functions:
getTempPath:
- DCHECK temp dir still exists (catch external cleanup in dev/CI)
- DCHECK temp name length matches TEMP_NAME_LENGTH
atomicWrite:
- DCHECK temp file exists after successful ofstream write
retrieveRaw:
- DCHECK buffer non-null for positive file sizes
- DCHECK assigned vector size matches fileSize
All benchmarks pass with zero assertion violations (TSan + Release).
* style(storage): fmt wrap DCHECK lines
* fix(review): downgrade PRECONDITION→DCHECK for catchable throws, guard zero-size UB
getObjectPath: YAMS_PRECONDITION always aborts — throw below is dead code.
Use YAMS_DCHECK instead (aborts in dev/CI, compiles out in release so
throw fires for backward-compatible catchable std::invalid_argument).
retrieveRaw: fileSize==0 → make_unique_for_overwrite(0) may return null,
assign(null,null) is UB. Early-return empty RawObject for zero-size files.
meson.build: guard benchmark blocks with benchmark_main_dep.found() too.
* fix(review): drop PRECONDITION dead throw, fix test comments
reference_counter: downgrade YAMS_PRECONDITION→DCHECK for transaction
active_ guard (aborts dev/CI, throw fires in release). Add matching
DCHECKs to decrement() and pruneReference() for consistency.
Fix inaccurate test comments claiming fork-test coverage for
WorkCoordinator double-start invariant.
* fix(review): DCHECK store postcondition, cast fileSize, rm dup main
store: downgrade YAMS_POSTCONDITION→DCHECK for exists() check.
Always-on POSTCONDITION adds stat() syscall to hot store() path;
debug-only DCHECK is the correct tier.
retrieveRaw: explicit static_cast<size_t>(fileSize) in assign
to avoid signed/unsigned narrowing warnings.
meson.build: remove benchmark_main_dep from metadata_query_bench_deps.
metadata_path_query_bench.cpp and tree_list_filter_bench.cpp both
define BENCHMARK_MAIN() — linking benchmark_main would be duplicate.
WorkCoordinator.h: update @throws→@pre in doxygen comment to match
YAMS_PRECONDITION(!started_) behavior.
* fix(bench): move pre-store to SetUp, PauseTiming unreliable
PauseTiming before first benchmark iteration caused single-iteration
runs in some Google Benchmark versions. Move pre-store into Fixture::SetUp
where it runs outside the timing loop naturally.
* fix(bench): check store/retrieve results, fix comment copy-vs-move
SetUp: throw if pre-store fails instead of silently continuing.
Retrieve: SkipWithError if retrieveRaw returns error.
Comment: assign() copies, not moves — fix misleading wording.
* fix(bench): rm benchmark_main_dep from targets with own main
ingestion_e2e_bench, retrieval_quality_bench, fusion_strategy_experiment
all define their own main() — linking benchmark_main_dep would cause
duplicate symbol at link time.
* fix(test): rename work_coordinator section, no longer tests double-start
Section title "Rejects start when already running" implied
double-start coverage but only checked worker count after first
start. Renamed to "Starts with correct worker count".
* test(refcount): add closed-state throw coverage for increment/decrement
After commit, increment/decrement must fail:
- Dev/CI (DCHECK): YAMS_DCHECK aborts, covered by fork tests
- Release (NDEBUG): DCHECK compiles out, throw fires
Conditional REQUIRE_THROWS_AS for release path.
* fix(bench): split meson guard for own-main vs benchmark_main targets
Targets with BENCHMARK_MAIN (metadata_path_query, tree_list_filter,
retrieval_quality, fusion_experiment, ingestion_e2e) only need
benchmark_dep. Storage/retrieval benchmarks need benchmark_main_dep.
Split into two guarded blocks to avoid skipping own-main targets
when benchmark_main is unavailable.
* fix(refcount): downgrade increment size PRECONDITION to DCHECK+error return
compressedSize>0 and uncompressedSize>=compressedSize come from user
content (block sizes). PRECONDITION would abort the process on corrupted
or malicious blocks. Return ErrorCode::InvalidArgument instead so
callers can handle gracefully.
* fix(daemon): defer startup maintenance
Make metadata repository readiness the startup boundary while moving search build, recovery cleanup, salvage, and VACUUM into coordinated background or pre-publication paths.\n\nKeep status honest for operators by separating database readiness from maintenance phase and surfacing recovery artifacts.
* fix(core): add assertions and harden daemon lifecycle
* fix(daemon): tighten shutdown lifecycle invariants
* fix(test): add PressureLimitedPoller capability-sleep shutdown test
* refactor(vector): harden backend contracts
Split the vector backend surface into narrower contracts and
move entity read paths to Result<> so callers can distinguish
backend errors from empty results.
Also fixes the TurboQuant lazy-init race, makes VectorDatabase
error state thread-safe, wires eager index preparation, and
adds regression plus memory-scaling benchmark coverage.
* fix(vector): address audit findings plus hardening
Fix the five confirmed bugs: coroutine lifetime hazard in
embedding_generator (await_with_timeout outliving captured state),
VectorSystemManager init retry latch poisoning, incremental HNSW
persistence race, vec0 registry key collisions, and simeon PMI
output_dim/config() drift.
Add invariant hardening: PostIngestQueue stage validation +
numChunks assertion, VectorSystemManager success postcondition,
VectorDatabase TurboQuant non-null assertions on all quantize/
dequantize paths, and sqlite_vec_backend Simeon PQ range and
divisibility preconditions.
Also adds regression coverage for init-retry and lock-busy paths
and a locked node snapshot helper for safe incremental HNSW
persistence.
* fix(test): add PressureLimitedPoller wake-timer shutdown tests
* chore(vector): resolve clang-tidy hygiene across queue, vdb, and sqlite-vec
Replace unprotected std::getenv calls with locked getenvCopy helpers,
add debug-level logging to previously-silent catch blocks in
PostIngestQueue, VectorDatabase, and SqliteVecBackend, wrap
PostIngestQueue timer-detach allocations in try-catch, and fix
unsigned-widening / optional-deref / unused-include hygiene.
Verified with LSP diagnostics and rebuilds of catch2_vector_submodule
and catch2_daemon_component_submodule.
* fix(daemon): harden PressureLimitedPoller inFlightCounter null check
* fix(daemon): harden PostIngestQueue wait_until deadline checks
* fix(daemon): clean up recovery sentinels regardless of salvage count
Previously the .recovered-* sentinel and .corrupt-* file cleanup
was gated on documentsSalvaged > 0, leaving quarantine
artifacts to accumulate on repeated crash/recovery cycles where
the corrupt DB had 0 salvageable docs.
Move cleanup outside the salvage-count guard so stale
quarantine artifacts are always removed after examination.
* feat(vec0): restore persisted ANN index on daemon restart
After vec0 rebuild, probe for vectors_<dim>_hnsw_meta shadow tables
and load a previously-persisted HNSW index. When found, the ANN
index is set directly on the vec0 table, skipping the O(n log n)
rebuild that was previously paid on every daemon restart.
Removes the stub restoreVec0AnnIndexIfPresentUnlocked() in favor
of inline restoration in rebuildVec0DimUnlocked(), which has
the vec0 table already created and ready for ann_index assignment.
* fix: enable DCHECK by default and assert metadata counter consistency
* fix(services): assert document deletion ordering content-before-metadata
* fix(test): add fork-based assertion validation tests
* fix(test): expand assertion tests for POSTCONDITION, DCHECK, UNREACHABLE
* fix(ci): enable -Denable-dcheck=true in all test CI lanes
* fix(test): address assertion review findings
* refactor(daemon): isolate poller config assertion checks
* refactor(services): reduce deletion and poller complexity
* style(ci): normalize test workflow spacing
* refactor(services): extract list query helpers
* refactor(services): extract list hydration helpers
* refactor(daemon): extract metrics snapshot helpers
* refactor(daemon): extract metrics counter snapshot
* refactor(daemon): extract metrics resource snapshot
* feat(graph): add daemon-backed explore
Wire graph explore through daemon IPC, keep CLI rendering stable,
and fall back locally when daemon transport or protocol skew fails.
Also add graph context service/tests, improve graph follow-up hints,
and keep release builds working with the current JSON library.
* fix(services): prevent prune delete metadata orphans
* fix(cli): honor data-dir in graph explore
- bypass daemon explore when the CLI pins a data dir so\n temp fixtures do not resolve against a long-lived global DB\n- guard snippet truncation and ratio math to remove Fold-\n reported underflow and div-by-zero paths\n- clamp zero-based symbol lines before rendering graph\n context
* fix(daemon): make lifecycle stop completion safe
* fix(daemon): gate db-open startup against shutdown
* fix(metadata): serialize FTS indexed counter updates
* test(daemon): simulate vector lock contention cross-process
* test(daemon): isolate post-ingest identity batch
* style(daemon): normalize test include order
* refactor(graph): split cli graph helpers
Keep graph command paths smaller and easier to evolve.
Lock graph explore/topology contracts into focused presenter,
dispatcher, and support tests so follow-up refactors keep the
current IPC and hot-path behavior intact.
* fix(config): harden expand_tilde and fix Windows XDG state path doubling
- expand_tilde: handle bare ~ and ~/ explicitly to avoid std::out_of_range
on substr(2) for single-char input
- getXDGStateHome: on Windows, go up two levels from %LOCALAPPDATA%\yams\state
to %LOCALAPPDATA% so callers' xdg/yams append doesn't double the yams segment
* fix(static-analysis): add logging to empty catch blocks and fix nosemgrep syntax
* fix(static-analysis): strip trailing text from nosemgrep comments
* fix(doctor): add logging to daemon_check empty catch blocks
* fix(db_repair): add logging to empty catch blocks
* fix(chunking): add precondition checks for rabin polynomial and windowSize
* fix(safety): add null precondition checks for Fold proved null-deref sites
* fix(daemon): widen init-failure dispatch, add null/dbz preconditions
InitializationFailedEvent races with shutdown — async init
fails after teardown begins. Widen canDispatch to accept
Ready/ShuttingDown. Add static_assert on SnapshotRing N>0.
Add YAMS_PRECONDITION null guards on hex/stages/data/bytes.
* ci(ubsan): use restricted build targets to avoid runner timeout
* fix(daemon): detach wake timers on Windows stop path to prevent SIGSEGV
* fix(config): cast parseTomlU32 to size_t for batch fields
* fix(ci): use static helper for Windows timer leak, restrict ubsan tests