Phase 0+1: solution skeleton, ports, adapters, host with parity to the Python wiki-index #1

Merged
k.renner merged 12 commits from feat/skeleton into main 2026-09-24 10:21:28 +00:00
Member

Design: homelab kubernetes/PROPOSAL-wiki-web-api.md (PR #939).

Layout: Wiki.Core (domain, nine ports, dependency-free vault parsing), Wiki.Application (sync with hash diff and per-model backfill, coordinator, rank, status), adapters for Gitea (note store, fact sink), llama.cpp embeddings, file-backed vector index and document catalog; Wiki.Host (composition root from appsettings, /health, /ready, /api/v1/status, /rank and /webhook shape-compatible with the Python service, POST /api/v1/sync behind an API key, Authentik-header and API-key auth, MudBlazor status page), Dockerfile, Woodpecker test and release pipelines. Contract suites in test/Wiki.Contracts run against every adapter. 80 tests, warnings as errors.

Parity (phase 1 acceptance): the host run locally against Gitea and the in-cluster wiki-embed, 40 labelled queries, same commit as the Python wiki-index: hit rate @1/@5/@10/@20 = 0.425/0.85/0.875/0.95 on both; MRR 0.604 vs 0.606; 35 of 40 identical top-10 sets.

Found and fixed: Gitea reset the keep-alive TLS connection twice during the 3094-document initial sync; the Gitea adapter now retries idempotent reads with backoff and recycles pooled connections.

Not in this PR: cluster manifests (homelab repo), TypeSafe and Anthropic adapters (later phases).

🤖 Generated with Claude Code

Design: homelab `kubernetes/PROPOSAL-wiki-web-api.md` (PR #939). **Layout**: `Wiki.Core` (domain, nine ports, dependency-free vault parsing), `Wiki.Application` (sync with hash diff and per-model backfill, coordinator, rank, status), adapters for Gitea (note store, fact sink), llama.cpp embeddings, file-backed vector index and document catalog; `Wiki.Host` (composition root from `appsettings`, `/health`, `/ready`, `/api/v1/status`, `/rank` and `/webhook` shape-compatible with the Python service, `POST /api/v1/sync` behind an API key, Authentik-header and API-key auth, MudBlazor status page), Dockerfile, Woodpecker test and release pipelines. Contract suites in `test/Wiki.Contracts` run against every adapter. 80 tests, warnings as errors. **Parity** (phase 1 acceptance): the host run locally against Gitea and the in-cluster wiki-embed, 40 labelled queries, same commit as the Python wiki-index: hit rate @1/@5/@10/@20 = 0.425/0.85/0.875/0.95 on both; MRR 0.604 vs 0.606; 35 of 40 identical top-10 sets. **Found and fixed**: Gitea reset the keep-alive TLS connection twice during the 3094-document initial sync; the Gitea adapter now retries idempotent reads with backoff and recycles pooled connections. Not in this PR: cluster manifests (homelab repo), TypeSafe and Anthropic adapters (later phases). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
claude-bot added 11 commits 2026-09-24 10:15:14 +00:00
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Batches requests (default size 2, matching llama-server's limits for long
notes), sorts responses by index, and normalises vectors to unit length.
CheckAsync prefers GET /health, falling back to a one-word embed call when
the server doesn't expose it. Registers as a keyed IEmbeddingModel so
multiple models can be wired up under different names.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
FileVectorIndex persists one binary file per model under index/ with
atomic tmp+rename writes; FileDocumentCatalog persists a single
catalog.json, rebuilding the link graph via WikiLinks.Extract on every
upsert. Both start empty and report health Ok with a reset detail if
their file on disk is corrupt, rather than crashing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
INoteStore reads the vault straight from Gitea (branch HEAD, a paged
recursive tree listing, raw file reads); IFactSink appends a new raw/
file via the contents API, numbering around any path collision. Both
verified against the live gitea.renner.uno API for response shape
rather than the swagger doc.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A long sync over one keep-alive connection to Gitea behind Traefik saw a
TLS connection reset after ~1000 documents and aborted the whole run.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
claude-bot added 1 commit 2026-09-24 10:18:34 +00:00
ci: registry login as the bot (org-owned repo); allow manual test runs
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/push/test Pipeline was successful
ded15eb96b
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
k.renner merged commit b48dd28b9e into main 2026-09-24 10:21:28 +00:00
k.renner deleted branch feat/skeleton 2026-09-24 10:21:28 +00:00
Sign in to join this conversation.