From f1a448d6dcd261d14318669c0b3be9ed58d3d97a Mon Sep 17 00:00:00 2001 From: altair823 Date: Sat, 2 May 2026 04:01:35 +0000 Subject: [PATCH] =?UTF-8?q?refactor(rename):=20kb=20=E2=86=92=20kebab=20?= =?UTF-8?q?=E2=80=94=20binary,=20env=20vars,=20XDG=20paths,=20file=20renam?= =?UTF-8?q?es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 두 번째 commit. 사용자 facing surface (CLI binary, env vars, XDG paths) + 코드 안 single-letter token (`KB_`, `kb.sqlite`, `/kb/`, tracing target) 일괄 rename. 그리고 3 개 file rename: - 디자인 doc `2026-04-27-kb-final-form-design.md` → `2026-04-27-kebab-final-form-design.md` - 최초 보고서 `kb_local_rust_report.md` → `kebab_local_rust_report.md` - workspace ignore `.kbignore` → `.kebabignore` ## 변경 - `crates/kebab-cli/Cargo.toml`: `[[bin]] name = "kb"` → `"kebab"`. - `crates/kebab-cli/src/main.rs`: `#[command(name = "kb", …)]` → `name = "kebab"`. - 모든 `KB_*` env var (코드 + doc + 테스트) → `KEBAB_*`. apply_env prefix 매칭 + 30+ 개 setting 키 모두. - XDG paths: `~/.config/kb` / `~/.local/share/kb` / `~/.cache/kb` / `~/.local/state/kb` → `~/.config/kebab` 등. config defaults + expand_path tests + paths.rs 의 hardcode 모두. - SQLite filename: `kb.sqlite` → `kebab.sqlite` (`SQLITE_FILE` const + 테스트 hardcode 모두). - tracing target: `target: "kb-*"` → `"kebab-*"` (10+ 곳). - snapshot fixture: `.kbignore` → `.kebabignore` (`fixtures/source-fs/ tree-1.snapshot.json` 갱신). ## 검증 - `cargo test --workspace -j 1` clean (linker OOM 회피 위해 직렬). - `cargo clippy --workspace --all-targets -- -D warnings` clean. 다음 commit 에서 docs sweep. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) --- crates/kebab-app/src/lib.rs | 22 +-- crates/kebab-app/src/logging.rs | 2 +- crates/kebab-app/tests/ingest_lexical.rs | 4 +- crates/kebab-chunk/src/md_heading_v1.rs | 2 +- crates/kebab-cli/Cargo.toml | 2 +- crates/kebab-cli/src/main.rs | 2 +- crates/kebab-config/src/lib.rs | 136 +++++++++--------- crates/kebab-config/src/paths.rs | 14 +- crates/kebab-core/src/lib.rs | 2 +- crates/kebab-embed-local/src/lib.rs | 8 +- crates/kebab-embed-local/tests/embed_model.rs | 2 +- crates/kebab-embed/src/lib.rs | 2 +- crates/kebab-eval/src/metrics.rs | 8 +- crates/kebab-eval/src/runner.rs | 12 +- .../kebab-eval/tests/metrics_and_compare.rs | 6 +- crates/kebab-eval/tests/runner.rs | 12 +- crates/kebab-llm-local/src/lib.rs | 2 +- crates/kebab-llm-local/tests/integration.rs | 4 +- crates/kebab-llm/src/lib.rs | 2 +- crates/kebab-normalize/src/lib.rs | 2 +- crates/kebab-rag/src/pipeline.rs | 14 +- crates/kebab-search/src/hybrid.rs | 4 +- crates/kebab-search/tests/hybrid.rs | 12 +- crates/kebab-search/tests/lexical.rs | 8 +- crates/kebab-source-fs/src/connector.rs | 18 +-- crates/kebab-source-fs/src/lib.rs | 4 +- crates/kebab-source-fs/src/walker.rs | 14 +- .../kebab-source-fs/tests/snapshot_tree1.rs | 14 +- crates/kebab-store-sqlite/src/store.rs | 6 +- crates/kebab-store-sqlite/tests/common/mod.rs | 2 +- crates/kebab-store-sqlite/tests/fts.rs | 4 +- crates/kebab-store-vector/src/lib.rs | 2 +- crates/kebab-store-vector/src/store.rs | 12 +- crates/kebab-store-vector/tests/snapshot.rs | 8 +- ... => 2026-04-27-kebab-final-form-design.md} | 0 fixtures/source-fs/tree-1.snapshot.json | 2 +- .../tree-1/{.kbignore => .kebabignore} | 0 ...st_report.md => kebab_local_rust_report.md | 0 38 files changed, 185 insertions(+), 185 deletions(-) rename docs/superpowers/specs/{2026-04-27-kb-final-form-design.md => 2026-04-27-kebab-final-form-design.md} (100%) rename fixtures/source-fs/tree-1/{.kbignore => .kebabignore} (100%) rename kb_local_rust_report.md => kebab_local_rust_report.md (100%) diff --git a/crates/kebab-app/src/lib.rs b/crates/kebab-app/src/lib.rs index f5e6727..15d6fc9 100644 --- a/crates/kebab-app/src/lib.rs +++ b/crates/kebab-app/src/lib.rs @@ -61,7 +61,7 @@ pub use app::App; /// Kept in lock-step with the literal used in the `kb-store-sqlite` /// idempotency / round-trip tests so the version label written by the /// app and the one used in cross-crate fixtures match. -const KB_PARSE_MD_VERSION: &str = "pulldown-cmark-0.x"; +const KEBAB_PARSE_MD_VERSION: &str = "pulldown-cmark-0.x"; /// Caller-supplied knobs for one [`ask`] invocation. /// @@ -187,7 +187,7 @@ pub fn ingest_with_config( .context("kb-app::ingest: ensure Lance table")?; } - let parser_version = ParserVersion(KB_PARSE_MD_VERSION.to_string()); + let parser_version = ParserVersion(KEBAB_PARSE_MD_VERSION.to_string()); let chunk_policy = chunk_policy_from_config(&app.config); // Pre-load every existing doc_id so we can label `IngestItem.kind` @@ -236,7 +236,7 @@ pub fn ingest_with_config( Ok(i) => i, Err(e) => { tracing::error!( - target: "kb-app", + target: "kebab-app", path = %asset.workspace_path.0, error = %e, "kb-app::ingest: per-file fatal" @@ -318,7 +318,7 @@ pub fn ingest_with_config( progress, ) { tracing::warn!( - target: "kb-app", + target: "kebab-app", error = %e, "kb-app::ingest: JobRepo::update_progress failed" ); @@ -330,7 +330,7 @@ pub fn ingest_with_config( None, ) { tracing::warn!( - target: "kb-app", + target: "kebab-app", error = %e, "kb-app::ingest: JobRepo::finish failed" ); @@ -338,7 +338,7 @@ pub fn ingest_with_config( } Err(e) => { tracing::warn!( - target: "kb-app", + target: "kebab-app", error = %e, "kb-app::ingest: JobRepo::create failed; run not recorded in `jobs`" ); @@ -361,7 +361,7 @@ pub fn ingest_with_config( Ok(s) => Some(s), Err(e) => { tracing::warn!( - target: "kb-app", + target: "kebab-app", error = %e, "kb-app::ingest: failed to serialize items_json; storing NULL" ); @@ -385,14 +385,14 @@ pub fn ingest_with_config( }; if let Err(e) = app.sqlite.record_ingest_run(&row) { tracing::warn!( - target: "kb-app", + target: "kebab-app", error = %e, "kb-app::ingest: record_ingest_run failed" ); } tracing::info!( - target: "kb-app", + target: "kebab-app", scanned = scanned_count, new = new_count, updated = updated_count, @@ -448,7 +448,7 @@ fn ingest_one_asset( existing_doc_ids: &std::collections::HashSet, ) -> anyhow::Result { tracing::debug!( - target: "kb-app::ingest", + target: "kebab-app::ingest", path = %asset.workspace_path.0, "processing asset" ); @@ -795,7 +795,7 @@ pub fn doctor_with_config_path(config_path: Option<&std::path::Path>) -> anyhow: // data_dir_writable — probe the resolved storage.data_dir from the // loaded config when present, else the XDG default. Apply env - // overrides so KB_STORAGE_DATA_DIR is respected too. + // overrides so KEBAB_STORAGE_DATA_DIR is respected too. let data_dir = match loaded_cfg.as_ref() { Some(c) => { // Re-apply env overrides on top so the same precedence as diff --git a/crates/kebab-app/src/logging.rs b/crates/kebab-app/src/logging.rs index a78c4c9..1b4baeb 100644 --- a/crates/kebab-app/src/logging.rs +++ b/crates/kebab-app/src/logging.rs @@ -1,6 +1,6 @@ //! Tracing initialization helper for `kb-cli`. //! -//! Daily-rolling file appender at `~/.local/state/kb/logs/` per task spec. +//! Daily-rolling file appender at `~/.local/state/kebab/logs/` per task spec. //! Returns a `WorkerGuard` that the caller must keep alive until program //! exit (so buffered log lines flush). diff --git a/crates/kebab-app/tests/ingest_lexical.rs b/crates/kebab-app/tests/ingest_lexical.rs index f3ae13f..9999552 100644 --- a/crates/kebab-app/tests/ingest_lexical.rs +++ b/crates/kebab-app/tests/ingest_lexical.rs @@ -87,8 +87,8 @@ fn ingest_records_ingest_runs_row_with_aggregate_counts() { assert_eq!(report.scanned, 3); let db_path = std::path::PathBuf::from(&env.config.storage.data_dir) - .join("kb.sqlite"); - let conn = rusqlite::Connection::open(&db_path).expect("open kb.sqlite"); + .join("kebab.sqlite"); + let conn = rusqlite::Connection::open(&db_path).expect("open kebab.sqlite"); let (scanned, new_c, updated, skipped, errors, items_json): ( i64, i64, diff --git a/crates/kebab-chunk/src/md_heading_v1.rs b/crates/kebab-chunk/src/md_heading_v1.rs index f9054b7..f29a4d4 100644 --- a/crates/kebab-chunk/src/md_heading_v1.rs +++ b/crates/kebab-chunk/src/md_heading_v1.rs @@ -186,7 +186,7 @@ impl Chunker for MdHeadingV1Chunker { flush(&mut acc, doc, &chunker_version, &policy_hash, &mut out); tracing::debug!( - target: "kb-chunk", + target: "kebab-chunk", doc_id = %doc.doc_id, chunks = out.len(), "md-heading-v1 chunked", diff --git a/crates/kebab-cli/Cargo.toml b/crates/kebab-cli/Cargo.toml index 591b60f..abd9da6 100644 --- a/crates/kebab-cli/Cargo.toml +++ b/crates/kebab-cli/Cargo.toml @@ -8,7 +8,7 @@ repository = { workspace = true } description = "kb command-line interface" [[bin]] -name = "kb" +name = "kebab" path = "src/main.rs" [dependencies] diff --git a/crates/kebab-cli/src/main.rs b/crates/kebab-cli/src/main.rs index 25b9624..e2df18a 100644 --- a/crates/kebab-cli/src/main.rs +++ b/crates/kebab-cli/src/main.rs @@ -11,7 +11,7 @@ use kebab_app::doctor_signal::{DoctorUnhealthy, NoHitSignal, RefusalSignal}; mod wire; #[derive(Parser, Debug)] -#[command(name = "kb", version, about = "personal local knowledge base")] +#[command(name = "kebab", version, about = "personal local knowledge base")] struct Cli { /// Path to a non-default `config.toml`. #[arg(long, global = true)] diff --git a/crates/kebab-config/src/lib.rs b/crates/kebab-config/src/lib.rs index 4620ff1..9452efb 100644 --- a/crates/kebab-config/src/lib.rs +++ b/crates/kebab-config/src/lib.rs @@ -1,6 +1,6 @@ //! `kb-config` — `Config` schema and XDG path resolution (§6). //! -//! Layer order (`Config::load`): defaults → file → env (`KB_
_`). +//! Layer order (`Config::load`): defaults → file → env (`KEBAB_
_`). //! CLI overrides land later, applied by `kb-cli` after `Config::load`. use std::collections::HashMap; @@ -113,8 +113,8 @@ impl Config { ], }, storage: StorageCfg { - data_dir: "${XDG_DATA_HOME:-~/.local/share}/kb".to_string(), - sqlite: "{data_dir}/kb.sqlite".to_string(), + data_dir: "${XDG_DATA_HOME:-~/.local/share}/kebab".to_string(), + sqlite: "{data_dir}/kebab.sqlite".to_string(), vector_dir: "{data_dir}/lancedb".to_string(), asset_dir: "{data_dir}/assets".to_string(), artifact_dir: "{data_dir}/artifacts".to_string(), @@ -191,139 +191,139 @@ impl Config { Ok(cfg) } - /// Apply `KB_
_` env overrides. Unknown keys are ignored. + /// Apply `KEBAB_
_` env overrides. Unknown keys are ignored. /// /// The mapping is an explicit grep-friendly whitelist — one match arm /// per leaf key in `Config`. Booleans accept `1` / `true` / `yes` /// (case-insensitive) for true and anything else for false. Numeric /// keys silently keep their prior value if the env value fails to - /// parse, so a malformed `KB_*` cannot crash startup. + /// parse, so a malformed `KEBAB_*` cannot crash startup. pub fn apply_env(mut self, env: &HashMap) -> Self { for (k, v) in env { - if !k.starts_with("KB_") { + if !k.starts_with("KEBAB_") { continue; } match k.as_str() { // workspace - "KB_WORKSPACE_ROOT" => self.workspace.root = v.clone(), + "KEBAB_WORKSPACE_ROOT" => self.workspace.root = v.clone(), // storage - "KB_STORAGE_DATA_DIR" => self.storage.data_dir = v.clone(), - "KB_STORAGE_SQLITE" => self.storage.sqlite = v.clone(), - "KB_STORAGE_VECTOR_DIR" => self.storage.vector_dir = v.clone(), - "KB_STORAGE_ASSET_DIR" => self.storage.asset_dir = v.clone(), - "KB_STORAGE_ARTIFACT_DIR" => self.storage.artifact_dir = v.clone(), - "KB_STORAGE_MODEL_DIR" => self.storage.model_dir = v.clone(), - "KB_STORAGE_RUNS_DIR" => self.storage.runs_dir = v.clone(), - "KB_STORAGE_COPY_THRESHOLD_MB" => { + "KEBAB_STORAGE_DATA_DIR" => self.storage.data_dir = v.clone(), + "KEBAB_STORAGE_SQLITE" => self.storage.sqlite = v.clone(), + "KEBAB_STORAGE_VECTOR_DIR" => self.storage.vector_dir = v.clone(), + "KEBAB_STORAGE_ASSET_DIR" => self.storage.asset_dir = v.clone(), + "KEBAB_STORAGE_ARTIFACT_DIR" => self.storage.artifact_dir = v.clone(), + "KEBAB_STORAGE_MODEL_DIR" => self.storage.model_dir = v.clone(), + "KEBAB_STORAGE_RUNS_DIR" => self.storage.runs_dir = v.clone(), + "KEBAB_STORAGE_COPY_THRESHOLD_MB" => { if let Ok(n) = v.parse::() { self.storage.copy_threshold_mb = n; } } // indexing - "KB_INDEXING_MAX_PARALLEL_EXTRACTORS" => { + "KEBAB_INDEXING_MAX_PARALLEL_EXTRACTORS" => { if let Ok(n) = v.parse::() { self.indexing.max_parallel_extractors = n; } } - "KB_INDEXING_MAX_PARALLEL_EMBEDDINGS" => { + "KEBAB_INDEXING_MAX_PARALLEL_EMBEDDINGS" => { if let Ok(n) = v.parse::() { self.indexing.max_parallel_embeddings = n; } } - "KB_INDEXING_WATCH_FILESYSTEM" => { + "KEBAB_INDEXING_WATCH_FILESYSTEM" => { self.indexing.watch_filesystem = parse_bool(v); } // chunking - "KB_CHUNKING_TARGET_TOKENS" => { + "KEBAB_CHUNKING_TARGET_TOKENS" => { if let Ok(n) = v.parse::() { self.chunking.target_tokens = n; } } - "KB_CHUNKING_OVERLAP_TOKENS" => { + "KEBAB_CHUNKING_OVERLAP_TOKENS" => { if let Ok(n) = v.parse::() { self.chunking.overlap_tokens = n; } } - "KB_CHUNKING_RESPECT_MARKDOWN_HEADINGS" => { + "KEBAB_CHUNKING_RESPECT_MARKDOWN_HEADINGS" => { self.chunking.respect_markdown_headings = parse_bool(v); } - "KB_CHUNKING_CHUNKER_VERSION" => self.chunking.chunker_version = v.clone(), + "KEBAB_CHUNKING_CHUNKER_VERSION" => self.chunking.chunker_version = v.clone(), // models.embedding - "KB_MODELS_EMBEDDING_PROVIDER" => self.models.embedding.provider = v.clone(), - "KB_MODELS_EMBEDDING_MODEL" => self.models.embedding.model = v.clone(), - "KB_MODELS_EMBEDDING_VERSION" => self.models.embedding.version = v.clone(), - "KB_MODELS_EMBEDDING_DIMENSIONS" => { + "KEBAB_MODELS_EMBEDDING_PROVIDER" => self.models.embedding.provider = v.clone(), + "KEBAB_MODELS_EMBEDDING_MODEL" => self.models.embedding.model = v.clone(), + "KEBAB_MODELS_EMBEDDING_VERSION" => self.models.embedding.version = v.clone(), + "KEBAB_MODELS_EMBEDDING_DIMENSIONS" => { if let Ok(n) = v.parse::() { self.models.embedding.dimensions = n; } } - "KB_MODELS_EMBEDDING_BATCH_SIZE" => { + "KEBAB_MODELS_EMBEDDING_BATCH_SIZE" => { if let Ok(n) = v.parse::() { self.models.embedding.batch_size = n; } } // models.llm - "KB_MODELS_LLM_PROVIDER" => self.models.llm.provider = v.clone(), - "KB_MODELS_LLM_MODEL" => self.models.llm.model = v.clone(), - "KB_MODELS_LLM_CONTEXT_TOKENS" => { + "KEBAB_MODELS_LLM_PROVIDER" => self.models.llm.provider = v.clone(), + "KEBAB_MODELS_LLM_MODEL" => self.models.llm.model = v.clone(), + "KEBAB_MODELS_LLM_CONTEXT_TOKENS" => { if let Ok(n) = v.parse::() { self.models.llm.context_tokens = n; } } - "KB_MODELS_LLM_ENDPOINT" => self.models.llm.endpoint = v.clone(), - "KB_MODELS_LLM_TEMPERATURE" => { + "KEBAB_MODELS_LLM_ENDPOINT" => self.models.llm.endpoint = v.clone(), + "KEBAB_MODELS_LLM_TEMPERATURE" => { if let Ok(f) = v.parse::() { self.models.llm.temperature = f; } } - "KB_MODELS_LLM_SEED" => { + "KEBAB_MODELS_LLM_SEED" => { if let Ok(n) = v.parse::() { self.models.llm.seed = n; } } // search - "KB_SEARCH_DEFAULT_K" => { + "KEBAB_SEARCH_DEFAULT_K" => { if let Ok(n) = v.parse::() { self.search.default_k = n; } } - "KB_SEARCH_HYBRID_FUSION" => self.search.hybrid_fusion = v.clone(), - "KB_SEARCH_RRF_K" => { + "KEBAB_SEARCH_HYBRID_FUSION" => self.search.hybrid_fusion = v.clone(), + "KEBAB_SEARCH_RRF_K" => { if let Ok(n) = v.parse::() { self.search.rrf_k = n; } } - "KB_SEARCH_SNIPPET_CHARS" => { + "KEBAB_SEARCH_SNIPPET_CHARS" => { if let Ok(n) = v.parse::() { self.search.snippet_chars = n; } } // rag - "KB_RAG_PROMPT_TEMPLATE_VERSION" => { + "KEBAB_RAG_PROMPT_TEMPLATE_VERSION" => { self.rag.prompt_template_version = v.clone(); } - "KB_RAG_SCORE_GATE" => { + "KEBAB_RAG_SCORE_GATE" => { if let Ok(f) = v.parse::() { self.rag.score_gate = f; } } - "KB_RAG_EXPLAIN_DEFAULT" => { + "KEBAB_RAG_EXPLAIN_DEFAULT" => { self.rag.explain_default = parse_bool(v); } - "KB_RAG_MAX_CONTEXT_TOKENS" => { + "KEBAB_RAG_MAX_CONTEXT_TOKENS" => { if let Ok(n) = v.parse::() { self.rag.max_context_tokens = n; } } - // Unknown KB_* keys are silently ignored — see + // Unknown KEBAB_* keys are silently ignored — see // `env_unknown_key_is_ignored` test. _ => {} } @@ -331,58 +331,58 @@ impl Config { self } - /// `~/.config/kb/config.toml` (honors `XDG_CONFIG_HOME`). + /// `~/.config/kebab/config.toml` (honors `XDG_CONFIG_HOME`). pub fn xdg_config_path() -> PathBuf { if let Ok(custom) = std::env::var("XDG_CONFIG_HOME") { if !custom.is_empty() { - return PathBuf::from(custom).join("kb").join("config.toml"); + return PathBuf::from(custom).join("kebab").join("config.toml"); } } match dirs::config_dir() { - Some(d) => d.join("kb").join("config.toml"), - None => PathBuf::from("./kb/config.toml"), + Some(d) => d.join("kebab").join("config.toml"), + None => PathBuf::from("./kebab/config.toml"), } } - /// `~/.local/share/kb` (honors `XDG_DATA_HOME`). + /// `~/.local/share/kebab` (honors `XDG_DATA_HOME`). pub fn xdg_data_dir() -> PathBuf { if let Ok(custom) = std::env::var("XDG_DATA_HOME") { if !custom.is_empty() { - return PathBuf::from(custom).join("kb"); + return PathBuf::from(custom).join("kebab"); } } match dirs::data_dir() { - Some(d) => d.join("kb"), - None => PathBuf::from("./kb-data"), + Some(d) => d.join("kebab"), + None => PathBuf::from("./kebab-data"), } } - /// `~/.cache/kb` (honors `XDG_CACHE_HOME`). + /// `~/.cache/kebab` (honors `XDG_CACHE_HOME`). pub fn xdg_cache_dir() -> PathBuf { if let Ok(custom) = std::env::var("XDG_CACHE_HOME") { if !custom.is_empty() { - return PathBuf::from(custom).join("kb"); + return PathBuf::from(custom).join("kebab"); } } match dirs::cache_dir() { - Some(d) => d.join("kb"), - None => PathBuf::from("./kb-cache"), + Some(d) => d.join("kebab"), + None => PathBuf::from("./kebab-cache"), } } - /// `~/.local/state/kb` (honors `XDG_STATE_HOME`). + /// `~/.local/state/kebab` (honors `XDG_STATE_HOME`). pub fn xdg_state_dir() -> PathBuf { if let Ok(custom) = std::env::var("XDG_STATE_HOME") { if !custom.is_empty() { - return PathBuf::from(custom).join("kb"); + return PathBuf::from(custom).join("kebab"); } } // `dirs` doesn't expose state_dir on all platforms; fall back to - // `$HOME/.local/state/kb` if XDG_STATE_HOME is unset. + // `$HOME/.local/state/kebab` if XDG_STATE_HOME is unset. if let Some(home) = dirs::home_dir() { - return home.join(".local").join("state").join("kb"); + return home.join(".local").join("state").join("kebab"); } - PathBuf::from("./kb-state") + PathBuf::from("./kebab-state") } } @@ -417,7 +417,7 @@ mod tests { #[test] fn env_override_score_gate() { let mut env = HashMap::new(); - env.insert("KB_RAG_SCORE_GATE".to_string(), "0.5".to_string()); + env.insert("KEBAB_RAG_SCORE_GATE".to_string(), "0.5".to_string()); let c = Config::defaults().apply_env(&env); assert!((c.rag.score_gate - 0.5).abs() < 1e-6); } @@ -425,7 +425,7 @@ mod tests { #[test] fn env_override_search_k() { let mut env = HashMap::new(); - env.insert("KB_SEARCH_DEFAULT_K".to_string(), "25".to_string()); + env.insert("KEBAB_SEARCH_DEFAULT_K".to_string(), "25".to_string()); let c = Config::defaults().apply_env(&env); assert_eq!(c.search.default_k, 25); } @@ -434,7 +434,7 @@ mod tests { fn env_unknown_key_is_ignored() { let baseline = Config::defaults(); let mut env = HashMap::new(); - env.insert("KB_NOPE_FOO".to_string(), "garbage".to_string()); + env.insert("KEBAB_NOPE_FOO".to_string(), "garbage".to_string()); let c = Config::defaults().apply_env(&env); assert_eq!(c, baseline); } @@ -442,7 +442,7 @@ mod tests { #[test] fn env_overrides_chunking_target_tokens() { let mut env = HashMap::new(); - env.insert("KB_CHUNKING_TARGET_TOKENS".to_string(), "777".to_string()); + env.insert("KEBAB_CHUNKING_TARGET_TOKENS".to_string(), "777".to_string()); let c = Config::defaults().apply_env(&env); assert_eq!(c.chunking.target_tokens, 777); } @@ -451,10 +451,10 @@ mod tests { fn env_overrides_models_llm_endpoint_and_temperature() { let mut env = HashMap::new(); env.insert( - "KB_MODELS_LLM_ENDPOINT".to_string(), + "KEBAB_MODELS_LLM_ENDPOINT".to_string(), "http://10.0.0.1:11434".to_string(), ); - env.insert("KB_MODELS_LLM_TEMPERATURE".to_string(), "0.7".to_string()); + env.insert("KEBAB_MODELS_LLM_TEMPERATURE".to_string(), "0.7".to_string()); let c = Config::defaults().apply_env(&env); assert_eq!(c.models.llm.endpoint, "http://10.0.0.1:11434"); assert!((c.models.llm.temperature - 0.7).abs() < 1e-6); @@ -464,7 +464,7 @@ mod tests { fn env_overrides_indexing_watch_filesystem_bool() { let mut env = HashMap::new(); env.insert( - "KB_INDEXING_WATCH_FILESYSTEM".to_string(), + "KEBAB_INDEXING_WATCH_FILESYSTEM".to_string(), "true".to_string(), ); let c = Config::defaults().apply_env(&env); @@ -477,10 +477,10 @@ mod tests { let prev = std::env::var("XDG_CONFIG_HOME").ok(); // SAFETY: tests in this module run sequentially; we restore below. unsafe { - std::env::set_var("XDG_CONFIG_HOME", "/tmp/kbtest-xdg-config"); + std::env::set_var("XDG_CONFIG_HOME", "/tmp/kebabtest-xdg-config"); } let p = Config::xdg_config_path(); - assert_eq!(p, PathBuf::from("/tmp/kbtest-xdg-config/kb/config.toml")); + assert_eq!(p, PathBuf::from("/tmp/kebabtest-xdg-config/kebab/config.toml")); // SAFETY: scope-local restore. unsafe { match prev { diff --git a/crates/kebab-config/src/paths.rs b/crates/kebab-config/src/paths.rs index f36c6dc..bc39ca6 100644 --- a/crates/kebab-config/src/paths.rs +++ b/crates/kebab-config/src/paths.rs @@ -1,7 +1,7 @@ //! Shared path expansion helper. //! //! `Config::storage.*` fields are stored as raw template strings (e.g. -//! `${XDG_DATA_HOME:-~/.local/share}/kb`, `{data_dir}/runs`). Every +//! `${XDG_DATA_HOME:-~/.local/share}/kebab`, `{data_dir}/runs`). Every //! crate that turns one of those strings into a real filesystem path //! needs to apply the same set of substitutions; this module is the //! single source of truth so the behavior cannot drift. @@ -133,8 +133,8 @@ mod tests { // SAFETY: lock held for the duration of this test. unsafe { std::env::set_var("XDG_DATA_HOME", "/custom/path") }; - let p = expand_path("${XDG_DATA_HOME:-~/.local/share}/kb", ""); - assert_eq!(p, PathBuf::from("/custom/path/kb")); + let p = expand_path("${XDG_DATA_HOME:-~/.local/share}/kebab", ""); + assert_eq!(p, PathBuf::from("/custom/path/kebab")); } #[test] @@ -145,8 +145,8 @@ mod tests { unsafe { std::env::remove_var("XDG_DATA_HOME") }; let home = std::env::var("HOME").expect("HOME must be set in tests"); - let expected = PathBuf::from(home).join(".local/share/kb"); - let p = expand_path("${XDG_DATA_HOME:-~/.local/share}/kb", ""); + let expected = PathBuf::from(home).join(".local/share/kebab"); + let p = expand_path("${XDG_DATA_HOME:-~/.local/share}/kebab", ""); assert_eq!(p, expected); } @@ -180,7 +180,7 @@ mod tests { // SAFETY: lock held for the duration of this test. unsafe { std::env::set_var("XDG_DATA_HOME", "/xdg/data") }; - let p = expand_path("{data_dir}/runs", "/xdg/data/kb"); - assert_eq!(p, PathBuf::from("/xdg/data/kb/runs")); + let p = expand_path("{data_dir}/runs", "/xdg/data/kebab"); + assert_eq!(p, PathBuf::from("/xdg/data/kebab/runs")); } } diff --git a/crates/kebab-core/src/lib.rs b/crates/kebab-core/src/lib.rs index 629d739..3abedb2 100644 --- a/crates/kebab-core/src/lib.rs +++ b/crates/kebab-core/src/lib.rs @@ -4,7 +4,7 @@ //! `kb-*` crate, so every other crate in the workspace can depend on it //! freely. //! -//! See `docs/superpowers/specs/2026-04-27-kb-final-form-design.md` for +//! See `docs/superpowers/specs/2026-04-27-kebab-final-form-design.md` for //! the canonical type bodies — this crate is the byte-for-byte mirror. pub mod ids; diff --git a/crates/kebab-embed-local/src/lib.rs b/crates/kebab-embed-local/src/lib.rs index 7267fe3..ca442fd 100644 --- a/crates/kebab-embed-local/src/lib.rs +++ b/crates/kebab-embed-local/src/lib.rs @@ -19,7 +19,7 @@ //! rules `kb-store-sqlite` applies to `data_dir` (`${XDG_DATA_HOME:-…}`, //! leading `~`, `{data_dir}` substitution). //! -//! See `docs/superpowers/specs/2026-04-27-kb-final-form-design.md` +//! See `docs/superpowers/specs/2026-04-27-kebab-final-form-design.md` //! §7.2 (Embedder), §6.4 ([models.embedding]), §9 (versioning). use std::sync::Mutex; @@ -82,7 +82,7 @@ impl FastembedEmbedder { check_dim(model_info.dim, config.models.embedding.dimensions)?; tracing::info!( - target: "kb-embed-local", + target: "kebab-embed-local", cache_dir = %cache_dir.display(), model = %config.models.embedding.model, dims = model_info.dim, @@ -97,7 +97,7 @@ impl FastembedEmbedder { .with_cache_dir(cache_dir.clone()) .with_show_download_progress(false); tracing::info!( - target: "kb-embed-local", + target: "kebab-embed-local", model = %config.models.embedding.model, cache_dir = %cache_dir.display(), "loading embedding model (first run will download ~470MB)" @@ -106,7 +106,7 @@ impl FastembedEmbedder { .context("fastembed: TextEmbedding::try_new")?; let dimensions = model_info.dim; tracing::info!( - target: "kb-embed-local", + target: "kebab-embed-local", model = %config.models.embedding.model, dimensions, "embedding model loaded" diff --git a/crates/kebab-embed-local/tests/embed_model.rs b/crates/kebab-embed-local/tests/embed_model.rs index df545ff..fee9c5f 100644 --- a/crates/kebab-embed-local/tests/embed_model.rs +++ b/crates/kebab-embed-local/tests/embed_model.rs @@ -26,7 +26,7 @@ use kebab_embed::{Embedder, EmbeddingInput, EmbeddingKind}; use kebab_embed_local::FastembedEmbedder; /// Build a `Config` whose `data_dir` lives in a per-process temp dir so -/// the test never writes into the developer's real `~/.local/share/kb`. +/// the test never writes into the developer's real `~/.local/share/kebab`. /// Returns the `Config` and the `TempDir` guard (caller keeps the guard /// alive for the test duration). fn test_config() -> (kebab_config::Config, tempfile::TempDir) { diff --git a/crates/kebab-embed/src/lib.rs b/crates/kebab-embed/src/lib.rs index e1b5c0c..6eaea68 100644 --- a/crates/kebab-embed/src/lib.rs +++ b/crates/kebab-embed/src/lib.rs @@ -11,7 +11,7 @@ //! deterministic test double. Real adapters (fastembed, candle, ollama-embed) //! live in p3-2 and MUST NOT be implemented here. //! -//! See `docs/superpowers/specs/2026-04-27-kb-final-form-design.md` §7.1, §7.2, +//! See `docs/superpowers/specs/2026-04-27-kebab-final-form-design.md` §7.1, §7.2, //! §11 for the contract. // ── Trait re-exports ────────────────────────────────────────────────────── diff --git a/crates/kebab-eval/src/metrics.rs b/crates/kebab-eval/src/metrics.rs index 6f25784..4f3eee8 100644 --- a/crates/kebab-eval/src/metrics.rs +++ b/crates/kebab-eval/src/metrics.rs @@ -40,10 +40,10 @@ const STORAGE_DECIMALS: u32 = 4; /// (P5-1) used — otherwise `expected_*` / `must_contain` won't line up /// with the stored `query_id`s. `pub(crate)` so the runner shares the /// exact same name + default rather than duplicating constants. -pub(crate) const KB_EVAL_GOLDEN: &str = "KB_EVAL_GOLDEN"; +pub(crate) const KEBAB_EVAL_GOLDEN: &str = "KEBAB_EVAL_GOLDEN"; /// Default golden YAML path (relative to CWD when set). Same -/// rationale as [`KB_EVAL_GOLDEN`] — single source of truth. +/// rationale as [`KEBAB_EVAL_GOLDEN`] — single source of truth. pub(crate) const DEFAULT_GOLDEN_PATH: &str = "fixtures/golden_queries.yaml"; /// Aggregate metrics for one stored eval run. @@ -151,7 +151,7 @@ pub fn store_aggregate_with_config( /// the runner uses, same default path. Pulled into its own helper so /// `compare_runs` can share it. pub(crate) fn resolve_golden_path() -> PathBuf { - match std::env::var(KB_EVAL_GOLDEN) { + match std::env::var(KEBAB_EVAL_GOLDEN) { Ok(s) if !s.is_empty() => PathBuf::from(s), _ => PathBuf::from(DEFAULT_GOLDEN_PATH), } @@ -161,7 +161,7 @@ fn load_golden_for_metrics() -> Result> { let path = resolve_golden_path(); load_golden_set(&path).with_context(|| { format!( - "load golden set from {} (override via KB_EVAL_GOLDEN)", + "load golden set from {} (override via KEBAB_EVAL_GOLDEN)", path.display() ) }) diff --git a/crates/kebab-eval/src/runner.rs b/crates/kebab-eval/src/runner.rs index b76d72e..c70ded0 100644 --- a/crates/kebab-eval/src/runner.rs +++ b/crates/kebab-eval/src/runner.rs @@ -13,7 +13,7 @@ use kebab_store_sqlite::{EvalRunRow, SqliteStore}; use time::OffsetDateTime; use crate::loader::{load_golden_set, validate_against_db}; -use crate::metrics::{DEFAULT_GOLDEN_PATH, KB_EVAL_GOLDEN}; +use crate::metrics::{DEFAULT_GOLDEN_PATH, KEBAB_EVAL_GOLDEN}; use crate::types::{EvalRun, EvalRunOpts, GoldenQuery, QueryResult}; /// Convert a wall-clock duration since `start` into milliseconds clamped @@ -46,7 +46,7 @@ pub fn run_eval_with_config(cfg: &kebab_config::Config, opts: &EvalRunOpts) -> R let golden_path = resolve_golden_path(); let queries = load_golden_set(&golden_path).with_context(|| { format!( - "load golden set from {} (override via KB_EVAL_GOLDEN)", + "load golden set from {} (override via KEBAB_EVAL_GOLDEN)", golden_path.display() ) })?; @@ -55,7 +55,7 @@ pub fn run_eval_with_config(cfg: &kebab_config::Config, opts: &EvalRunOpts) -> R // ── 2. Mint identifiers + open store ────────────────────────────────── let run_id = mint_run_id(); let created_at = OffsetDateTime::now_utc(); - let commit_hash = std::env::var("KB_COMMIT_HASH") + let commit_hash = std::env::var("KEBAB_COMMIT_HASH") .ok() .filter(|s| !s.is_empty()); @@ -110,7 +110,7 @@ pub fn run_eval_with_config(cfg: &kebab_config::Config, opts: &EvalRunOpts) -> R let duration_ms = elapsed_ms_u32(started); tracing::info!( - target: "kb-eval", + target: "kebab-eval", run_id = %run_id, suite = %opts.suite, queries = per_query.len(), @@ -136,11 +136,11 @@ fn mint_run_id() -> String { format!("run_{id}") } -/// Resolve the golden YAML path. Honors the `KB_EVAL_GOLDEN` env +/// Resolve the golden YAML path. Honors the `KEBAB_EVAL_GOLDEN` env /// override; otherwise relative to CWD. The path is NOT expanded for /// `~` / `${...}` placeholders — direct file paths only. fn resolve_golden_path() -> PathBuf { - match std::env::var(KB_EVAL_GOLDEN) { + match std::env::var(KEBAB_EVAL_GOLDEN) { Ok(s) if !s.is_empty() => PathBuf::from(s), _ => PathBuf::from(DEFAULT_GOLDEN_PATH), } diff --git a/crates/kebab-eval/tests/metrics_and_compare.rs b/crates/kebab-eval/tests/metrics_and_compare.rs index be9746f..3cf992b 100644 --- a/crates/kebab-eval/tests/metrics_and_compare.rs +++ b/crates/kebab-eval/tests/metrics_and_compare.rs @@ -34,7 +34,7 @@ fn cfg_with_data_dir(tmp: &TempDir, golden_yaml: &str) -> Config { // SAFELY scoped — `set_var` is process-global so callers serialise // tests via the `serial_test`-style guard below. unsafe { - std::env::set_var("KB_EVAL_GOLDEN", &golden_path); + std::env::set_var("KEBAB_EVAL_GOLDEN", &golden_path); } cfg } @@ -127,9 +127,9 @@ fn write_run( store.record_eval_run_with_results(&row, &results).unwrap(); } -/// Each test mutates a process-global env var (`KB_EVAL_GOLDEN`) and +/// Each test mutates a process-global env var (`KEBAB_EVAL_GOLDEN`) and /// expects to see its own write. Take this mutex around the body of -/// every test that touches `KB_EVAL_GOLDEN` so two concurrent test +/// every test that touches `KEBAB_EVAL_GOLDEN` so two concurrent test /// threads don't trip over each other's golden YAML. fn env_guard() -> std::sync::MutexGuard<'static, ()> { use std::sync::{Mutex, OnceLock}; diff --git a/crates/kebab-eval/tests/runner.rs b/crates/kebab-eval/tests/runner.rs index e13e0f9..6a34c39 100644 --- a/crates/kebab-eval/tests/runner.rs +++ b/crates/kebab-eval/tests/runner.rs @@ -7,7 +7,7 @@ //! workspace's source-of-truth, //! - lexical-only retrieval (`SearchMode::Lexical`) so no embedder is //! required (`models.embedding.provider = "none"`), -//! - golden YAML pointed at via `KB_EVAL_GOLDEN`. +//! - golden YAML pointed at via `KEBAB_EVAL_GOLDEN`. //! //! Determinism: lexical-only with a fixed seed corpus produces //! byte-identical `per_query.jsonl` content (modulo `run_id` / @@ -24,7 +24,7 @@ use kebab_store_sqlite::SqliteStore; use rusqlite::params; use tempfile::TempDir; -/// `KB_EVAL_GOLDEN` is process-global state. Tests touching it must +/// `KEBAB_EVAL_GOLDEN` is process-global state. Tests touching it must /// serialize so they don't trample each other when `cargo test` /// runs them in parallel. static GOLDEN_ENV_LOCK: Mutex<()> = Mutex::new(()); @@ -143,19 +143,19 @@ fn lexical_opts() -> EvalRunOpts { } } -/// Run the eval after pointing `KB_EVAL_GOLDEN` at `yaml`. The env +/// Run the eval after pointing `KEBAB_EVAL_GOLDEN` at `yaml`. The env /// guard must outlive the call so concurrent tests don't reset the /// var mid-run. fn run_with_golden R, R>(yaml: &Path, f: F) -> R { let _g = GOLDEN_ENV_LOCK.lock().unwrap_or_else(|p| p.into_inner()); - // SAFETY: `KB_EVAL_GOLDEN` is a benign env var; the GOLDEN_ENV_LOCK + // SAFETY: `KEBAB_EVAL_GOLDEN` is a benign env var; the GOLDEN_ENV_LOCK // serializes mutations so concurrent tests don't race. unsafe { - std::env::set_var("KB_EVAL_GOLDEN", yaml); + std::env::set_var("KEBAB_EVAL_GOLDEN", yaml); } let out = f(); unsafe { - std::env::remove_var("KB_EVAL_GOLDEN"); + std::env::remove_var("KEBAB_EVAL_GOLDEN"); } out } diff --git a/crates/kebab-llm-local/src/lib.rs b/crates/kebab-llm-local/src/lib.rs index 3e0bbfd..d97e521 100644 --- a/crates/kebab-llm-local/src/lib.rs +++ b/crates/kebab-llm-local/src/lib.rs @@ -29,7 +29,7 @@ //! - **Lazy connect.** [`OllamaLanguageModel::new`] does not hit the network; //! the first error surfaces on [`LanguageModel::generate_stream`]. //! -//! See `docs/superpowers/specs/2026-04-27-kb-final-form-design.md` §7.2, +//! See `docs/superpowers/specs/2026-04-27-kebab-final-form-design.md` §7.2, //! §6.4 (`[models.llm]`), §0 Q5 (streaming), §10 (errors), and report §11.2 //! (Ollama protocol notes). diff --git a/crates/kebab-llm-local/tests/integration.rs b/crates/kebab-llm-local/tests/integration.rs index 16a237c..a0836e2 100644 --- a/crates/kebab-llm-local/tests/integration.rs +++ b/crates/kebab-llm-local/tests/integration.rs @@ -19,8 +19,8 @@ use kebab_llm_local::{LanguageModel, OllamaLanguageModel}; #[ignore = "requires a local Ollama daemon + pulled model"] fn real_ollama_streams_non_empty_response() { // Use whatever model the workspace defaults select. Override via the - // KB_MODELS_LLM_MODEL env var if you want a different one for this run - // (e.g. `KB_MODELS_LLM_MODEL=qwen2.5:7b-instruct cargo test ... -- --ignored`). + // KEBAB_MODELS_LLM_MODEL env var if you want a different one for this run + // (e.g. `KEBAB_MODELS_LLM_MODEL=qwen2.5:7b-instruct cargo test ... -- --ignored`). let cfg = Config::load(None).expect("config should load"); let llm = OllamaLanguageModel::new(&cfg).unwrap(); diff --git a/crates/kebab-llm/src/lib.rs b/crates/kebab-llm/src/lib.rs index 5001c52..c536ae8 100644 --- a/crates/kebab-llm/src/lib.rs +++ b/crates/kebab-llm/src/lib.rs @@ -12,7 +12,7 @@ //! from `generate_stream` itself (e.g., connection refused) before any chunk //! is yielded; the mock never does. //! -//! See `docs/superpowers/specs/2026-04-27-kb-final-form-design.md` §7.1, §7.2, +//! See `docs/superpowers/specs/2026-04-27-kebab-final-form-design.md` §7.1, §7.2, //! §0 Q5 (streaming), §3.8 (`ModelRef`) for the contract. // ── Trait re-exports ────────────────────────────────────────────────────── diff --git a/crates/kebab-normalize/src/lib.rs b/crates/kebab-normalize/src/lib.rs index 2723d6f..b65cb18 100644 --- a/crates/kebab-normalize/src/lib.rs +++ b/crates/kebab-normalize/src/lib.rs @@ -96,7 +96,7 @@ pub fn build_canonical_document( .collect(); tracing::debug!( - target: "kb-normalize", + target: "kebab-normalize", "built canonical document doc_id={} blocks={}", doc_id.0, lifted_blocks.len() diff --git a/crates/kebab-rag/src/pipeline.rs b/crates/kebab-rag/src/pipeline.rs index fe9fbe3..3271f63 100644 --- a/crates/kebab-rag/src/pipeline.rs +++ b/crates/kebab-rag/src/pipeline.rs @@ -135,7 +135,7 @@ impl RagPipeline { let top_score = hits.first().map(|h| h.retrieval.fusion_score).unwrap_or(0.0); tracing::debug!( - target: "kb-rag", + target: "kebab-rag", chunks_returned, top_score, mode = ?opts.mode, @@ -161,7 +161,7 @@ impl RagPipeline { // collapse to the more accurate `NoChunks` refusal here. if packed_entries.is_empty() { tracing::warn!( - target: "kb-rag", + target: "kebab-rag", chunks_returned = hits.len(), "kb-rag: all retrieved chunks were unfetchable from the store; \ falling back to NoChunks refusal" @@ -324,7 +324,7 @@ impl RagPipeline { // Drop the moved `finish_reason` early into a tracing breadcrumb; the // wire schema does not surface it (per design §3.8). tracing::debug!( - target: "kb-rag", + target: "kebab-rag", grounded = answer.grounded, refusal = ?answer.refusal_reason, refusal_phrase_detected = matched_refusal_phrase, @@ -354,7 +354,7 @@ impl RagPipeline { self.docs.put_answer(&answer, query, packed_chunks_json.as_deref()) { tracing::warn!( - target: "kb-rag", + target: "kebab-rag", error = %e, "kb-rag: put_answer failed; in-memory Answer still returned" ); @@ -386,7 +386,7 @@ impl RagPipeline { Some(c) => c.text, None => { tracing::warn!( - target: "kb-rag", + target: "kebab-rag", chunk_id = %hit.chunk_id.0, "kb-rag: chunk not found in store; skipping" ); @@ -454,7 +454,7 @@ impl RagPipeline { created_at: OffsetDateTime::now_utc(), }; if let Err(e) = self.docs.put_answer(&answer, query, None) { - tracing::warn!(target: "kb-rag", error = %e, "kb-rag: put_answer (NoChunks) failed"); + tracing::warn!(target: "kebab-rag", error = %e, "kb-rag: put_answer (NoChunks) failed"); } Ok(answer) } @@ -529,7 +529,7 @@ impl RagPipeline { created_at: OffsetDateTime::now_utc(), }; if let Err(e) = self.docs.put_answer(&answer, query, None) { - tracing::warn!(target: "kb-rag", error = %e, "kb-rag: put_answer (ScoreGate) failed"); + tracing::warn!(target: "kebab-rag", error = %e, "kb-rag: put_answer (ScoreGate) failed"); } Ok(answer) } diff --git a/crates/kebab-search/src/hybrid.rs b/crates/kebab-search/src/hybrid.rs index e1c036b..9ebd7de 100644 --- a/crates/kebab-search/src/hybrid.rs +++ b/crates/kebab-search/src/hybrid.rs @@ -93,7 +93,7 @@ impl HybridRetriever { let vec_iv = vector.index_version(); if lex_iv.0 != vec_iv.0 { tracing::warn!( - target: "kb-search", + target: "kebab-search", lexical_index = %lex_iv.0, vector_index = %vec_iv.0, "kb-search hybrid: lexical and vector index_version differ; consider re-indexing" @@ -323,7 +323,7 @@ fn parse_fusion(name: &str, k_rrf: u32) -> FusionPolicy { "rrf" => FusionPolicy::Rrf { k_rrf: k }, other => { tracing::warn!( - target: "kb-search", + target: "kebab-search", policy = other, "kb-search hybrid: unknown fusion policy; falling back to RRF" ); diff --git a/crates/kebab-search/tests/hybrid.rs b/crates/kebab-search/tests/hybrid.rs index af62bfc..fcda0c5 100644 --- a/crates/kebab-search/tests/hybrid.rs +++ b/crates/kebab-search/tests/hybrid.rs @@ -157,17 +157,17 @@ fn hybrid_snapshot_run_1() { .join("hybrid") .join("run-1.json"); - if std::env::var_os("KB_UPDATE_SNAPSHOTS").is_some() { + if std::env::var_os("KEBAB_UPDATE_SNAPSHOTS").is_some() { std::fs::create_dir_all(fixture.parent().unwrap()).unwrap(); std::fs::write(&fixture, serde_json::to_string_pretty(&actual).unwrap()).unwrap(); eprintln!("[snapshot] regenerated {}", fixture.display()); - // Fail loudly so that accidentally setting KB_UPDATE_SNAPSHOTS + // Fail loudly so that accidentally setting KEBAB_UPDATE_SNAPSHOTS // in CI surfaces as a test failure rather than a silent // overwrite + green run. Same fail-loud-instead-of-silent-pass // philosophy as P3-2's `SNAPSHOT_HASH_BASELINE = 0` and P3-3's // placeholder fixture guards. panic!( - "[snapshot] regenerated {}, re-run without KB_UPDATE_SNAPSHOTS to verify pin", + "[snapshot] regenerated {}, re-run without KEBAB_UPDATE_SNAPSHOTS to verify pin", fixture.display() ); } @@ -176,7 +176,7 @@ fn hybrid_snapshot_run_1() { serde_json::from_str(&std::fs::read_to_string(&fixture).unwrap_or_else(|_| { panic!( "missing snapshot fixture at {}; run with \ - KB_UPDATE_SNAPSHOTS=1 to create", + KEBAB_UPDATE_SNAPSHOTS=1 to create", fixture.display() ) })) @@ -189,14 +189,14 @@ fn hybrid_snapshot_run_1() { panic!( "snapshot fixture is a placeholder — regenerate on AVX hardware then commit. \ Path: {}. To regenerate: \ - `KB_UPDATE_SNAPSHOTS=1 cargo test -p kb-search -- --ignored hybrid_snapshot`.", + `KEBAB_UPDATE_SNAPSHOTS=1 cargo test -p kb-search -- --ignored hybrid_snapshot`.", fixture.display() ); } assert_eq!( actual, expected, - "hybrid snapshot drift; rerun with KB_UPDATE_SNAPSHOTS=1 to regenerate" + "hybrid snapshot drift; rerun with KEBAB_UPDATE_SNAPSHOTS=1 to regenerate" ); // Independent guard: fusion scores must be non-increasing across diff --git a/crates/kebab-search/tests/lexical.rs b/crates/kebab-search/tests/lexical.rs index 4cb98be..2939aef 100644 --- a/crates/kebab-search/tests/lexical.rs +++ b/crates/kebab-search/tests/lexical.rs @@ -29,7 +29,7 @@ impl Env { config.storage.data_dir = temp.path().to_string_lossy().into_owned(); let store = SqliteStore::open(&config).expect("open store"); store.run_migrations().expect("run migrations"); - let db_path = temp.path().join("kb.sqlite"); + let db_path = temp.path().join("kebab.sqlite"); Self { _temp: temp, store: Arc::new(store), @@ -618,7 +618,7 @@ fn lexical_snapshot_run_1() { // `Vec` for a fixed query is checked verbatim against // `tests/fixtures/search/lexical/run-1.json`. Update both sides in // the same commit when intentional changes ship. - // Stable because rusqlite ships bundled SQLite — a tokenizer/bm25 algorithm change in a future SQLite bump will require regenerating run-1.json via `KB_UPDATE_SNAPSHOTS=1`. + // Stable because rusqlite ships bundled SQLite — a tokenizer/bm25 algorithm change in a future SQLite bump will require regenerating run-1.json via `KEBAB_UPDATE_SNAPSHOTS=1`. let env = Env::new(); let conn = env.raw_conn(); insert_document(&conn, &id32("d"), "notes/snap.md", "Snap", "en", "primary", &[]); @@ -656,11 +656,11 @@ fn lexical_snapshot_run_1() { let baseline_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/search/lexical/run-1.json"); - if std::env::var_os("KB_UPDATE_SNAPSHOTS").is_some() { + if std::env::var_os("KEBAB_UPDATE_SNAPSHOTS").is_some() { std::fs::write(&baseline_path, serde_json::to_string_pretty(&actual).unwrap()).unwrap(); } let baseline_text = std::fs::read_to_string(&baseline_path) - .expect("baseline snapshot must exist; run with KB_UPDATE_SNAPSHOTS=1 to seed"); + .expect("baseline snapshot must exist; run with KEBAB_UPDATE_SNAPSHOTS=1 to seed"); let expected: serde_json::Value = serde_json::from_str(&baseline_text).unwrap(); assert_eq!(actual, expected, "lexical run-1 snapshot drift"); } diff --git a/crates/kebab-source-fs/src/connector.rs b/crates/kebab-source-fs/src/connector.rs index f904395..0cf5e3e 100644 --- a/crates/kebab-source-fs/src/connector.rs +++ b/crates/kebab-source-fs/src/connector.rs @@ -74,13 +74,13 @@ impl SourceConnector for FsSourceConnector { scope.root.clone() }; - // Union: config.workspace.exclude ∪ scope.exclude ∪ .kbignore. - // Per §6.2 the union of `.kbignore` and `config.workspace.exclude` + // Union: config.workspace.exclude ∪ scope.exclude ∪ .kebabignore. + // Per §6.2 the union of `.kebabignore` and `config.workspace.exclude` // is the filter set. `scope.exclude` is added on top so a caller // can layer a per-call narrowing. let mut excludes = self.default_exclude.clone(); excludes.extend(scope.exclude.iter().cloned()); - // .kbignore is re-read on every scan() so users can edit it without + // .kebabignore is re-read on every scan() so users can edit it without // restarting any long-running process. let kbignore = read_kbignore(&root)?; @@ -243,7 +243,7 @@ mod tests { fn scan_filters_by_kbignore() { let dir = tempfile::tempdir().unwrap(); let root = dir.path(); - std::fs::write(root.join(".kbignore"), "*.tmp\n").unwrap(); + std::fs::write(root.join(".kebabignore"), "*.tmp\n").unwrap(); std::fs::write(root.join("a.md"), b"x").unwrap(); std::fs::write(root.join("b.tmp"), b"x").unwrap(); @@ -252,14 +252,14 @@ mod tests { .unwrap(); let v = conn.scan(&SourceScope::default()).unwrap(); let names: Vec<_> = v.iter().map(|a| a.workspace_path.0.clone()).collect(); - // Decision: `.kbignore` itself IS emitted as a RawAsset (MediaType::Other("")). + // Decision: `.kebabignore` itself IS emitted as a RawAsset (MediaType::Other("")). // Rationale: a config file that affects ingest is itself part of the // workspace contents; the markdown extractor (P1-2) will reject Other("") - // on its own. If we ever decide to omit `.kbignore` from the asset list, + // on its own. If we ever decide to omit `.kebabignore` from the asset list, // this test will catch it. assert!( - names.contains(&".kbignore".to_string()), - ".kbignore must be emitted as an asset; got: {names:?}" + names.contains(&".kebabignore".to_string()), + ".kebabignore must be emitted as an asset; got: {names:?}" ); assert!(names.contains(&"a.md".to_string())); assert!(!names.contains(&"b.tmp".to_string())); @@ -285,7 +285,7 @@ mod tests { fn scan_unions_config_exclude_and_kbignore() { let dir = tempfile::tempdir().unwrap(); let root = dir.path(); - std::fs::write(root.join(".kbignore"), "*.tmp\n").unwrap(); + std::fs::write(root.join(".kebabignore"), "*.tmp\n").unwrap(); std::fs::write(root.join("a.md"), b"x").unwrap(); std::fs::write(root.join("b.tmp"), b"x").unwrap(); std::fs::write(root.join("c.log"), b"x").unwrap(); diff --git a/crates/kebab-source-fs/src/lib.rs b/crates/kebab-source-fs/src/lib.rs index ae6b128..ea7f294 100644 --- a/crates/kebab-source-fs/src/lib.rs +++ b/crates/kebab-source-fs/src/lib.rs @@ -1,10 +1,10 @@ //! `kb-source-fs` — local filesystem `SourceConnector`. //! //! Walks `config.workspace.root`, applies gitignore-style filters from -//! `config.workspace.exclude` ∪ `.kbignore`, computes BLAKE3 of every file, +//! `config.workspace.exclude` ∪ `.kebabignore`, computes BLAKE3 of every file, //! and emits `Vec` sorted by `workspace_path` for determinism. //! -//! Per design §3.3 (RawAsset), §6.2 (workspace + .kbignore), §6.6 (POSIX +//! Per design §3.3 (RawAsset), §6.2 (workspace + .kebabignore), §6.6 (POSIX //! normalization), §7.1 (SourceScope), §7.2 (SourceConnector), §8 (module //! boundaries). diff --git a/crates/kebab-source-fs/src/walker.rs b/crates/kebab-source-fs/src/walker.rs index 6f399cc..1ba14fa 100644 --- a/crates/kebab-source-fs/src/walker.rs +++ b/crates/kebab-source-fs/src/walker.rs @@ -3,7 +3,7 @@ //! //! Filter set (per task spec, design §6.2): //! - `config.workspace.exclude` (passed in by `FsSourceConnector`) -//! - `/.kbignore` (optional file at workspace root) +//! - `/.kebabignore` (optional file at workspace root) //! - default-excludes for `.DS_Store` and macOS resource forks (`._*`) //! //! All three are merged via `ignore::overrides::OverrideBuilder`, which @@ -36,7 +36,7 @@ use walkdir::{DirEntry, WalkDir}; /// Default-excludes baked into the connector. These are NOT configurable; /// they cover noise that is never useful to ingest and would otherwise need -/// to appear in every user's `.kbignore`. +/// to appear in every user's `.kebabignore`. const DEFAULT_EXCLUDES: &[&str] = &[ // Finder metadata ".DS_Store", @@ -46,7 +46,7 @@ const DEFAULT_EXCLUDES: &[&str] = &[ "**/._*", ]; -/// Build the merged `Override` from `config.workspace.exclude` ∪ `.kbignore` +/// Build the merged `Override` from `config.workspace.exclude` ∪ `.kebabignore` /// ∪ baked-in default excludes. /// /// Each input pattern is registered as an *exclude* (gitignore-style: a @@ -73,16 +73,16 @@ pub(crate) fn build_overrides( for pat in kbignore_patterns { builder .add(&format!("!{pat}")) - .with_context(|| format!("invalid .kbignore pattern: {pat}"))?; + .with_context(|| format!("invalid .kebabignore pattern: {pat}"))?; } builder.build().context("failed to compile override set") } -/// Read `/.kbignore` if it exists. Each non-blank, non-comment line is +/// Read `/.kebabignore` if it exists. Each non-blank, non-comment line is /// a gitignore pattern. Missing file → empty Vec (not an error). pub(crate) fn read_kbignore(root: &Path) -> Result> { - let path = root.join(".kbignore"); + let path = root.join(".kebabignore"); if !path.exists() { return Ok(Vec::new()); } @@ -250,7 +250,7 @@ mod tests { fn read_kbignore_strips_blanks_and_comments() { let dir = tempfile::tempdir().unwrap(); std::fs::write( - dir.path().join(".kbignore"), + dir.path().join(".kebabignore"), "# comment\n*.tmp\n\nignored/**\n", ) .unwrap(); diff --git a/crates/kebab-source-fs/tests/snapshot_tree1.rs b/crates/kebab-source-fs/tests/snapshot_tree1.rs index 0431faf..3578710 100644 --- a/crates/kebab-source-fs/tests/snapshot_tree1.rs +++ b/crates/kebab-source-fs/tests/snapshot_tree1.rs @@ -9,8 +9,8 @@ //! │ ├── alpha.md //! │ └── beta.md //! ├── ignored/ -//! │ └── skip.tmp # excluded by .kbignore -//! ├── .kbignore # contains: *.tmp +//! │ └── skip.tmp # excluded by .kebabignore +//! ├── .kebabignore # contains: *.tmp //! └── .DS_Store # implicitly excluded //! ``` //! @@ -52,7 +52,7 @@ fn cfg_for_fixture(root: &str) -> Config { let mut c = Config::defaults(); c.workspace.root = root.to_string(); // Clear default excludes (`.git/**`, `node_modules/**`, `.obsidian/**`) - // so the snapshot is purely a function of the fixture + .kbignore + + // so the snapshot is purely a function of the fixture + .kebabignore + // baked-in default-excludes. c.workspace.exclude.clear(); c @@ -101,18 +101,18 @@ fn scan_and_strip() -> Value { fn tree_1_snapshot_matches_baseline() { let actual = scan_and_strip(); - // If KB_REGEN_SNAPSHOT is set, (re)write the baseline and exit + // If KEBAB_REGEN_SNAPSHOT is set, (re)write the baseline and exit // *before* attempting to read it. This is the only path that may // create the file from scratch. - if std::env::var_os("KB_REGEN_SNAPSHOT").is_some() { + if std::env::var_os("KEBAB_REGEN_SNAPSHOT").is_some() { let pretty = serde_json::to_string_pretty(&actual).unwrap() + "\n"; std::fs::write(baseline_path(), pretty).expect("write baseline"); - panic!("regenerated baseline; rerun without KB_REGEN_SNAPSHOT to verify"); + panic!("regenerated baseline; rerun without KEBAB_REGEN_SNAPSHOT to verify"); } let baseline_text = std::fs::read_to_string(baseline_path()).unwrap_or_else(|_| { panic!( - "missing baseline at {} — regenerate via `KB_REGEN_SNAPSHOT=1 cargo test \ + "missing baseline at {} — regenerate via `KEBAB_REGEN_SNAPSHOT=1 cargo test \ -p kb-source-fs --test snapshot_tree1 -- tree_1_snapshot_matches_baseline`", baseline_path().display() ) diff --git a/crates/kebab-store-sqlite/src/store.rs b/crates/kebab-store-sqlite/src/store.rs index 2ee3636..0ac1408 100644 --- a/crates/kebab-store-sqlite/src/store.rs +++ b/crates/kebab-store-sqlite/src/store.rs @@ -28,7 +28,7 @@ const ASSET_ID_HEX_LEN: usize = 32; /// Default file name under `config.storage.data_dir`. Kept private — the /// path layout is a §6.3 design decision, not part of the store's public /// surface. -const SQLITE_FILE: &str = "kb.sqlite"; +const SQLITE_FILE: &str = "kebab.sqlite"; /// Subdirectory under `data_dir` holding shard-prefixed asset bytes /// (`/`). Mirrors design §6.3. @@ -74,7 +74,7 @@ impl SqliteStore { apply_pragmas(&conn)?; tracing::debug!( - target: "kb-store-sqlite", + target: "kebab-store-sqlite", data_dir = %data_dir.display(), db = %db_path.display(), "opened sqlite store" @@ -94,7 +94,7 @@ impl SqliteStore { schema::runner() .run(&mut *conn) .map_err(|e| StoreError::Migration(e.to_string()))?; - tracing::debug!(target: "kb-store-sqlite", "migrations applied"); + tracing::debug!(target: "kebab-store-sqlite", "migrations applied"); Ok(()) } diff --git a/crates/kebab-store-sqlite/tests/common/mod.rs b/crates/kebab-store-sqlite/tests/common/mod.rs index e65c18f..b9f8b25 100644 --- a/crates/kebab-store-sqlite/tests/common/mod.rs +++ b/crates/kebab-store-sqlite/tests/common/mod.rs @@ -37,7 +37,7 @@ impl TestEnv { } pub fn db_path(&self) -> PathBuf { - self.temp.path().join("kb.sqlite") + self.temp.path().join("kebab.sqlite") } /// Open a side-channel rusqlite connection for direct SQL inspection. diff --git a/crates/kebab-store-sqlite/tests/fts.rs b/crates/kebab-store-sqlite/tests/fts.rs index 350c16d..2d66204 100644 --- a/crates/kebab-store-sqlite/tests/fts.rs +++ b/crates/kebab-store-sqlite/tests/fts.rs @@ -335,7 +335,7 @@ fn normalize_ws(s: &str) -> String { /// - no `END;` after the virtual-table line fn extract_design_5_5_fts_block() -> String { let doc = include_str!( - "../../../docs/superpowers/specs/2026-04-27-kb-final-form-design.md" + "../../../docs/superpowers/specs/2026-04-27-kebab-final-form-design.md" ); let heading_idx = doc .find("### 5.5 Chunks + FTS5") @@ -437,7 +437,7 @@ fn fts_v002_matches_design_section_5_5_verbatim() { // ── 6. WAL cleanup: drop store before tempdir reaps WAL/SHM ────────── /// Mirror the P1-6 pattern: opening + migrating + dropping the store -/// must not strand `kb.sqlite-wal`/`-shm` files such that the tempdir +/// must not strand `kebab.sqlite-wal`/`-shm` files such that the tempdir /// can't be cleaned up. After dropping the store + side-channel conn, /// the WAL/SHM siblings must either not exist or be removable — if a /// stray handle were holding them open, on Windows the remove would diff --git a/crates/kebab-store-vector/src/lib.rs b/crates/kebab-store-vector/src/lib.rs index 53b30a9..5543982 100644 --- a/crates/kebab-store-vector/src/lib.rs +++ b/crates/kebab-store-vector/src/lib.rs @@ -20,7 +20,7 @@ //! serializes vector ops, and current-thread saves the two worker //! threads a multi-thread runtime spawns by default. //! -//! See `docs/superpowers/specs/2026-04-27-kb-final-form-design.md` +//! See `docs/superpowers/specs/2026-04-27-kebab-final-form-design.md` //! §5.6 (embedding_records DDL), §6.3 (lancedb table naming), //! §7.2 (VectorStore), §9 (versioning). diff --git a/crates/kebab-store-vector/src/store.rs b/crates/kebab-store-vector/src/store.rs index dbb857c..2fbbc0e 100644 --- a/crates/kebab-store-vector/src/store.rs +++ b/crates/kebab-store-vector/src/store.rs @@ -111,7 +111,7 @@ impl LanceVectorStore { })?; tracing::debug!( - target: "kb-store-vector", + target: "kebab-store-vector", vector_dir = %vector_dir.display(), "opened LanceVectorStore" ); @@ -141,7 +141,7 @@ impl LanceVectorStore { .await .context("create_empty_table")?; tracing::info!( - target: "kb-store-vector", + target: "kebab-store-vector", table = table_name, dim, "created Lance table" @@ -275,7 +275,7 @@ impl VectorStore for LanceVectorStore { .context("phase 3: mark embedding_records committed")?; tracing::info!( - target: "kb-store-vector", + target: "kebab-store-vector", table = %table_name, rows = recs.len(), "upsert committed" @@ -306,7 +306,7 @@ impl VectorStore for LanceVectorStore { Some(name) => name, None => { tracing::debug!( - target: "kb-store-vector", + target: "kebab-store-vector", dim, "search: no Lance table matches query dim — returning empty" ); @@ -477,7 +477,7 @@ fn decode_lance_hits(batches: &[RecordBatch]) -> Result> { fn score_from_distance(distance: f32) -> f32 { if distance.is_nan() { tracing::warn!( - target: "kb-store-vector", + target: "kebab-store-vector", "NaN cosine distance from Lance — coercing to score 0" ); return 0.0; @@ -515,7 +515,7 @@ async fn find_matching_table( } Err(e) => { tracing::warn!( - target: "kb-store-vector", + target: "kebab-store-vector", table = %name, error = %e, "search: skipped unopenable table" diff --git a/crates/kebab-store-vector/tests/snapshot.rs b/crates/kebab-store-vector/tests/snapshot.rs index 1d3d9e1..8f33702 100644 --- a/crates/kebab-store-vector/tests/snapshot.rs +++ b/crates/kebab-store-vector/tests/snapshot.rs @@ -73,7 +73,7 @@ fn vector_hits_snapshot_run_1() { .join("vector") .join("run-1.json"); - if std::env::var_os("KB_UPDATE_SNAPSHOTS").is_some() { + if std::env::var_os("KEBAB_UPDATE_SNAPSHOTS").is_some() { std::fs::create_dir_all(fixture.parent().unwrap()).unwrap(); std::fs::write(&fixture, serde_json::to_string_pretty(&actual).unwrap()) .unwrap(); @@ -83,7 +83,7 @@ fn vector_hits_snapshot_run_1() { let expected: serde_json::Value = serde_json::from_str(&std::fs::read_to_string(&fixture).unwrap_or_else( |_| panic!( - "missing snapshot fixture at {}; run with KB_UPDATE_SNAPSHOTS=1 to create", + "missing snapshot fixture at {}; run with KEBAB_UPDATE_SNAPSHOTS=1 to create", fixture.display() ), )) @@ -97,14 +97,14 @@ fn vector_hits_snapshot_run_1() { panic!( "snapshot fixture is a placeholder — regenerate on AVX hardware then commit. \ Path: {}. To regenerate: \ - `KB_UPDATE_SNAPSHOTS=1 cargo test -p kb-store-vector -- --ignored snapshot`.", + `KEBAB_UPDATE_SNAPSHOTS=1 cargo test -p kb-store-vector -- --ignored snapshot`.", fixture.display() ); } assert_eq!( actual, expected, - "snapshot drift; rerun with KB_UPDATE_SNAPSHOTS=1 to regenerate" + "snapshot drift; rerun with KEBAB_UPDATE_SNAPSHOTS=1 to regenerate" ); // Independent guard: scores must be non-increasing. diff --git a/docs/superpowers/specs/2026-04-27-kb-final-form-design.md b/docs/superpowers/specs/2026-04-27-kebab-final-form-design.md similarity index 100% rename from docs/superpowers/specs/2026-04-27-kb-final-form-design.md rename to docs/superpowers/specs/2026-04-27-kebab-final-form-design.md diff --git a/fixtures/source-fs/tree-1.snapshot.json b/fixtures/source-fs/tree-1.snapshot.json index 350d53c..7059f6a 100644 --- a/fixtures/source-fs/tree-1.snapshot.json +++ b/fixtures/source-fs/tree-1.snapshot.json @@ -15,7 +15,7 @@ "kind": "copied", "path": "" }, - "workspace_path": ".kbignore" + "workspace_path": ".kebabignore" }, { "asset_id": "ba6cd31cab86eff7a86638ee76494bcf", diff --git a/fixtures/source-fs/tree-1/.kbignore b/fixtures/source-fs/tree-1/.kebabignore similarity index 100% rename from fixtures/source-fs/tree-1/.kbignore rename to fixtures/source-fs/tree-1/.kebabignore diff --git a/kb_local_rust_report.md b/kebab_local_rust_report.md similarity index 100% rename from kb_local_rust_report.md rename to kebab_local_rust_report.md