chore(routing): pre-merge cleanup — Plan 7 reminders, code_review→review, operator note
All checks were successful
CI / Lint / Test / Vet (push) Successful in 11s
CI / Mirror to GitHub (push) Successful in 4s

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Mathias Bergqvist
2026-05-05 23:22:15 +02:00
parent d72454d929
commit bee4bb3c1f
6 changed files with 41 additions and 7 deletions

View File

@@ -67,6 +67,31 @@ Record *why* things are the way they are. Future-you will thank present-you.
--- ---
## Plan 6: routing pod reuses internal/skills/{review,debug,retrospective,trainer}
Plan 6 (Mode 2 routing pod, 2026-05-04) introduces a second consumer of
the four cost-routable skill packages. The routing pod constructs each
skill via `<pkg>.New(Config{...})` and hands it `routing.Router.Run` as
the `CompleteFunc`. Plan 7 (supervisor retirement) MUST NOT delete the
four packages.
**Plan 7's allowed deletions:**
- `internal/skills/{tdd,spec,tier}/` (not consumed by the routing pod)
- `cmd/supervisor/` (binary)
- `Dockerfile` (supervisor's, at repo root — distinct from `Dockerfile.routing`)
- supervisor manifests in the infra repo
- NodePort `:30320`
**Plan 7's preserved code:**
- `internal/skills/{review,debug,retrospective,trainer}/`
- `internal/registry`
- `internal/mcp`
- `internal/exec/litellm.go`
- `internal/routing/` (entirely new in Plan 6)
- `cmd/routing/`
---
## 2026-04-08 — Mistral Vibe gets its own adapter ## 2026-04-08 — Mistral Vibe gets its own adapter
**Context**: Vibe doesn't read `AGENTS.md` — it uses `~/.vibe/prompts/` and `~/.vibe/agents/` with TOML config. **Context**: Vibe doesn't read `AGENTS.md` — it uses `~/.vibe/prompts/` and `~/.vibe/agents/` with TOML config.

View File

@@ -122,6 +122,8 @@ The supervisor probes connectivity at call time:
| `HYPERGUILD_ROUTE_LOCAL_CEIL` | `0.70` | Below pass rate, route to Claude. Between CEIL and FLOOR is the sample band. | | `HYPERGUILD_ROUTE_LOCAL_CEIL` | `0.70` | Below pass rate, route to Claude. Between CEIL and FLOOR is the sample band. |
| `HYPERGUILD_PASS_RATE_TTL_SECONDS` | `60` | Per-skill pass-rate cache TTL | | `HYPERGUILD_PASS_RATE_TTL_SECONDS` | `60` | Per-skill pass-rate cache TTL |
> **Operator note:** LiteLLM at `LITELLM_BASE_URL` must register both `HYPERGUILD_LOCAL_MODEL` and `HYPERGUILD_CLAUDE_MODEL` for routing to do useful work. If a model is missing, LiteLLM returns 4xx, the routing pod's local route fails, the fail-open retry on Claude likely also fails (since both are missing), and the only signal is `final_status: "fail"` on `_routing` entries in the brain.
## Phase 2 (planned) ## Phase 2 (planned)
- `review` skill — structured code review with iron law enforcement - `review` skill — structured code review with iron law enforcement

View File

@@ -1,5 +1,12 @@
package main package main
// The internal/skills/{debug,retrospective,review,trainer} packages imported
// below are also imported by cmd/supervisor. Plan 7 (supervisor retirement)
// MUST NOT delete these four packages — the routing pod is their second
// consumer. Plan 7 deletes only internal/skills/{tdd,spec,tier} (the skills
// that don't route to local), the supervisor binary, and supervisor manifests.
// See docs/superpowers/specs/2026-05-04-mode-2-routing-pod-design.md (Constraints).
import ( import (
"context" "context"
"log/slog" "log/slog"

View File

@@ -12,7 +12,7 @@ import (
// LogEntry describes a single routing decision to log via the brain MCP. // LogEntry describes a single routing decision to log via the brain MCP.
type LogEntry struct { type LogEntry struct {
SessionID string SessionID string
Skill string // the original skill the call routed (e.g., "code_review") Skill string // the original skill the call routed (e.g., "review")
Decision string // "local" or "claude" or "claude_fallback" Decision string // "local" or "claude" or "claude_fallback"
Message string // free-form, e.g. "model=qwen35, pass_rate=0.94" Message string // free-form, e.g. "model=qwen35, pass_rate=0.94"
ProjectRoot string ProjectRoot string

View File

@@ -27,7 +27,7 @@ func TestLoggerLogDecision(t *testing.T) {
l := routing.NewLogger(srv.URL) l := routing.NewLogger(srv.URL)
err := l.LogDecision(context.Background(), routing.LogEntry{ err := l.LogDecision(context.Background(), routing.LogEntry{
SessionID: "sess-1", SessionID: "sess-1",
Skill: "code_review", Skill: "review",
Decision: "local", Decision: "local",
Message: "model=qwen35, pass_rate=0.94", Message: "model=qwen35, pass_rate=0.94",
ProjectRoot: "/home/x/proj", ProjectRoot: "/home/x/proj",
@@ -44,7 +44,7 @@ func TestLoggerLogDecision(t *testing.T) {
assert.Equal(t, "_routing", args["skill"]) assert.Equal(t, "_routing", args["skill"])
assert.Equal(t, "decide", args["phase"]) assert.Equal(t, "decide", args["phase"])
assert.Equal(t, "skip", args["final_status"]) assert.Equal(t, "skip", args["final_status"])
assert.Contains(t, args["message"].(string), "code_review: local") assert.Contains(t, args["message"].(string), "review: local")
assert.Equal(t, "sess-1", args["session_id"]) assert.Equal(t, "sess-1", args["session_id"])
assert.Equal(t, "/home/x/proj", args["project_root"]) assert.Equal(t, "/home/x/proj", args["project_root"])
assert.Equal(t, float64(1234), args["duration_ms"]) assert.Equal(t, float64(1234), args["duration_ms"])

View File

@@ -64,7 +64,7 @@ func TestRouterRoutesLocalAtHighPassRate(t *testing.T) {
r, _, _ := newRouter(t, llm, 0.95) r, _, _ := newRouter(t, llm, 0.95)
out, _, err := r.Run(context.Background(), routing.RunInput{ out, _, err := r.Run(context.Background(), routing.RunInput{
Skill: "code_review", System: "sys", User: "user", SessionID: "s1", ProjectRoot: "/p", Skill: "review", System: "sys", User: "user", SessionID: "s1", ProjectRoot: "/p",
}) })
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, "ok", out) assert.Equal(t, "ok", out)
@@ -80,7 +80,7 @@ func TestRouterRoutesClaudeAtLowPassRate(t *testing.T) {
r, _, _ := newRouter(t, llm, 0.3) r, _, _ := newRouter(t, llm, 0.3)
_, _, err := r.Run(context.Background(), routing.RunInput{ _, _, err := r.Run(context.Background(), routing.RunInput{
Skill: "code_review", System: "sys", User: "user", SessionID: "s2", Skill: "review", System: "sys", User: "user", SessionID: "s2",
}) })
require.NoError(t, err) require.NoError(t, err)
@@ -95,7 +95,7 @@ func TestRouterFailsOpenLocalErrorToClaude(t *testing.T) {
r, _, _ := newRouter(t, llm, 0.95) // would route local r, _, _ := newRouter(t, llm, 0.95) // would route local
out, _, err := r.Run(context.Background(), routing.RunInput{ out, _, err := r.Run(context.Background(), routing.RunInput{
Skill: "code_review", System: "sys", User: "user", SessionID: "s3", Skill: "review", System: "sys", User: "user", SessionID: "s3",
}) })
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, "ok-after-fallback", out) assert.Equal(t, "ok-after-fallback", out)
@@ -125,7 +125,7 @@ func TestRouterDefaultsToLocalWhenBrainUnreachable(t *testing.T) {
} }
_, _, err := r.Run(context.Background(), routing.RunInput{ _, _, err := r.Run(context.Background(), routing.RunInput{
Skill: "code_review", System: "sys", User: "user", SessionID: "s4", Skill: "review", System: "sys", User: "user", SessionID: "s4",
}) })
require.NoError(t, err) require.NoError(t, err)