|
| 1 | +# Session Handoff — Phase 5 + Phase 6 |
| 2 | + |
| 3 | +> Tài liệu bàn giao cho session tiếp theo. Đọc trước khi bắt đầu code. |
| 4 | +
|
| 5 | +--- |
| 6 | + |
| 7 | +## Trạng thái hiện tại |
| 8 | + |
| 9 | +**Branch**: `claude/code-intelligence-setup-6q6zn7` |
| 10 | +**PR**: #2 (draft) — đã bao gồm Phase 0.5 → Phase 4 |
| 11 | +**CI**: Green (clippy, fmt, test — 94 tests pass) |
| 12 | +**Repo chỉ được sửa**: `Code-Intelligence`. **TUYỆT ĐỐI KHÔNG** thay đổi `SUPER-MCP` (chỉ dùng tham chiếu). |
| 13 | + |
| 14 | +### Đã hoàn thành |
| 15 | + |
| 16 | +| Phase | Nội dung | Commit | |
| 17 | +|-------|----------|--------| |
| 18 | +| 0.5 | CI pipeline, debt registry, ADR-0001 | `96c0b19` | |
| 19 | +| 1B | Analysis module ports (coverage, dead_code, codeowners, hotspot, diff_impact), C-1 regression test | `96c0b19` | |
| 20 | +| 2 | FormalResolver (stack-graphs), ConservativeResolver, EdgeConfidence, ADR-0002 | `0417e15` | |
| 21 | +| 3 | FTS5 dual-column BM25, output sanitizer (10 patterns), search module (5 SearchKind) | `a2c3969` | |
| 22 | +| 4 | 16 MCP tool handlers via rmcp `#[tool]`, clap CLI, ServerHandler, Mutex\<Connection\> | `f7b79a4` | |
| 23 | + |
| 24 | +### Codebase structure |
| 25 | + |
| 26 | +``` |
| 27 | +crates/ |
| 28 | +├── ci-core/src/ |
| 29 | +│ ├── lib.rs # pub mod: db, graph, config, types, analysis, resolver, search, sanitize |
| 30 | +│ ├── db/schema.rs # SQLite schema: symbols, file_index, call_edges, import_edges, fts_exact, fts_tokens |
| 31 | +│ ├── db/queries.rs # batch_callees, batch_callers |
| 32 | +│ ├── graph/coreness.rs # K-core O(V+E) |
| 33 | +│ ├── graph/hub.rs # Hub detection (C-2 fix) |
| 34 | +│ ├── graph/path.rs # Bidirectional BFS (F1,F2,F3,F10) |
| 35 | +│ ├── graph/tokenize.rs # Identifier tokenization |
| 36 | +│ ├── resolver/formal.rs # Stack Graphs Python resolver |
| 37 | +│ ├── resolver/conservative.rs # Alias-tracking 6 languages |
| 38 | +│ ├── search.rs # FTS5 search: symbol, text, file, semantic(stub), hybrid(RRF k=20) |
| 39 | +│ ├── sanitize.rs # Credential patterns → [REDACTED:label] |
| 40 | +│ ├── config.rs # Config loading + defaults |
| 41 | +│ ├── types.rs # 11 enums (SearchKind, EdgeConfidence, TerminatedBy, etc.) |
| 42 | +│ └── analysis/ # coverage, dead_code, codeowners, hotspot, diff_impact (stubs with types) |
| 43 | +├── ci-server/src/ |
| 44 | +│ ├── lib.rs # serve_stdio(), doctor(), default_db_path() |
| 45 | +│ └── tools.rs # 16 MCP tools, CodeIntelligenceServer, ServerHandler impl |
| 46 | +└── ci-cli/src/ |
| 47 | + └── main.rs # clap CLI: ci serve, ci index(stub), ci doctor |
| 48 | +``` |
| 49 | + |
| 50 | +### Key technical details |
| 51 | + |
| 52 | +- **rmcp 0.1.5** — `#[tool(tool_box)]` on impl block generates tool_box + ServerHandler methods. `#[tool]` macro only supports `name`, `description`, `vis` attributes. **KHÔNG có `annotations()`** — rmcp 0.1.5 doesn't support tool annotations in macro. |
| 53 | +- **Mutex\<Connection\>** — rusqlite::Connection is NOT Sync. Server uses `Arc<Mutex<Connection>>` with `db()` helper. For `prepare()` calls, must bind guard to local variable (`let conn = self.db(); let stmt = conn.prepare(...)`) to avoid temporary dropped while borrowed. |
| 54 | +- **Tool return type** — rmcp tools return `String` (implements `IntoContents`). No `Json<T>` wrapper. Serialize via `serde_json::to_string_pretty(&output).unwrap_or_default()`. |
| 55 | +- **Parameters** — `rmcp::handler::server::tool::Parameters<T>` for tool input deserialization. |
| 56 | +- **Tests** — 94 tests across ci-core (search: 11, sanitize: 11, graph: 12, db: 2, resolver: various). ci-server has 0 unit tests currently. |
| 57 | + |
| 58 | +--- |
| 59 | + |
| 60 | +## Phase 5 — Capability Mới |
| 61 | + |
| 62 | +**Ref**: `docs/migration-plan-v2.md` → "Phase 5 — Capability Mới" |
| 63 | + |
| 64 | +### 5.1 — `symbol_metrics_history` table |
| 65 | + |
| 66 | +Thêm bảng mới vào `ci-core/src/db/schema.rs`: |
| 67 | + |
| 68 | +```sql |
| 69 | +CREATE TABLE IF NOT EXISTS symbol_metrics_history ( |
| 70 | + id INTEGER PRIMARY KEY AUTOINCREMENT, |
| 71 | + qualified_name TEXT NOT NULL, |
| 72 | + snapshot_at TEXT NOT NULL, -- ISO8601 timestamp |
| 73 | + caller_count INTEGER NOT NULL DEFAULT 0, |
| 74 | + callee_count INTEGER NOT NULL DEFAULT 0, |
| 75 | + coreness INTEGER NOT NULL DEFAULT 0, |
| 76 | + is_hub INTEGER NOT NULL DEFAULT 0, |
| 77 | + churn_count INTEGER NOT NULL DEFAULT 0, |
| 78 | + complexity REAL, |
| 79 | + UNIQUE(qualified_name, snapshot_at) |
| 80 | +); |
| 81 | +CREATE INDEX IF NOT EXISTS idx_smh_symbol ON symbol_metrics_history(qualified_name); |
| 82 | +CREATE INDEX IF NOT EXISTS idx_smh_time ON symbol_metrics_history(snapshot_at); |
| 83 | +``` |
| 84 | + |
| 85 | +Cập nhật `init_db()` để include migration. Tham khảo pattern migration idempotent đã có (CREATE TABLE IF NOT EXISTS). |
| 86 | + |
| 87 | +### 5.2 — `ci fitness-check` CLI command |
| 88 | + |
| 89 | +**File**: `crates/ci-cli/src/main.rs` — thêm subcommand `FitnessCheck` |
| 90 | + |
| 91 | +```rust |
| 92 | +FitnessCheck { |
| 93 | + #[arg(long, default_value = ".")] |
| 94 | + project_root: PathBuf, |
| 95 | + #[arg(long)] |
| 96 | + config: Option<PathBuf>, // thresholds.toml |
| 97 | + #[arg(long, default_value = "false")] |
| 98 | + json: bool, // JSON output for CI parsing |
| 99 | +} |
| 100 | +``` |
| 101 | + |
| 102 | +**Logic** (`crates/ci-core/src/fitness.rs` — file mới): |
| 103 | +1. Load thresholds từ TOML (hoặc defaults): |
| 104 | + ```toml |
| 105 | + [thresholds] |
| 106 | + max_hub_count = 50 |
| 107 | + max_avg_coreness = 15.0 |
| 108 | + max_dead_code_pct = 10.0 |
| 109 | + max_hotspot_risk = 0.75 |
| 110 | + min_edge_coverage_pct = 60.0 |
| 111 | + ``` |
| 112 | +2. Query DB cho current metrics: |
| 113 | + - Hub count: `SELECT COUNT(*) FROM symbols WHERE is_hub = 1` |
| 114 | + - Avg coreness: `SELECT AVG(coreness) FROM symbols WHERE coreness > 0` |
| 115 | + (Lưu ý: `symbols` table chưa có column `coreness` — cần thêm hoặc compute on-the-fly từ graph) |
| 116 | + - Dead code %: delegate tới `analysis::dead_code` (nếu đã port đầy đủ, hoặc stub) |
| 117 | + - Hotspot risk: max risk từ `analysis::hotspot` |
| 118 | + - Edge coverage: `SELECT COUNT(DISTINCT from_symbol) FROM call_edges` / total symbols |
| 119 | +3. Compare với thresholds |
| 120 | +4. Exit code: 0 nếu pass, 1 nếu fail |
| 121 | +5. Output: human-readable default, JSON nếu `--json` |
| 122 | + |
| 123 | +**Tham khảo**: SUPER-MCP `npm run ci` pattern — fitness check chạy như CI gate trong target project. |
| 124 | + |
| 125 | +### 5.3 — Structured telemetry |
| 126 | + |
| 127 | +**File**: `crates/ci-server/src/telemetry.rs` (file mới) |
| 128 | + |
| 129 | +Telemetry events qua `tracing`: |
| 130 | +```rust |
| 131 | +tracing::info!( |
| 132 | + tool = tool_name, |
| 133 | + duration_ms = elapsed.as_millis(), |
| 134 | + result_size = result.len(), |
| 135 | + "tool_execution_completed" |
| 136 | +); |
| 137 | +``` |
| 138 | + |
| 139 | +Cần wrap tool execution trong timing logic. Có 2 cách: |
| 140 | +1. **Middleware approach**: Wrap `call_tool` trong ServerHandler impl để tự động time mọi tool call. |
| 141 | +2. **Per-tool approach**: Thêm timing vào mỗi tool function. |
| 142 | + |
| 143 | +Recommend option 1 — override `call_tool` trong `impl ServerHandler`: |
| 144 | +```rust |
| 145 | +async fn call_tool(&self, req: CallToolRequestParam, ctx: RequestContext<RoleServer>) -> Result<CallToolResult, McpError> { |
| 146 | + let start = std::time::Instant::now(); |
| 147 | + let tool_name = req.name.clone(); |
| 148 | + let result = Self::tool_box().call(ToolCallContext::new(self, req, ctx)).await; |
| 149 | + tracing::info!(tool = %tool_name, duration_ms = start.elapsed().as_millis(), "tool_call"); |
| 150 | + result |
| 151 | +} |
| 152 | +``` |
| 153 | + |
| 154 | +**Lưu ý**: `#[tool(tool_box)]` trên `impl ServerHandler` đã generate `call_tool`. Nếu muốn override, cần KHÔNG dùng `@derive` cho `call_tool` mà viết tay. Có thể cần restructure: dùng `#[tool(tool_box)]` chỉ trên plain impl block (generates `fn tool_box()`), rồi viết `impl ServerHandler` manually với custom `call_tool` + `list_tools` delegation. |
| 155 | + |
| 156 | +### 5.4 — Snapshot writer |
| 157 | + |
| 158 | +**File**: `crates/ci-core/src/fitness.rs` hoặc `crates/ci-core/src/db/metrics.rs` |
| 159 | + |
| 160 | +Function `snapshot_metrics(conn, timestamp)`: |
| 161 | +1. Query current symbols + coreness + caller_count |
| 162 | +2. INSERT INTO symbol_metrics_history |
| 163 | +3. Gọi từ `ci index` sau khi index xong, hoặc từ `ci fitness-check` |
| 164 | + |
| 165 | +### 5.5 — Tests cần viết |
| 166 | + |
| 167 | +- `fitness.rs`: threshold pass/fail logic, default thresholds, TOML parsing |
| 168 | +- `telemetry.rs`: verify tracing events emitted (dùng `tracing_test` crate hoặc custom subscriber) |
| 169 | +- `db/schema.rs`: verify `symbol_metrics_history` table created, insert/query works |
| 170 | +- Integration: `ci fitness-check` exit code 0/1 |
| 171 | + |
| 172 | +### 5.6 — Exit criteria |
| 173 | + |
| 174 | +- [ ] `symbol_metrics_history` table exists, migration idempotent |
| 175 | +- [ ] `ci fitness-check` exit 0 khi metrics within thresholds |
| 176 | +- [ ] `ci fitness-check` exit 1 khi vượt threshold, message rõ ràng |
| 177 | +- [ ] `ci fitness-check --json` output parseable JSON |
| 178 | +- [ ] Telemetry events in stderr khi tool called |
| 179 | +- [ ] CI green (clippy, fmt, tests) |
| 180 | + |
| 181 | +--- |
| 182 | + |
| 183 | +## Phase 6 — Distribution |
| 184 | + |
| 185 | +**Ref**: `docs/migration-plan-v2.md` → "Phase 6 — Distribution" |
| 186 | + |
| 187 | +### 6.1 — Cross-compile static binary |
| 188 | + |
| 189 | +**File**: `.github/workflows/release.yml` (file mới) |
| 190 | + |
| 191 | +```yaml |
| 192 | +name: Release |
| 193 | +on: |
| 194 | + push: |
| 195 | + tags: ['v*'] |
| 196 | + |
| 197 | +jobs: |
| 198 | + build: |
| 199 | + strategy: |
| 200 | + matrix: |
| 201 | + include: |
| 202 | + - target: x86_64-unknown-linux-musl |
| 203 | + os: ubuntu-latest |
| 204 | + - target: aarch64-unknown-linux-musl |
| 205 | + os: ubuntu-latest |
| 206 | + - target: aarch64-apple-darwin |
| 207 | + os: macos-latest |
| 208 | + runs-on: ${{ matrix.os }} |
| 209 | + steps: |
| 210 | + - uses: actions/checkout@v4 |
| 211 | + - uses: dtolnay/rust-toolchain@stable |
| 212 | + with: |
| 213 | + targets: ${{ matrix.target }} |
| 214 | + - name: Install musl tools (Linux) |
| 215 | + if: contains(matrix.target, 'musl') |
| 216 | + run: sudo apt-get install -y musl-tools |
| 217 | + - name: Install cross (aarch64-linux) |
| 218 | + if: matrix.target == 'aarch64-unknown-linux-musl' |
| 219 | + run: cargo install cross |
| 220 | + - name: Build |
| 221 | + run: | |
| 222 | + if [ "${{ matrix.target }}" = "aarch64-unknown-linux-musl" ]; then |
| 223 | + cross build --release --target ${{ matrix.target }} |
| 224 | + else |
| 225 | + cargo build --release --target ${{ matrix.target }} |
| 226 | + fi |
| 227 | + - name: Upload artifact |
| 228 | + uses: actions/upload-artifact@v4 |
| 229 | + with: |
| 230 | + name: ci-${{ matrix.target }} |
| 231 | + path: target/${{ matrix.target }}/release/ci |
| 232 | + |
| 233 | + release: |
| 234 | + needs: build |
| 235 | + runs-on: ubuntu-latest |
| 236 | + permissions: |
| 237 | + contents: write |
| 238 | + steps: |
| 239 | + - uses: actions/download-artifact@v4 |
| 240 | + - name: Create release |
| 241 | + uses: softprops/action-gh-release@v2 |
| 242 | + with: |
| 243 | + files: ci-*/ci |
| 244 | +``` |
| 245 | +
|
| 246 | +**Lưu ý quan trọng**: tree-sitter grammars cần compile native. Khi cross-compile cho musl, cần verify tree-sitter-python/etc build thành công. Nếu fail → fallback: build trên CI native runner cho mỗi platform. |
| 247 | +
|
| 248 | +### 6.2 — Containerfile |
| 249 | +
|
| 250 | +**File**: `Containerfile` (root) |
| 251 | + |
| 252 | +```dockerfile |
| 253 | +FROM rust:1.85-alpine AS builder |
| 254 | +RUN apk add --no-cache musl-dev |
| 255 | +WORKDIR /build |
| 256 | +COPY Cargo.toml Cargo.lock ./ |
| 257 | +COPY crates/ crates/ |
| 258 | +RUN cargo build --release --target x86_64-unknown-linux-musl |
| 259 | +
|
| 260 | +FROM scratch |
| 261 | +COPY --from=builder /build/target/x86_64-unknown-linux-musl/release/ci /ci |
| 262 | +ENTRYPOINT ["/ci"] |
| 263 | +CMD ["serve", "--project-root", "/project", "--db-path", "/data/index.db"] |
| 264 | +``` |
| 265 | + |
| 266 | +### 6.3 — compose.yaml example |
| 267 | + |
| 268 | +**File**: `compose.yaml` (root) |
| 269 | + |
| 270 | +```yaml |
| 271 | +services: |
| 272 | + code-intelligence: |
| 273 | + build: . |
| 274 | + read_only: true |
| 275 | + cap_drop: [ALL] |
| 276 | + security_opt: [no-new-privileges:true] |
| 277 | + pids_limit: 64 |
| 278 | + mem_limit: 256m |
| 279 | + volumes: |
| 280 | + - ./:/project:ro |
| 281 | + - ci-data:/data |
| 282 | + command: ["serve", "--project-root", "/project", "--db-path", "/data/index.db"] |
| 283 | +
|
| 284 | +volumes: |
| 285 | + ci-data: |
| 286 | +``` |
| 287 | + |
| 288 | +### 6.4 — `ci init` command |
| 289 | + |
| 290 | +**File**: `crates/ci-cli/src/main.rs` — thêm subcommand |
| 291 | + |
| 292 | +```rust |
| 293 | +Init { |
| 294 | + #[arg(long, default_value = ".")] |
| 295 | + project_root: PathBuf, |
| 296 | +} |
| 297 | +``` |
| 298 | + |
| 299 | +Logic: |
| 300 | +1. Detect project root (tìm `.git/`, `Cargo.toml`, `package.json`, `pyproject.toml`) |
| 301 | +2. Tạo `.codeindex/` directory |
| 302 | +3. Tạo `.codeindex/config.json` với defaults từ `config.rs` |
| 303 | +4. Print hướng dẫn: "Run `ci index` to build the index, then `ci serve` to start MCP server" |
| 304 | + |
| 305 | +### 6.5 — Tests cần viết |
| 306 | + |
| 307 | +- Release workflow: test manually bằng `act` (GitHub Actions local runner) hoặc push test tag |
| 308 | +- Containerfile: `docker build .` + `docker run --rm ci doctor` |
| 309 | +- `ci init`: verify config file created, idempotent (không overwrite existing) |
| 310 | + |
| 311 | +### 6.6 — Exit criteria |
| 312 | + |
| 313 | +- [ ] Binary downloadable cho 3 platform (x86_64-linux-musl, aarch64-linux-musl, aarch64-apple-darwin) |
| 314 | +- [ ] `ci init` tạo config file đúng |
| 315 | +- [ ] Container image build thành công |
| 316 | +- [ ] `docker run code-intelligence doctor` pass |
| 317 | +- [ ] Release workflow green on tag push |
| 318 | +- [ ] CI green (clippy, fmt, tests) |
| 319 | + |
| 320 | +--- |
| 321 | + |
| 322 | +## Gotchas & Lessons learned |
| 323 | + |
| 324 | +1. **rmcp 0.1.5 API quirks**: |
| 325 | + - `#[tool]` macro chỉ support `name`, `description`, `vis`. KHÔNG có `annotations()`. |
| 326 | + - `#[tool(tool_box)]` trên plain impl → generates `fn tool_box()`. Trên `impl ServerHandler` → generates `list_tools()` + `call_tool()`. |
| 327 | + - Tool functions return `String` (not `Json<T>`). Serialize output manually. |
| 328 | + - `Parameters<T>` is at `rmcp::handler::server::tool::Parameters`. |
| 329 | + |
| 330 | +2. **rusqlite::Connection is NOT Sync** → phải wrap trong `Mutex`. Khi dùng `prepare()`, PHẢI bind guard vào local variable: |
| 331 | + ```rust |
| 332 | + let conn = self.db(); // MutexGuard lives here |
| 333 | + let mut stmt = conn.prepare(...) // borrows conn |
| 334 | + ``` |
| 335 | + KHÔNG ĐƯỢC: `self.db().prepare(...)` — temporary guard dropped. |
| 336 | + |
| 337 | +3. **CI on this repo** uses Rust 1.96.0+ which has newer clippy lints (e.g., `useless_conversion`). Always test clippy with `-- -D warnings` locally trước khi push. |
| 338 | + |
| 339 | +4. **`cargo fmt --all` và `cargo clippy --workspace`** — luôn chạy CẢ HAI và verify TỪNG CÁI riêng biệt. Đừng chain `&&` rồi chỉ check exit code cuối. |
| 340 | + |
| 341 | +5. **SUPER-MCP repo** (`/home/user/SUPER-MCP`) — CHỈ ĐỌC, KHÔNG SỬA. Dùng để tham khảo patterns. |
| 342 | + |
| 343 | +--- |
| 344 | + |
| 345 | +## Thứ tự thực hiện đề xuất |
| 346 | + |
| 347 | +### Phase 5 (ước lượng ~5-7 ngày) |
| 348 | +1. `symbol_metrics_history` table + migration |
| 349 | +2. `ci-core/src/fitness.rs` — threshold logic + TOML parsing |
| 350 | +3. `ci fitness-check` CLI subcommand |
| 351 | +4. Telemetry middleware trong `ci-server` |
| 352 | +5. Tests + clippy + fmt |
| 353 | +6. Commit + push + update PR |
| 354 | + |
| 355 | +### Phase 6 (ước lượng ~3-5 ngày) |
| 356 | +1. `ci init` CLI subcommand |
| 357 | +2. `.github/workflows/release.yml` |
| 358 | +3. `Containerfile` + `compose.yaml` |
| 359 | +4. Test cross-compile (ít nhất x86_64-musl) |
| 360 | +5. Tests + clippy + fmt |
| 361 | +6. Commit + push + update PR |
| 362 | + |
| 363 | +--- |
| 364 | + |
| 365 | +## Files quan trọng cần đọc trước khi bắt đầu |
| 366 | + |
| 367 | +| File | Tại sao | |
| 368 | +|------|---------| |
| 369 | +| `docs/migration-plan-v2.md` | Chi tiết Phase 5, 6 requirements | |
| 370 | +| `crates/ci-core/src/db/schema.rs` | Hiểu DB schema hiện tại, pattern migration | |
| 371 | +| `crates/ci-server/src/tools.rs` | Hiểu rmcp integration pattern, ServerHandler | |
| 372 | +| `crates/ci-server/src/lib.rs` | serve_stdio(), doctor() | |
| 373 | +| `crates/ci-cli/src/main.rs` | CLI structure, thêm subcommands ở đây | |
| 374 | +| `crates/ci-core/src/config.rs` | Config defaults | |
| 375 | +| `crates/ci-core/src/types.rs` | All type enums | |
| 376 | +| `/home/user/SUPER-MCP/` | Reference ONLY — telemetry patterns, container patterns | |
0 commit comments