feat: learning view — make the growing knowledge base visible
Some checks failed
ci / build-test (push) Has been cancelled
ci / docker-build (push) Has been cancelled

First slice of the observability/learning UI (the "see the system come alive
and learn" ask). The Knowledge page was search-only — blank until you typed —
so the knowledge Nomos now writes via upsert_knowledge was invisible unless
you knew to search for it. Now the page LEADS with what the system knows and
is learning:

- internal/httpapi/knowledge.go: GET /api/v1/knowledge/recent — recency-ordered
  knowledge + a stats header (total, agent-authored, learned-this-week,
  by-kind). Custom route (not OpenAPI-generated), same auth as the rest.
- web Knowledge page rewrite: stat cards up top (Total / Written by Nomos /
  Learned this week / runbooks-investigations), then a "Recently learned" feed
  with agent-authored notes highlighted and badged "learned by Nomos", tags,
  and relative timestamps. A toggle filters to Nomos-only. Search still works,
  now as a mode you enter/clear rather than the whole page.

This turns "the system is getting smarter" from a claim into something you
watch fill up: every gotcha the agent records shows here within seconds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-10 19:22:35 +02:00
parent 60edff2065
commit ec41c0b828
4 changed files with 266 additions and 53 deletions

View File

@@ -139,6 +139,11 @@ func NewHandler(ctx context.Context, pool *db.Pool, cfg config.Config, uiHandler
// inherits the router's base middleware and applies auth via With().
r.With(combinedAuth(cfg)).Get("/api/v1/events/stream", s.serveSSE)
// Custom (non-OpenAPI) route: recency-ordered knowledge + stats for the
// Knowledge page's "what the system has learned" view. Registered after
// HandlerWithOptions so it wins over any generated catch-all.
r.With(combinedAuth(cfg)).Get("/api/v1/knowledge/recent", s.serveRecentKnowledge)
// Mount MCP at /mcp (plan R3-10)
nomosAgentID := uuid.Nil
if cfg.NomosAgentID != "" {