[content] ContentResolver trait and GitalyDirectContentResolver
## Problem to Solve
When agents expand `File`, `Definition`, or `ImportedSymbol` nodes in GKG queries, the server returns metadata (path, name, line numbers) but cannot return the actual source code. There is no mechanism to fetch file content at query time — the indexer has content during processing (via Gitaly) but discards it after extracting metadata.
Content resolution must be batch-aware: a typical agent query returns 100–1,000 nodes, and per-file round-trips to Gitaly are unacceptable (1,000 × 5ms = 5 seconds).
## Proposed Solution
Introduce a `ContentResolver` trait and `GitalyDirectContentResolver` implementation in `gkg-server`:
**`ContentResolver` trait** (`gkg-server/src/content/resolver.rs`):
- `resolve_batch(requests: &[ContentRequest]) -> Result<HashMap<i64, Bytes>>`
- `ContentRequest`: `{ node_id, organization_id, root_namespace_id, project_id, branch, file_path, blob_sha?, start_byte?, end_byte? }`
- Batch limit enforcement (default 1,000)
**`GitalyDirectContentResolver`**:
- Groups requests by `project_id` (Gitaly's `list_blobs` is per-project)
- Dispatches concurrent `list_blobs` calls via the existing `GitlabClient` HTTP API
- Uses `<branch>:<path>` revision format (Path B — no schema changes needed)
- Slices content for `Definition` nodes using `start_byte`/`end_byte`
**Key files to create/modify**:
- `crates/gkg-server/src/content/mod.rs` — module root
- `crates/gkg-server/src/content/resolver.rs` — trait + request types
- `crates/gkg-server/src/content/gitaly_direct.rs` — GitalyDirect impl
- `crates/gkg-server/src/content/types.rs` — ContentRequest, error types
## Acceptance Criteria
- [x] `ContentResolver` trait defined with batch API
- [x] `GitalyDirectContentResolver` groups by project_id and uses concurrent `list_blobs`
- [x] Batch limit enforced with structured error
- [x] Definition content sliced by start_byte/end_byte
- [x] Missing blobs handled gracefully (logged, not batch-failing)
- [x] Unit tests with mock Gitaly responses
issue