fix(people): seed people store at core boot (#4344) (#4345)

This commit is contained in:
oxoxDev
2026-06-30 21:56:05 +05:30
committed by GitHub
parent 4035345936
commit 1a59b0ea3d
4 changed files with 69 additions and 1 deletions
+14
View File
@@ -1819,6 +1819,20 @@ async fn run_server_inner(
),
Err(e) => log::warn!("[boot] whatsapp_data::global init failed: {e}"),
}
// Seed the people store so people controllers + `people_*`
// tools can read/write. Without this the process-global stays
// empty and every call fails with "people store not
// initialised" (Sentry TAURI-RUST-8NM). Sits inside this
// Ok(cfg) arm so it inherits the wrong-workspace guard above
// (never seed against a Config::default fallback).
match crate::openhuman::people::store::init_from_workspace(&cfg.workspace_dir).await
{
Ok(_) => log::info!(
"[boot] people::store initialized (workspace={})",
cfg.workspace_dir.display()
),
Err(e) => log::warn!("[boot] people::store init failed: {e}"),
}
// Prune legacy bundled skills (dev-workflow / github-issue-crusher
// / pr-review-shepherd) that older builds seeded into
// <workspace>/skills/. OpenHuman no longer ships bundled defaults;
+1 -1
View File
@@ -58,7 +58,7 @@ Dedicated SQLite DB managed by `PeopleStore` (open via `open_at(path)` or `open_
- `handle_aliases``(kind, value)` primary key → `person_id` (FK, `ON DELETE CASCADE`); `value` is the canonicalized form. This table *is* the resolver index.
- `interactions``(person_id, ts, is_outbound, length)` rows the scorer aggregates; indexed by `(person_id, ts DESC)` and `ts DESC`.
Migrations are tracked in `_people_migrations` and applied idempotently in a transaction. The store is exposed process-globally through a `tokio::sync::OnceCell` (`init` once at startup, `get` from controller handlers). Note: no `people::store::init` call was found outside the module — controller handlers will return "people store not initialised" until something seeds it.
Migrations are tracked in `_people_migrations` and applied idempotently in a transaction. The store is exposed process-globally through a `tokio::sync::OnceCell` (`get` from controller handlers). Core boot seeds it via `store::init_from_workspace(workspace_dir)` (`src/core/jsonrpc.rs`, alongside `memory::global` and `whatsapp_data::global`), opening `<workspace>/people/people.db`; tests construct stores directly with `open_in_memory`.
## Dependencies
+27
View File
@@ -29,6 +29,33 @@ pub async fn init(store: Arc<PeopleStore>) -> Result<(), &'static str> {
.map_err(|_| "people store already initialised")
}
/// Seed the process-global store from a workspace directory, opening the
/// on-disk db at `<workspace>/people/people.db` (schema migrations run on
/// open). Idempotent: a second call — or a concurrent boot path — is a no-op
/// that returns the already-seeded store rather than erroring.
///
/// Wired into core boot (`src/core/jsonrpc.rs`) alongside `memory::global` and
/// `whatsapp_data::global`. Without this seed the global stays empty and every
/// people controller / `people_*` tool fails with "people store not
/// initialised" (Sentry TAURI-RUST-8NM).
pub async fn init_from_workspace(
workspace_dir: &std::path::Path,
) -> Result<Arc<PeopleStore>, String> {
if let Some(existing) = GLOBAL.get() {
log::debug!("[people:store] already initialised");
return Ok(existing.clone());
}
let db_path = workspace_dir.join("people").join("people.db");
let store = Arc::new(
PeopleStore::open_at(&db_path).map_err(|e| format!("people store open failed: {e}"))?,
);
// Race-resolve: another caller may have seeded while we were opening.
match GLOBAL.set(store.clone()) {
Ok(()) => Ok(store),
Err(_) => Ok(GLOBAL.get().cloned().unwrap_or(store)),
}
}
pub fn get() -> Result<Arc<PeopleStore>, &'static str> {
GLOBAL
.get()
+27
View File
@@ -1,5 +1,7 @@
//! Cross-file integration tests for the people domain.
use std::sync::Arc;
use chrono::Utc;
use crate::openhuman::people::address_book;
@@ -57,6 +59,31 @@ fn schema_exposes_four_controllers() {
assert_eq!(names.len(), 4);
}
/// Regression for Sentry TAURI-RUST-8NM: the process-global people store was
/// never seeded at boot, so `store::get()` (and every `people_*` tool /
/// controller) always failed with "people store not initialised". Boot now
/// calls `init_from_workspace`; verify it seeds the global, creates the on-disk
/// db, and is idempotent.
#[tokio::test]
async fn init_from_workspace_seeds_global_store() {
use crate::openhuman::people::store;
let tmp = tempfile::tempdir().unwrap();
let store = store::init_from_workspace(tmp.path()).await.unwrap();
assert!(
tmp.path().join("people").join("people.db").exists(),
"boot seed must create <workspace>/people/people.db"
);
// The previously-dead global is now reachable — this is the fix.
let via_global = store::get().expect("people store reachable after boot seed");
assert!(Arc::ptr_eq(&store, &via_global));
// Idempotent: a second seed returns the same instance, never errors.
let again = store::init_from_workspace(tmp.path()).await.unwrap();
assert!(Arc::ptr_eq(&store, &again));
}
#[test]
fn person_id_uuid_format() {
let id = PersonId::new();