Commit Graph

424 Commits

Author SHA1 Message Date
th-kim0823
4b1b8a15bf feat(kebab-mcp): ask tool (fb-30)
Fourth (and final v1) tool — `ask` (input: query / optional session_id).
Multi-turn via optional session_id (kebab_app::ask_with_session_with_config),
single-shot via ask_with_config when None. Refusal (grounded:false) NOT
mapped to isError — agent branches on the wire payload's grounded flag.

AskOpts has no Default impl (must construct manually). Answer carries no
schema_version field (tagged inline via entry().or_insert_with, idempotent).
Mode defaulted to Lexical: reqwest::blocking::Client::build creates and
drops a tokio runtime, panicking inside async context — the empty-corpus
refusal test avoids this via spawn_blocking; the tool itself uses Lexical
as the default mode since MCP callers typically run without an embedding
provider configured.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 15:50:57 +09:00
th-kim0823
52782fdf72 feat(kebab-mcp): search tool (fb-30)
Third tool — `search` (input: query / mode / k). First tool with
non-empty input — establishes the pattern: SearchInput struct with
JsonSchema derive + Tool::new uses
rmcp::handler::server::common::schema_for_type::<SearchInput>() for
inputSchema + call_tool match arm parses request.arguments via
serde_json::from_value.

search_with_config takes owned Config, so state.config (Arc<Config>)
is cloned via (*state.config).clone(). Output: search_hit.v1 array —
SearchHit (kebab-core) does not carry schema_version field, so each
element is tagged inline before serialising.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 15:44:56 +09:00
th-kim0823
360fa53b02 feat(kebab-mcp): doctor tool (fb-30)
Second tool — `doctor` (no input args, returns doctor.v1 JSON via
kebab_app::doctor_with_config_path). Mirrors schema tool's manual-dispatch
pattern: Tool::new entry in list_tools, match arm in call_tool, per-tool
module in tools/doctor.rs.

doctor_with_config_path takes Option<&Path> (not &Config), so KebabAppState
is extended with config_path: Option<PathBuf>. All existing callers
(initialize.rs, tools_call_schema.rs, serve_stdio_async) pass None for now;
Plan Task 10 (Cmd::Mcp wiring) will thread the actual --config path through.
doctor_with_config falls back to XDG default when config_path is None —
same behavior as bare `kebab doctor`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 15:41:13 +09:00
th-kim0823
8ca8e18d12 feat(kebab-mcp): schema tool (fb-30)
First tool wired — `schema` (no input args, returns schema.v1 JSON
mirroring `kebab schema --json`). Establishes the per-tool module
pattern (crates/kebab-mcp/src/tools/<name>.rs) + error helper that maps
anyhow::Error to MCP CallToolResult.error with error.v1 content.

Dispatch pattern: manual dispatch — explicit `list_tools` + `call_tool`
overrides on `impl ServerHandler for KebabHandler` with a
`match request.name.as_ref()` arm per tool. No proc-macro magic.
Tasks 5-7 should add a new arm + new tools/<name>.rs following the same
pattern; also add a `Tool::new(...)` entry in `list_tools`.

API shapes confirmed from rmcp 1.6 source:
- Content = Annotated<RawContent>; text via `Content::text(s)`; pattern
  match via `&content.raw` → `RawContent::Text(t)` → `t.text`
- CallToolResult::success(Vec<Content>) / ::error(Vec<Content>)
- ListToolsResult::with_all_items(Vec<Tool>)
- schema_for_empty_input() from rmcp::handler::server::common

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 15:38:00 +09:00
th-kim0823
8f6e6bc01a feat(kebab-mcp): handler skeleton + initialize handshake (fb-30)
KebabHandler implements rmcp::ServerHandler::get_info — returns
serverInfo (name="kebab", version from CARGO_PKG_VERSION) and
capabilities.tools. KebabAppState wraps Config in Arc for cheap clone
into per-request task scope. serve_stdio entry builds a multi-thread
tokio runtime and runs the server until client closes the stream.

