yams/tests/unit/daemon/embedding_provider_recovery_catch2_test.cpp
Trevon 211db15606
fix(daemon): bound semantic neighbor scores and report startup phase (#334)
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>
2026-10-22 16:48:49 -06:00

34 lines
1.3 KiB
C++

// Copyright (c) 2025 YAMS Contributors
// SPDX-License-Identifier: GPL-3.0-or-later
// Validates degraded->ready recovery via ServiceManager's FSM helper.
#include <catch2/catch_test_macros.hpp>
#include <yams/daemon/components/DaemonLifecycleFsm.h>
#include <yams/daemon/components/EmbeddingProviderFsm.h>
#include <yams/daemon/components/ServiceManager.h>
using namespace yams::daemon;
TEST_CASE("EmbeddingProviderRecovery: degraded then recovered to ready", "[daemon]") {
#ifdef _WIN32
SKIP("EmbeddingProvider FSM tests skipped on Windows - see daemon IPC plan");
#endif
DaemonConfig cfg; // defaults are fine; no plugins needed
StateComponent state{};
DaemonLifecycleFsm lifecycleFsm;
ServiceManager sm(cfg, state, lifecycleFsm);
// Simulate degraded provider
sm.testing_setModelProviderDegraded(true, "unit-test degraded");
auto s1 = sm.getEmbeddingProviderFsmSnapshot();
CHECK(s1.state == EmbeddingProviderState::Degraded);
CHECK(s1.lastError.find("degraded") != std::string::npos);
// Simulate recovery (helper dispatches ModelLoadedEvent with dimension 0)
sm.testing_setModelProviderDegraded(false, "");
auto s2 = sm.getEmbeddingProviderFsmSnapshot();
CHECK(s2.state == EmbeddingProviderState::ModelReady);
CHECK(s2.embeddingDimension == 0u);
}