docs: document the non-OpenAPI routes carve-out (R11)

10 routes are registered manually on the chi router in server.go rather
than generated from openapi.yaml. Added a 'Non-OpenAPI routes' comment
block at the top of NewHandler listing each route with its structural
reason for the carve-out:

  - Auth/infra: /healthz, /api/v1/auth/oidc-*, /oidc-callback — bypass
    auth middleware or aren't JSON API
  - SSE override: /api/v1/events/stream — re-registered for Flush()
  - Ad-hoc aggregations: /knowledge/recent, /knowledge/content/{id},
    /activity/recent, /activity/session/{id}, /learning/timeline,
    /learning/trend — derived shapes with no schema type yet

Updated .agents/dev/CONTRIBUTING.md §OpenAPI codegen with the carve-out
policy: if an ad-hoc route stabilizes, promote it to openapi.yaml with a
proper schema and migrate the serve* function to a strict handler.
This commit is contained in:
2026-07-17 23:02:08 +02:00
parent 8709e01dcb
commit 7dc1c1ae39
3 changed files with 41 additions and 1 deletions

View File

@@ -97,6 +97,25 @@ func NewHandler(ctx context.Context, pool *db.Pool, cfg config.Config) http.Hand
MaxAge: 86400,
}))
// ─── Non-OpenAPI routes (carve-out) ────────────────────────────────
//
// These routes are registered manually on the chi router rather than
// generated from api/openapi.yaml. Each has a structural reason it
// can't go through the strict-server codegen:
//
// /healthz — infra liveness probe, no auth, no /api/v1 prefix
// /api/v1/auth/oidc-* — auth flow, must run before auth middleware
// /oidc-callback — standalone HTML page, not a JSON API
// /api/v1/events/stream — in OpenAPI but re-registered for SSE Flush()
// /api/v1/knowledge/recent — ad-hoc aggregation, no schema type yet
// /api/v1/knowledge/content/{id} — returns raw markdown, not a gen type
// /api/v1/activity/recent — recency-ordered, not paginated
// /api/v1/activity/session/{id} — session-scoped aggregation
// /api/v1/learning/timeline — derived view, no backing schema type
// /api/v1/learning/trend — derived view, no backing schema type
//
// See .agents/dev/CONTRIBUTING.md §OpenAPI codegen for the policy.
// Liveness — no auth, no audit (plan SG18). Not exposed via Caddy.
r.Get("/healthz", func(w http.ResponseWriter, req *http.Request) {
ctx, cancel := context.WithTimeout(req.Context(), 2*time.Second)
@@ -173,23 +192,27 @@ func NewHandler(ctx context.Context, pool *db.Pool, cfg config.Config) http.Hand
// 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.
// (See "Non-OpenAPI routes" carve-out block above.)
r.With(combinedAuth(cfg, false)).Get("/api/v1/knowledge/recent", s.serveRecentKnowledge)
// Custom (non-OpenAPI) route: full markdown content for a knowledge
// entity (document/investigation/runbook) by its own id or slug — the
// generated /api/v1/knowledge/{id} route (GetEntityKnowledge) answers a
// different question (knowledge referencing this entity), not this one.
// (See "Non-OpenAPI routes" carve-out block above.)
r.With(combinedAuth(cfg, false)).Get("/api/v1/knowledge/content/{id}", s.serveKnowledgeContent)
// Custom (non-OpenAPI) routes: the global activity feed (recency-ordered,
// unlike ListExecutions which sorts by target for pagination) and the
// per-session "what did this session do" digest.
// (See "Non-OpenAPI routes" carve-out block above.)
r.With(combinedAuth(cfg, false)).Get("/api/v1/activity/recent", s.serveRecentActivity)
r.With(combinedAuth(cfg, false)).Get("/api/v1/activity/session/{id}", s.serveSessionDigest)
// Learning view: capability timeline + success trend, both derived from
// executions (real, growing data) rather than the patterns/skills tables,
// which are correctly modeled but have no writers anywhere yet.
// (See "Non-OpenAPI routes" carve-out block above.)
r.With(combinedAuth(cfg, false)).Get("/api/v1/learning/timeline", s.serveLearningTimeline)
r.With(combinedAuth(cfg, false)).Get("/api/v1/learning/trend", s.serveLearningTrend)