rmcp 1.6 API used:
- rmcp::ServerHandler trait (re-exported from handler::server)
- ServerInfo::new(caps).with_server_info(impl) builder (not struct-init:
  InitializeResult/Implementation are #[non_exhaustive])
- ServerCapabilities::builder().enable_tools().build() — builder macro
  generated, confirms the plan-literal pattern works
- Implementation::new(name, version) — non-exhaustive constructor
- rmcp::transport::stdio() returns (tokio::io::Stdin, tokio::io::Stdout)
  tuple; tuple impls IntoTransport via AsyncRead+AsyncWrite blanket
- handler.serve(transport).await → RunningService<RoleServer, H>
  (ServiceExt::serve, returns Result<_, ServerInitializeError>)
- service.waiting().await → Result<QuitReason, JoinError>
- serve_stdio is plain fn wrapping a manually-built tokio runtime
  (avoids nested-runtime hazard if kebab-cli ever gains its own rt)

Tools wire-up lands in subsequent tasks (one tool per task).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 15:31:13 +09:00
th-kim0823
2c09ed6af4 🏗️ chore(kebab-mcp): scaffold new crate (fb-30)
Empty lib + serve_stdio entry that bails until Task 3 wires rmcp. Adds
rmcp 1.6 to workspace dependencies (server + macros + transport-io +
schemars features) + tokio multi-thread/io-util/io-std local extensions.

schemars declared as "1" (resolved to 1.2.1) — matches rmcp 1.6's ^1.0
requirement (verified via crates.io /dependencies; plan literal was 0.9
which would conflict). Path-style refs for kebab-app / kebab-config /
kebab-core follow workspace convention.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 15:25:57 +09:00
th-kim0823
1f53930234 🏗️ refactor(kebab-app): promote error_classify → kebab-app::error_wire (fb-30 prep)
fb-30 의 새 crate `kebab-mcp` 가 동일 classify 모듈 사용 — UI crate 끼리
import 는 facade rule 위반이므로 kebab-app 으로 promotion. fb-27 commit
c91228e 의 코드 그대로 이전 (struct + classify + classify_llm + 7 unit
test). reqwest dev-dep 도 함께 이동.

kebab-cli 는 `kebab_app::ErrorV1` / `kebab_app::classify` 로 import 경로
1줄 변경 + wire.rs 의 `&crate::error_classify::ErrorV1` 1줄 교체. 동작
무영향.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 15:13:28 +09:00
65cfaf7c75 Merge pull request 'docs(fb-30): MCP server (stdio) spec + implementation plan' (#107) from spec/p9-fb-30-mcp-server into main
Reviewed-on: #107
2026-05-07 06:07:41 +00:00
th-kim0823
72855df98b 📝 docs(plan): p9-fb-30 MCP server implementation plan
14-task plan covering:
- error_classify → kebab-app::error_wire promotion (Task 1)
- new crate kebab-mcp scaffold (Task 2)
- KebabHandler skeleton + initialize (Task 3)
- 4 tool wire-up: schema / doctor / search / ask (Tasks 4-7)
- error mapping test (Task 8)
- tools/list integration (Task 9)
- kebab-cli Cmd::Mcp + spawn smoke (Task 10)
- capability flag flip (Task 11)
- doc sync (Task 12)
- HOTFIXES + status flip (Task 13)
- final workspace verification (Task 14)

rmcp 1.6 SDK 채택. plan 의 macro / extractor 시그니처는 best-effort —
실제 rmcp 1.6 API 와 다르면 Task 3 부터 hand-roll fallback 명시.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 14:54:43 +09:00
th-kim0823
58ec4578b9 📝 docs(spec): p9-fb-30 MCP server (stdio) 설계 문서
`kebab mcp` 신규 subcommand + new crate `kebab-mcp` 도입을 위한
brainstorm 산출물. agent integration "MVP" 완성 (Claude Code / Cursor /
OpenAI Agents 등 host-agnostic 사용 가능).

핵심 결정:
- `kebab mcp` subcommand (kebab-cli 내, 신규 binary 아님)
- 4 read-only tool (`search` / `ask` / `schema` / `doctor`) — ingest /
  fetch / list_docs / inspect_chunk 는 fb-31 / fb-35 / 후속에서 추가
- Resources / Prompts 모두 skip (tools only)
- Rust MCP SDK 사용 — rmcp 채택 우선, plan 단계 verify
- stdio 단일 transport — fb-29 deferral 따라 HTTP-SSE P+
- error mapping: tool dispatch 실패만 isError=true + error.v1 content,
  refusal / no-hit / unhealthy 는 정상 응답 (semantic flag 으로 분기)
- classify 모듈 이전: kebab-cli::error_classify → kebab-app::error_wire
  (kebab-cli + kebab-mcp 둘 다 동일 모듈 사용, facade 룰 준수)
- capability flag `mcp_server` false → true

릴리스: 0.3.0 → 0.4.0 minor — 신규 surface + new crate + 디자인 §10.1
변경 + capability flip 모두 trigger.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 14:44:09 +09:00
769139996b Merge pull request 'docs: defer fb-29 HTTP daemon + fb-30 stdio-only' (#106) from chore/fb-29-defer into main
Reviewed-on: #106
2026-05-07 04:59:36 +00:00
th-kim0823
2e8de14434 📝 docs: defer fb-29 HTTP daemon + fb-30 stdio-only
2026-05-07 brainstorm 결정 — fb-29 HTTP daemon defer.

근거:
- single-user local-first 환경에서 daemon 복잡도 (PID file / port lock /
  single-instance / lifecycle UX / loopback security) 가 비대.
- fb-30 stdio MCP 가 동일 사용자 가치 (agent integration + session 동안
  hot cache) 를 daemon 없이 제공 — agent host (Claude Code, Cursor 등)
  가 subprocess 띄우면 session 동안 process 유지 = 자연스러운 hot state.
- ask 의 dominant cost 는 Ollama LLM 추론 (수 초) 라 daemon 효과 제한적.
  search 만 의미 있는데 그 비용도 fastembed model load (~1s) 가 dominant —
  stdio MCP subprocess 가 동일하게 회피.

후속 fb-30 의 transport 는 stdio 단일. HTTP-SSE 옵션은 future task —
재개 trigger:
- browser agent / remote multi-host 시나리오 등장.
- TUI ↔ CLI 다중 인스턴스 state sharing 요구.
- fb-30 MCP HTTP-SSE 변형 도입 검토.

영향:
- fb-29 spec frontmatter `status: open` → `deferred`,
  `target_version: 0.3.0` → `P+`, `unblocks: [p9-fb-30]` → `[]`.
- fb-30 `depends_on: [p9-fb-27, p9-fb-29]` → `[p9-fb-27]`.
- INDEX.md 의 0.3.0 group + HANDOFF 의 release 그룹 표 갱신.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:58:02 +09:00
50ad7f1f28 Merge pull request 'chore: bump version 0.2.1 → 0.3.0 (fb-27 release)' (#105) from chore/bump-v0.3.0 into main
Reviewed-on: #105
v0.3.0
2026-05-07 04:48:53 +00:00
th-kim0823
73f5d73112 chore: bump workspace version 0.2.1 → 0.3.0 — fb-27 release
agent foundation MVP 첫 component (fb-27 introspection + structured
error wire) 머지 후 minor bump. trigger:

- frozen design contract 변경 (§10.1 capability matrix subsection 신설)
- wire schema additive (`schema.v1` + `error.v1` 신규)
- 신규 CLI surface (`kebab schema`)

이전 binary (v0.2.1) 는 새 wire schema / error wire 인지 못 함 — 0.3.0
부터 agent integration (fb-30 MCP, fb-29 daemon, fb-28 readonly/quiet 등)
의 capability discovery + structured error 활용 가능.

후속 fb-26 / 28 / 29 / 30 / 31 머지는 0.3.x patch / 0.4.0 누적 예정.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:38:05 +09:00
c732189eb3 Merge pull request 'feat(fb-27): introspection (kebab schema) + structured error wire' (#104) from feat/p9-fb-27-introspection-and-error-wire into main
Reviewed-on: #104
2026-05-07 04:19:40 +00:00
th-kim0823
1bcca7f9ca 🏗️ refactor(fb-27): apply round 1 review nits
- schema.rs: extract `SCHEMA_V1_ID` const + re-export via kebab-app::lib.rs.
  wire.rs::wire_schema 의 2 literal 도 import 해서 single source of truth.
- schema.rs::collect_models: parser_version 가 markdown 만 surface 함을
  주석으로 명시 (PDF/image extractor 의 자체 version 은 SchemaV1.models 가
  multi-medium map 으로 진화 시 surface).
- main.rs::print_schema_text: 헤더 줄 끝의 `\n` 제거 + `println!()` 추가 —
  다른 section 들과 패턴 일관.
- error_classify.rs::llm_unreachable_classifies: timeout 50ms → 500ms (10x
  headroom) + 접근 방식 + 한계 주석 추가.
- HOTFIXES: open_existing 의 RW flag + 주석-only enforcement 갭을
  Known-limitation 에 명시.

Round 1 review summary: #104 (comment)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:17:50 +09:00
th-kim0823
f7d6ea593e 📝 docs: fix capability flag names in skill + document not_indexed details deviation (fb-27)
- SKILL.md: `streaming_ingest` → `streaming_ask`, `multi_turn` → `rag_multi_turn` (capability name mismatch flagged in final review — agents following the example literal would read non-existent fields).
- HOTFIXES.md: add `not_indexed.details` to the interim wire shape deviations list — emit `{ expected, found }` only (spec literal `{ data_dir, expected, found }` not honored because NotIndexed signal carries one full path, not separate data_dir).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 12:58:17 +09:00
th-kim0823
cc0e4e6551 📝 docs(tasks): HOTFIXES entry + p9-fb-27 status → completed
HOTFIXES 항목이 fb-27 의 live binding 변경 + interim wire shape
deviation 의 source of truth (error.v1.details 가 신규 typed signal
도입 전까지 spec literal 과 일부 일탈).

spec 상단 banner 와 frontmatter status 가 frozen 상태 + post-merge
HOTFIXES cross-link 으로 갱신.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 12:40:38 +09:00
th-kim0823
bb7b1cec4b 📝 docs: sync README / HANDOFF / CLAUDE / skill / design for fb-27
- README 명령 표 에 `kebab schema` 추가
- HANDOFF post-도그푸딩 항목 한 줄
- CLAUDE.md wire schema 절 schema.v1 / error.v1 추가
- integrations skill — schema 활용 안내 (additive)
- design §10.1 capability matrix subsection 신설

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 12:37:40 +09:00
th-kim0823
c25f4f89e3 📝 docs(wire-schema): schema.v1 + error.v1 JSON Schema (fb-27)
schema.v1: full introspection report shape with required fields for
wire / capabilities / models / stats. capabilities object enumerates
all 10 flag names (current 6 true + future 4 false) as required keys.

error.v1: 7-code enum + permissive details object. Real emitted
details shapes documented in description (per-code context varies and
some fields are interim until IoFailure / OpTimeout typed signals
land in follow-up).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 12:35:15 +09:00
th-kim0823
3725986af7 🧪 test(kebab-cli): integration coverage for kebab schema + error.v1 (fb-27)
cli_schema: exercises `kebab schema` (text + --json) on a fresh-but-init'd
KB. Pins schema_version, kebab_version non-empty, capabilities.json_mode
true, capabilities.mcp_server false (future placeholder).

cli_error_wire: spawns `kebab --json --config <malformed.toml> ingest`
and verifies stderr emits a single error.v1 ndjson line with
code == "config_invalid". Non-JSON mode regression-pinned to keep the
legacy `error:` prefix. Note: --config /nonexistent silently falls back
to defaults (by design); a file that exists but fails TOML parsing is
the reliable trigger for config_invalid.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 12:33:46 +09:00
th-kim0823
912c7aa07d feat(kebab-cli): emit error.v1 ndjson on stderr in --json mode (fb-27)
Wraps the existing `Err(e)` arm with a `cli.json` branch:
- `--json`: stderr ndjson `error.v1` via wire_error_v1
- non-`--json`: legacy `error: <msg>` text path (unchanged)

exit_code() unchanged — RefusalSignal/NoHitSignal/DoctorUnhealthy
still drive 1/1/3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 12:28:41 +09:00
th-kim0823
4eb13c63ae feat(kebab-cli): kebab schema subcommand (fb-27)
Text mode: doctor-style key/value layout. JSON mode: schema.v1 wire
record. Honors `--config <path>` via the established
`kebab_app::schema_with_config(&cfg)` facade pattern (per the P3-5 /
P4-3 regression conventions).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 12:24:06 +09:00
th-kim0823
c91228e7d5 feat(kebab-cli): error_classify dispatcher + wire helpers (fb-27)
`error_classify::classify` maps anyhow::Error → ErrorV1 wire record by
downcasting to known typed errors (LlmError + ConfigInvalid + NotIndexed
re-exported from kebab_app::error_signal, plus std::io::Error chain).
Generic fallback emits `code: "generic"` with the chain in `details` when
verbose.

wire.rs adds wire_schema (idempotent re-tag, mirrors wire_doctor pattern
since SchemaV1 carries its own schema_version field) and wire_error_v1
(simple tag_object). Tests pin both wrappers + 7 classify code paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 12:11:54 +09:00
th-kim0823
3e33daaa9b 🧪 test(kebab-app): assert chunk_count + asset_count in schema_report (fb-27)
Plug coverage hole flagged in code review — test 1 was asserting only
doc_count + last_ingest_at, leaving count_summary's chunk_count and
asset_count queries un-pinned.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 12:06:04 +09:00
th-kim0823
ab96335174 🧪 test(kebab-app): schema_with_config integration coverage (fb-27)
Two scenarios: freshly-ingested 2-doc KB (stats reflect counts +
last_ingest_at populated) and empty-but-initialized KB (counts zero,
last_ingest_at None). The empty case runs ingest_with_config over an
empty workspace dir to seed kebab.sqlite before calling schema_with_config,
since open_existing (used internally) returns NotIndexed if the DB is absent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 12:02:20 +09:00
th-kim0823
61aae1c1d5 🏗️ refactor(kebab-app): consolidate PARSER_VERSION + clarify intent (fb-27)
Replace kebab-app's private `KEBAB_PARSE_MD_VERSION` literal with a
direct reference to `kebab_parse_md::PARSER_VERSION` so the parser
version cascade has a single source of truth (design §9 invariant).

Add maintenance comment on schema.rs WIRE_SCHEMAS const pointing to
docs/wire-schema/v1/ + kebab-cli wire helpers as the authoritative
sources to keep in sync.

Tighten open_existing doc comment to match the actual SQLITE_OPEN_READ_WRITE
flag (needed for WAL pragma application) — callers should still avoid
issuing mutations through this connection.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 11:58:06 +09:00
th-kim0823
39b4433549 feat(kebab-app): schema_with_config facade (fb-27)
New `SchemaV1` struct + `schema_with_config(&Config)` builder. Surfaces
wire schemas list, capabilities (current + future placeholders), model
versions (parser/chunker/embedding/prompt_template/index/corpus_revision),
and stats (doc/chunk/asset counts + last ingest). kebab-store-sqlite
gains `count_summary()` to back the stats block.

Deviations from plan:
- `cfg.models.embedding.id` → `cfg.models.embedding.model` (actual field name)
- No `Config::expand_path` method → free fn `kebab_config::expand_path(&cfg.storage.data_dir, "")`
- `PARSER_VERSION` added to `kebab-parse-md/src/lib.rs` (was absent; synced with `KEBAB_PARSE_MD_VERSION` literal in kebab-app)
- `INDEX_VERSION_STR` added to `kebab-store-vector/src/store.rs` + re-exported from `lib.rs` (was a private `const`)
- `corpus_revision()` returns `u64` directly (not `Result<u64>`) — no `?` in collect_models
- `SchemaV1` carries `schema_version: "schema.v1"` field (wire schema convention)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 11:46:37 +09:00
th-kim0823
1c4d554bf4 🏗️ refactor(kebab-store-sqlite): harden open_existing against silent create (fb-27)
Replace `path.exists()` + `Connection::open` (which silently CREATEs on
race) with `Connection::open_with_flags` using READ_WRITE|URI but NOT
CREATE. SQLite surfaces `SQLITE_CANTOPEN` for missing files; we wrap as
NotIndexed { found: None } as before.

Adds open_existing_does_not_create_missing_db regression test pinning
the no-side-effect invariant.

Also documents read-only intent on open_existing, the format contract
on NotIndexed.found, and removes scaffolding comments from kebab-app
error_signal that are no longer load-bearing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 11:40:42 +09:00
th-kim0823
d7bfd01ef5 feat(kebab-store-sqlite): add NotIndexed typed error (fb-27)
New `SqliteStore::open_existing` API + `NotIndexed` signal for the
missing-DB case. kebab-app re-exports the type via its `error_signal`
module so kebab-cli's `error_classify` can map it to
`error.v1 { code: "not_indexed" }`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 11:32:04 +09:00
th-kim0823
26a2e021b0 🏗️ refactor(kebab-config): stabilize ConfigInvalid.cause prefix (fb-27)
Replace `read failed: {e}` / `parse failed: {e}` with the underscore-
slugged `read_failed:` / `parse_failed:` prefixes so kebab-cli's
error_classify (Task 8) and the error.v1 JSON Schema (Task 14) can
treat the prefix as a stable wire contract while leaving the OS /
toml-crate detail in the suffix as free-form context.

Also add the symmetric `cause` non-empty assertion to the malformed-TOML
test so a regression that empties `cause` on the parse path would be
caught.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 11:29:00 +09:00
th-kim0823
58c06664b8 feat(kebab-config): add ConfigInvalid typed error (fb-27)
Wraps every error path in `Config::from_file` (read failure, TOML parse,
validation) so downstream callers can `downcast_ref::<ConfigInvalid>()`
to build the `error.v1` wire record. kebab-app re-exports the type via
its `error_signal` module.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 11:22:58 +09:00
th-kim0823
3efdf7ef2f 🏗️ chore(kebab-app): scaffold error_signal module (fb-27)
Re-exports existing doctor_signal entries (RefusalSignal / NoHitSignal /
DoctorUnhealthy) + LlmError from kebab-llm-local. ConfigInvalid /
NotIndexed re-exports added in subsequent tasks once the source crates
define them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 11:17:58 +09:00
th-kim0823
c20da0f274 📝 docs(plan): p9-fb-27 implementation plan
17-task TDD plan covering: typed signal scaffolding (kebab-app
error_signal module), ConfigInvalid + NotIndexed typed errors, SchemaV1
struct + schema_with_config facade, count_summary helper, wire_schema +
wire_error_v1 helpers, error_classify dispatcher, Cmd::Schema CLI arm,
--json mode error.v1 emission, JSON Schema literals, doc sync.

Each task = bite-sized TDD cycle (write failing test → impl → verify
pass → commit). Final task = workspace clippy + cargo test --workspace
-j 1 + manual smoke.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 11:12:05 +09:00
th-kim0823
f01f8dfc9b 📝 docs(spec): p9-fb-27 introspection + error wire 설계 문서
`kebab schema` 신규 명령 + `error.v1` wire schema 도입을 위한 brainstorm
산출물. agent 통합 (fb-30 MCP, fb-29 daemon) 의 prerequisite — 한 번의
introspection 호출로 wire 버전 / capabilities / model versions / index
stats 를 노출하고, fatal error 가 `--json` 모드에서 stderr ndjson 으로
구조화된다.

핵심 결정:
- `kebab schema --json` 단일 명령 (정적 + 동적 통합)
- error.v1 emission 은 `--json` 모드에서만 — 비 `--json` 은 기존 stderr text 유지
- exit code 0/1/2/3 unchanged, error.v1.code 가 fine-grained 분기
- 7 code initial set (config_invalid / not_indexed / model_unreachable /
  model_not_pulled / timeout / io_error / generic) + future-additive 정책

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 10:58:24 +09:00
16b4f9fb9f 📝 docs(HANDOFF): 도그푸딩 피드백에 따른 백로그 항목 추가
- P9 dogfooding 백로그 항목 fb-26 ~ fb-42 추가
- 각 항목의 목표, 증상, 후속 작업 및 위험 요소 명시
- release 계획에 따른 0.3.0 ~ 0.6.0 분할

📝 docs(INDEX): 백로그 항목에 대한 세부 정보 추가

- fb-26 ~ fb-42 항목의 세부 정보 및 상태 추가
- 각 항목의 목표와 후속 작업 명시
- 도그푸딩 피드백에 따른 개선 사항 반영

🔧 chore(tasks): 새로운 백로그 항목 파일 생성

- p9-fb-26 ~ p9-fb-42 각 항목에 대한 개별 파일 생성
- 각 파일에 목표, 증상, 후속 작업 및 위험 요소 포함
- doogfooding 피드백을 기반으로 한 개선 사항 문서화
2026-05-06 13:26:36 +00:00
dd172af47c Merge pull request 'feat(integrations): ship Claude Code skill for kebab' (#103) from feat/claude-code-skill into main
Reviewed-on: #103
2026-05-06 09:04:31 +00:00
th-kim0823
e31871d03e feat(integrations): ship Claude Code skill for kebab
Add `integrations/claude-code/kebab/` — a Claude Code skill that calls
`kebab search --json` / `kebab ask --json` automatically when the user
asks an internal-context / runbook / wiki-lookup question. Install via
`cp -r integrations/claude-code/kebab ~/.claude/skills/` (or symlink for
in-place updates).

The shipped SKILL.md keeps its frontmatter `description` generic so the
trigger fires on any "internal / org-specific" cue — per-user keywords
(team / system / acronym) belong in a local override, not in the
repo-shipped frontmatter.

CLAUDE.md §Wire schema v1: add a paragraph documenting the
`integrations/<host>/` layout and the rule that a wire schema major bump
(v1→v2) updates each shipped integration in the same PR — same shape as
the version-cascade rule.

README.md §외부 AI 통합: replace the "~50줄 wrapper" prose with a direct
link to `integrations/claude-code/`, since the wrapper is now in-tree.
2026-05-06 18:00:53 +09:00
5398fb057c Merge pull request 'chore: bump workspace version 0.2.0 → 0.2.1 (p9-fb-25 release)' (#102) from chore/bump-v0.2.1 into main
Reviewed-on: #102
v0.2.1
2026-05-05 12:51:33 +00:00
1409eaae51 chore: bump workspace version 0.2.0 → 0.2.1 — p9-fb-25 release
사용자 도그푸딩 / 실사용 binary 필요로 release bump (CLAUDE.md release
규약 트리거 1).

p9-fb-25 변경사항:

- WorkspaceCfg.include 제거 (옛 config 의 include = [...] silently
  무시 + 단발 deprecation warning).
- IngestItem.warnings 가 Skipped 시 사유 채움 (`unsupported media
  type: .docx` 등).
- IngestReport.skipped_by_extension: BTreeMap<String, u32> 신규
  (additive wire — v1 호환).
- CLI / TUI summary 에 breakdown (`5 skipped: 3 docx, 1 txt`).
- kebab init 헤더 + README 에 지원 형식 (md / png / jpg / pdf) 명시.

caller breaking 없음 (모든 surface 변경 backward-compat). 730
워크스페이스 테스트 통과.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 12:49:10 +00:00
8dadac2a45 Merge pull request 'fix(kebab-config): p9-fb-25 — workspace.include 제거 + 지원 형식 가시성' (#101) from fix/p9-fb-25-config-include-removal into main
Reviewed-on: #101
2026-05-05 12:46:34 +00:00
e4432a2388 review(p9-fb-25): 회차 1 nit 반영 — render_skipped_breakdown 단일 source + NO_EXT_SENTINEL + 카운트 + deprecation 문구
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 12:35:10 +00:00
51feff5f16 docs(p9-fb-25): README + HANDOFF + HOTFIXES + INDEX + per-task spec
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 12:20:38 +00:00
44dee2c30f feat(kebab-cli, kebab-tui): p9-fb-25 task 6 — render skipped-by-extension breakdown
Append ": A docx, B txt, ..." after the N skipped count in both the
CLI ingest summary and TUI status_line terminal events (completed +
aborted). Breakdown is desc-sorted by count, ties broken by key
alphabetic; empty map produces no extra text.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 12:16:43 +00:00
9545367904 feat(kebab-app): p9-fb-25 task 5 — Skipped warnings + skipped_by_extension aggregation
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 12:13:13 +00:00
693f5582f0 feat(kebab-core, kebab-app): p9-fb-25 task 4 — IngestReport.skipped_by_extension + wire schema additive
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 12:06:34 +00:00
d64282433c feat(kebab-app): p9-fb-25 task 3 — init_workspace header lists supported extensions
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 11:55:38 +00:00
ef5d0770ae review(p9-fb-25-task1): fix kebab-app test references to removed WorkspaceCfg.include
reviewer-flagged: task 1 missed test files using cfg.workspace.include.

- crates/kebab-app/tests/common/mod.rs: SourceScope literal switched
  to ..Default::default().
- crates/kebab-app/tests/image_pipeline.rs (×3): drop dead-no-op
  cfg.workspace.include.push(...) calls; comment explains removal.
- crates/kebab-app/tests/pdf_pipeline.rs: same treatment.

Pre-fb-25 these pushes were no-ops (include was dead config field
not enforced anywhere). Removal is purely mechanical.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 11:53:19 +00:00
7f31721a47 refactor(kebab-cli, kebab-tui): p9-fb-25 task 2 — SourceScope via ..Default::default()
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 11:47:39 +00:00
b22c8cfd45 feat(kebab-config): p9-fb-25 task 1 — drop WorkspaceCfg.include + deprecation probe
- Remove `pub include: Vec<String>` from `WorkspaceCfg` struct (denylist-only model).
- Drop `include: vec!["**/*.md"]` from `Config::defaults()`.
- Add `from_file` deprecation probe: raw `toml::Value` scan fires a
  one-shot `tracing::warn!` (via `OnceLock`) when an old config still
  carries `workspace.include = [...]`. serde ignores the unknown field
  cleanly (no `deny_unknown_fields`).
- Compile-fix `kebab-cli` (main.rs:329) and `kebab-tui`
  (ingest_progress.rs:39): replace `cfg.workspace.include.clone()` with
  `Vec::new()` (Task 2 will switch to `..Default::default()`).
- Two new tests: `legacy_include_field_is_ignored_silently` (backward
  compat round-trip) + `workspace_cfg_has_only_root_and_exclude_fields`
  (exhaustive destructure — compile-time guard against re-introduction).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 11:44:35 +00:00