stacklok / stacklok/mecatl

Add secret-free effective-runtime-config/status endpoint (router, slots, guardrails, warnings)

Open
#398 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
152
Forks
16
Avg merge
14h 48m
Merged PRs (30d)
536

Description

Split out from #391 (claim 4).

Claim

A client can know the YAML it supplied, but cannot query what Mecatl actually accepted and resolved. A read-only endpoint should expose at least: whether semantic routing is active; category names and resolved model IDs; classifier slot/model; active model slots; guardrails enabled state and rule summary; warnings/dropped configuration — so clients can display runtime truth rather than configured intent.

Investigation (verified against the codebase)

Verdict: confirmed. The full client surface was enumerated; no endpoint exposes runtime-resolved config state.

Evidence
  • HTTP: 44 routes (internal/adapter/server/http.go:43-86) + /healthz//readyz (health.go:32-48, pure liveness) — session CRUD/run/approve/plan/cancel/fork, MCP discovery (sources = configured names, not resolved state), content discovery, GET /v1/models (the only status-ish endpoint; models-only), teams, schedules. gRPC: HarnessService 30 rpcs (mirrors HTTP) + ScheduleService 10 rpcs. None expose router active state, category→model resolution, classifier slot/model, model-slot bindings, guardrails state/rule summary, or config warnings. Grep for effective|/v1/status|runtime.?status across internal/ = zero hits.
  • The truth exists but is diag-log-only at Build: logModelRouterFacts (internal/app/slots.go:697-716), logGuardrailsPosture/guardrailsPostureLine (internal/app/build.go:3365-3422), narratePosture (internal/app/posture.go:262-271), logSlotConfigFacts; permconfig drop warnings (internal/adapter/permconfig/resolve.go:433-669, ~14 WARN sites) are emitted-and-forgotten. The resolution happens (foldOperatorModelRouter build.go:1234, foldOperatorModelSlots build.go:1145, foldOperatorGuardrails guardrails.go:21) and lives in the process-local app.Config, reachable only via the daemon log.
Nuance (minor understatement in the claim)

GET /v1/models + ListModelsResponse.provider_status already exposes live provider reachability/model counts, and CreateSessionResponse.resolved_model echoes the effective per-session provider/model/window/effort — but neither covers router/guardrails/slots/warnings, so the verdict stands.

Proposed fix (~1-2 days)

Read-only GET /v1/status in NewHTTPHandler + a Service method projecting a snapshot app.Build computes once and hands to server.NewService via a Config field (the SetProviderStatus precedent, service.go:1003). Contents all derivable from the post-fold Config:

  • posture;
  • router {active, disabled, categories + resolved selectors, default_category, classifier model/slot};
  • slots (post-fold, alias→concrete via the same resolveSlotModel);
  • guardrails {enabled, checker + provenance, mode, rule_count, used_defaults, rule summaries};
  • warnings.

The warnings collector (a Diagnostics decorator or permconfig recording dropped-block reasons) is the only genuinely new machinery.

Constraints

  • HTTP-only needs no proto change (a gRPC mirror would need task generate + the same-PR docs/user-docs rule).
  • Not api-compat-gated (composition layer, outside engine/api/*.txt).
  • Mount behind the existing authn middleware — never on the unauthenticated health mux (health.go:15-16).
  • Secret-free discipline is binding — never project MCP ServerConfig.Headers/tokens (AGENTS.md: AgentMCPServer.Headers is secret-shaped, "never logged/projected"), provider keys, or --auth-token; guardrail rule prompts summary-only.
  • The warnings ring buffer is a new process-lifetime structure → ADR 0027 List 1 row (the static snapshot itself adds none).
  • The endpoint is readable by any authenticated client — if multi-tenancy ever lands, consider admin-scoping.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with internal/adapter/server/http.go:43-86, internal/app/build.go, internal/app/slots.go, and service.go:1003; trace how the post-fold app.Config reaches NewHTTPHandler and the SetProviderStatus precedent. Done means an authenticated GET /v1/status reports resolved router, slots, guardrails, posture, and warnings without exposing secrets, while preserving the unauthenticated health mux.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
api, backend, security
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.