2026-03-13 00:18:14 +00:00
|
|
|
use std::path::Path;
|
|
|
|
|
|
|
|
|
|
use serde_json::Value;
|
|
|
|
|
use tracing::{info, warn};
|
|
|
|
|
|
|
|
|
|
use crate::cache::NclCache;
|
|
|
|
|
|
|
|
|
|
/// Counts of records upserted per table during a seed pass.
|
|
|
|
|
pub struct SeedReport {
|
|
|
|
|
pub nodes: usize,
|
|
|
|
|
pub edges: usize,
|
|
|
|
|
pub dimensions: usize,
|
|
|
|
|
pub membranes: usize,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Seed ontology tables from local NCL files into SurrealDB.
|
|
|
|
|
///
|
feat: unified auth model, project onboarding, install pipeline, config management
The full scope across this batch: POST /sessions key→token exchange, SessionStore dual-index with revoke_by_id, CLI Bearer injection (ONTOREF_TOKEN), ontoref setup
--gen-keys, install scripts, daemon config form roundtrip, ADR-004/005, on+re self-description update (fully-self-described), and landing page refresh.
2026-03-13 20:56:31 +00:00
|
|
|
/// All record IDs are prefixed with `slug` (`{slug}--{id}`) to ensure
|
|
|
|
|
/// multi-project isolation within shared tables. Records from different
|
|
|
|
|
/// projects never collide even when they share the same ontology ID.
|
2026-03-13 00:18:14 +00:00
|
|
|
#[cfg(feature = "db")]
|
|
|
|
|
pub async fn seed_ontology(
|
|
|
|
|
db: &stratum_db::StratumDb,
|
feat: unified auth model, project onboarding, install pipeline, config management
The full scope across this batch: POST /sessions key→token exchange, SessionStore dual-index with revoke_by_id, CLI Bearer injection (ONTOREF_TOKEN), ontoref setup
--gen-keys, install scripts, daemon config form roundtrip, ADR-004/005, on+re self-description update (fully-self-described), and landing page refresh.
2026-03-13 20:56:31 +00:00
|
|
|
slug: &str,
|
2026-03-13 00:18:14 +00:00
|
|
|
project_root: &Path,
|
|
|
|
|
cache: &NclCache,
|
|
|
|
|
import_path: Option<&str>,
|
|
|
|
|
) -> SeedReport {
|
|
|
|
|
let mut report = SeedReport {
|
|
|
|
|
nodes: 0,
|
|
|
|
|
edges: 0,
|
|
|
|
|
dimensions: 0,
|
|
|
|
|
membranes: 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let core_path = project_root.join(".ontology").join("core.ncl");
|
|
|
|
|
if core_path.exists() {
|
|
|
|
|
match cache.export(&core_path, import_path).await {
|
|
|
|
|
Ok((json, _)) => {
|
feat: unified auth model, project onboarding, install pipeline, config management
The full scope across this batch: POST /sessions key→token exchange, SessionStore dual-index with revoke_by_id, CLI Bearer injection (ONTOREF_TOKEN), ontoref setup
--gen-keys, install scripts, daemon config form roundtrip, ADR-004/005, on+re self-description update (fully-self-described), and landing page refresh.
2026-03-13 20:56:31 +00:00
|
|
|
report.nodes = seed_table_by_id(db, slug, "node", &json, "nodes").await;
|
|
|
|
|
report.edges = seed_edges(db, slug, &json).await;
|
2026-03-13 00:18:14 +00:00
|
|
|
}
|
|
|
|
|
Err(e) => warn!(error = %e, "seed: core.ncl export failed"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let state_path = project_root.join(".ontology").join("state.ncl");
|
|
|
|
|
if state_path.exists() {
|
|
|
|
|
match cache.export(&state_path, import_path).await {
|
|
|
|
|
Ok((json, _)) => {
|
feat: unified auth model, project onboarding, install pipeline, config management
The full scope across this batch: POST /sessions key→token exchange, SessionStore dual-index with revoke_by_id, CLI Bearer injection (ONTOREF_TOKEN), ontoref setup
--gen-keys, install scripts, daemon config form roundtrip, ADR-004/005, on+re self-description update (fully-self-described), and landing page refresh.
2026-03-13 20:56:31 +00:00
|
|
|
report.dimensions =
|
|
|
|
|
seed_table_by_id(db, slug, "dimension", &json, "dimensions").await;
|
2026-03-13 00:18:14 +00:00
|
|
|
}
|
|
|
|
|
Err(e) => warn!(error = %e, "seed: state.ncl export failed"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let gate_path = project_root.join(".ontology").join("gate.ncl");
|
|
|
|
|
if gate_path.exists() {
|
|
|
|
|
match cache.export(&gate_path, import_path).await {
|
|
|
|
|
Ok((json, _)) => {
|
feat: unified auth model, project onboarding, install pipeline, config management
The full scope across this batch: POST /sessions key→token exchange, SessionStore dual-index with revoke_by_id, CLI Bearer injection (ONTOREF_TOKEN), ontoref setup
--gen-keys, install scripts, daemon config form roundtrip, ADR-004/005, on+re self-description update (fully-self-described), and landing page refresh.
2026-03-13 20:56:31 +00:00
|
|
|
report.membranes = seed_table_by_id(db, slug, "membrane", &json, "membranes").await;
|
2026-03-13 00:18:14 +00:00
|
|
|
}
|
|
|
|
|
Err(e) => warn!(error = %e, "seed: gate.ncl export failed"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
info!(
|
feat: unified auth model, project onboarding, install pipeline, config management
The full scope across this batch: POST /sessions key→token exchange, SessionStore dual-index with revoke_by_id, CLI Bearer injection (ONTOREF_TOKEN), ontoref setup
--gen-keys, install scripts, daemon config form roundtrip, ADR-004/005, on+re self-description update (fully-self-described), and landing page refresh.
2026-03-13 20:56:31 +00:00
|
|
|
slug,
|
2026-03-13 00:18:14 +00:00
|
|
|
nodes = report.nodes,
|
|
|
|
|
edges = report.edges,
|
|
|
|
|
dimensions = report.dimensions,
|
|
|
|
|
membranes = report.membranes,
|
|
|
|
|
"ontology seeded from local files"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
report
|
|
|
|
|
}
|
|
|
|
|
|
feat: unified auth model, project onboarding, install pipeline, config management
The full scope across this batch: POST /sessions key→token exchange, SessionStore dual-index with revoke_by_id, CLI Bearer injection (ONTOREF_TOKEN), ontoref setup
--gen-keys, install scripts, daemon config form roundtrip, ADR-004/005, on+re self-description update (fully-self-described), and landing page refresh.
2026-03-13 20:56:31 +00:00
|
|
|
/// Seed ontology tables from a pre-exported JSON payload (push-based sync).
|
|
|
|
|
///
|
|
|
|
|
/// `slug` scopes all record IDs — see `seed_ontology` for the isolation model.
|
|
|
|
|
#[cfg(feature = "db")]
|
|
|
|
|
pub async fn seed_from_payload(
|
|
|
|
|
db: &stratum_db::StratumDb,
|
|
|
|
|
slug: &str,
|
|
|
|
|
payload: &serde_json::Value,
|
|
|
|
|
) -> SeedReport {
|
|
|
|
|
let mut report = SeedReport {
|
|
|
|
|
nodes: 0,
|
|
|
|
|
edges: 0,
|
|
|
|
|
dimensions: 0,
|
|
|
|
|
membranes: 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if let Some(core) = payload.get("core") {
|
|
|
|
|
report.nodes = seed_table_by_id(db, slug, "node", core, "nodes").await;
|
|
|
|
|
report.edges = seed_edges(db, slug, core).await;
|
|
|
|
|
}
|
|
|
|
|
if let Some(state) = payload.get("state") {
|
|
|
|
|
report.dimensions = seed_table_by_id(db, slug, "dimension", state, "dimensions").await;
|
|
|
|
|
}
|
|
|
|
|
if let Some(gate) = payload.get("gate") {
|
|
|
|
|
report.membranes = seed_table_by_id(db, slug, "membrane", gate, "membranes").await;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
info!(
|
|
|
|
|
slug,
|
|
|
|
|
nodes = report.nodes,
|
|
|
|
|
edges = report.edges,
|
|
|
|
|
dimensions = report.dimensions,
|
|
|
|
|
membranes = report.membranes,
|
|
|
|
|
"ontology seeded from push payload"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
report
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-13 00:18:14 +00:00
|
|
|
/// Generic upsert: extract `json[array_key]`, iterate, use each item's `id`
|
feat: unified auth model, project onboarding, install pipeline, config management
The full scope across this batch: POST /sessions key→token exchange, SessionStore dual-index with revoke_by_id, CLI Bearer injection (ONTOREF_TOKEN), ontoref setup
--gen-keys, install scripts, daemon config form roundtrip, ADR-004/005, on+re self-description update (fully-self-described), and landing page refresh.
2026-03-13 20:56:31 +00:00
|
|
|
/// field as record key prefixed with `{slug}--` for multi-project isolation.
|
2026-03-13 00:18:14 +00:00
|
|
|
#[cfg(feature = "db")]
|
|
|
|
|
async fn seed_table_by_id(
|
|
|
|
|
db: &stratum_db::StratumDb,
|
feat: unified auth model, project onboarding, install pipeline, config management
The full scope across this batch: POST /sessions key→token exchange, SessionStore dual-index with revoke_by_id, CLI Bearer injection (ONTOREF_TOKEN), ontoref setup
--gen-keys, install scripts, daemon config form roundtrip, ADR-004/005, on+re self-description update (fully-self-described), and landing page refresh.
2026-03-13 20:56:31 +00:00
|
|
|
slug: &str,
|
2026-03-13 00:18:14 +00:00
|
|
|
table: &str,
|
|
|
|
|
json: &Value,
|
|
|
|
|
array_key: &str,
|
|
|
|
|
) -> usize {
|
|
|
|
|
let items = match json.get(array_key).and_then(|a| a.as_array()) {
|
|
|
|
|
Some(arr) => arr,
|
|
|
|
|
None => return 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let mut count = 0;
|
|
|
|
|
for item in items {
|
feat: unified auth model, project onboarding, install pipeline, config management
The full scope across this batch: POST /sessions key→token exchange, SessionStore dual-index with revoke_by_id, CLI Bearer injection (ONTOREF_TOKEN), ontoref setup
--gen-keys, install scripts, daemon config form roundtrip, ADR-004/005, on+re self-description update (fully-self-described), and landing page refresh.
2026-03-13 20:56:31 +00:00
|
|
|
let raw_id = match item.get("id").and_then(|i| i.as_str()) {
|
2026-03-13 00:18:14 +00:00
|
|
|
Some(id) => id,
|
|
|
|
|
None => continue,
|
|
|
|
|
};
|
feat: unified auth model, project onboarding, install pipeline, config management
The full scope across this batch: POST /sessions key→token exchange, SessionStore dual-index with revoke_by_id, CLI Bearer injection (ONTOREF_TOKEN), ontoref setup
--gen-keys, install scripts, daemon config form roundtrip, ADR-004/005, on+re self-description update (fully-self-described), and landing page refresh.
2026-03-13 20:56:31 +00:00
|
|
|
let id = format!("{slug}--{raw_id}");
|
|
|
|
|
// Rewrite the `id` field to the prefixed value so the content is
|
|
|
|
|
// consistent with the record specifier (`table:id`). SurrealDB rejects
|
|
|
|
|
// upserts where the `id` field in the content differs from the
|
|
|
|
|
// targeted record ID.
|
|
|
|
|
let mut record = item.clone();
|
|
|
|
|
record["id"] = Value::String(id.clone());
|
|
|
|
|
if let Err(e) = db.upsert(table, &id, record).await {
|
2026-03-13 00:18:14 +00:00
|
|
|
warn!(table, id, error = %e, "seed: upsert failed");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
count += 1;
|
|
|
|
|
}
|
|
|
|
|
count
|
|
|
|
|
}
|
|
|
|
|
|
feat: unified auth model, project onboarding, install pipeline, config management
The full scope across this batch: POST /sessions key→token exchange, SessionStore dual-index with revoke_by_id, CLI Bearer injection (ONTOREF_TOKEN), ontoref setup
--gen-keys, install scripts, daemon config form roundtrip, ADR-004/005, on+re self-description update (fully-self-described), and landing page refresh.
2026-03-13 20:56:31 +00:00
|
|
|
/// Edges use a deterministic compound key: `{slug}--{from}--{kind}--{to}`.
|
2026-03-13 00:18:14 +00:00
|
|
|
#[cfg(feature = "db")]
|
feat: unified auth model, project onboarding, install pipeline, config management
The full scope across this batch: POST /sessions key→token exchange, SessionStore dual-index with revoke_by_id, CLI Bearer injection (ONTOREF_TOKEN), ontoref setup
--gen-keys, install scripts, daemon config form roundtrip, ADR-004/005, on+re self-description update (fully-self-described), and landing page refresh.
2026-03-13 20:56:31 +00:00
|
|
|
async fn seed_edges(db: &stratum_db::StratumDb, slug: &str, core_json: &Value) -> usize {
|
2026-03-13 00:18:14 +00:00
|
|
|
let edges = match core_json.get("edges").and_then(|e| e.as_array()) {
|
|
|
|
|
Some(arr) => arr,
|
|
|
|
|
None => return 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let mut count = 0;
|
|
|
|
|
for edge in edges {
|
|
|
|
|
let from = edge.get("from").and_then(|f| f.as_str()).unwrap_or("");
|
|
|
|
|
let to = edge.get("to").and_then(|t| t.as_str()).unwrap_or("");
|
|
|
|
|
let kind = edge
|
|
|
|
|
.get("kind")
|
|
|
|
|
.and_then(|k| k.as_str())
|
|
|
|
|
.unwrap_or("unknown");
|
feat: unified auth model, project onboarding, install pipeline, config management
The full scope across this batch: POST /sessions key→token exchange, SessionStore dual-index with revoke_by_id, CLI Bearer injection (ONTOREF_TOKEN), ontoref setup
--gen-keys, install scripts, daemon config form roundtrip, ADR-004/005, on+re self-description update (fully-self-described), and landing page refresh.
2026-03-13 20:56:31 +00:00
|
|
|
let edge_id = format!("{slug}--{from}--{kind}--{to}");
|
2026-03-13 00:18:14 +00:00
|
|
|
|
|
|
|
|
if let Err(e) = db.upsert("edge", &edge_id, edge.clone()).await {
|
|
|
|
|
warn!(edge_id, error = %e, "seed: edge upsert failed");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
count += 1;
|
|
|
|
|
}
|
|
|
|
|
count
|
|
|
|
|
}
|