matrix: strong-model downgrade gate — SPEC §3.4 component 4 of 5
Pure-Go port of mode.rs::execute's pass5 downgrade gate (Rust
2026-04-26). Adds POST /v1/matrix/downgrade endpoint via matrixd.
The gate captures the pass5 finding: composing matrix corpora into
codereview_lakehouse on a strong model LOST 5/5 head-to-head reps
against matrix-free codereview_isolation on grok-4.1-fast (p=0.031).
Strong models have enough native capacity that bug fingerprints +
adversarial framing + file content carry them; matrix chunks
displace depth-of-analysis.
Logic (matches Rust mode.rs:614-632):
if mode == codereview_lakehouse
&& !forced_mode
&& !LH_FORCE_FULL_ENRICHMENT
&& !is_weak_model(model)
→ flip to codereview_isolation, record downgraded_from
is_weak_model captures the empirical weak-list:
- `:free` suffix or `:free/` infix (OpenRouter free tier)
- qwen3.5:latest, qwen3:latest (local last-resort rungs)
- everything else → strong by default
Tests:
- 3 unit tests in internal/matrix/downgrade_test.go: IsWeakModel
coverage, MaybeDowngrade truth table (5 rows), forced-mode
precedence (forced beats every other bypass)
- scripts/downgrade_smoke.sh: 6 assertions through gateway covering
all 5 truth-table rows + empty-mode 400
14-smoke regression sweep all green (D1-D6, G1, G1P, G2,
storaged_cap, pathway, matrix, relevance, downgrade).
SPEC §3.4 progress: 4 of 5 components shipped (corpus builders,
multi-corpus retrieve+merge, relevance filter, downgrade gate).
Last component is learning-loop integration.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,8 @@
|
||||
// GET /matrix/corpora — list known vectord indexes (proxy)
|
||||
// POST /matrix/relevance — adjacency-pollution filter (CODE-aware;
|
||||
// port of mcp-server/relevance.ts)
|
||||
// POST /matrix/downgrade — strong-model auto-downgrade decision
|
||||
// (port of mode.rs::execute pass5 gate)
|
||||
//
|
||||
// matrixd talks to embedd (for query-text embedding) and vectord
|
||||
// (for per-corpus search) via HTTP. Both URLs come from
|
||||
@@ -61,6 +63,7 @@ func (h *handlers) register(r chi.Router) {
|
||||
r.Post("/matrix/search", h.handleSearch)
|
||||
r.Get("/matrix/corpora", h.handleCorpora)
|
||||
r.Post("/matrix/relevance", h.handleRelevance)
|
||||
r.Post("/matrix/downgrade", h.handleDowngrade)
|
||||
}
|
||||
|
||||
func (h *handlers) handleSearch(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -101,6 +104,35 @@ func (h *handlers) handleRelevance(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusOK, res)
|
||||
}
|
||||
|
||||
// downgradeRequest is the POST /matrix/downgrade body. Mirrors
|
||||
// matrix.DowngradeInput; ForceFullOverride is read from the
|
||||
// LH_FORCE_FULL_ENRICHMENT env var when omitted from the body.
|
||||
type downgradeRequest struct {
|
||||
Mode string `json:"mode"`
|
||||
Model string `json:"model"`
|
||||
ForcedMode bool `json:"forced_mode,omitempty"`
|
||||
ForceFullOverride *bool `json:"force_full_override,omitempty"`
|
||||
}
|
||||
|
||||
func (h *handlers) handleDowngrade(w http.ResponseWriter, r *http.Request) {
|
||||
var req downgradeRequest
|
||||
if !decodeJSON(w, r, &req) {
|
||||
return
|
||||
}
|
||||
if req.Mode == "" || req.Model == "" {
|
||||
http.Error(w, "mode and model are required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
in := matrix.NewDowngradeInputFromEnv(req.Mode, req.Model, req.ForcedMode)
|
||||
if req.ForceFullOverride != nil {
|
||||
// Explicit body override beats env, useful for tooling that
|
||||
// wants to ask "what would the gate do under these conditions"
|
||||
// without env pollution.
|
||||
in.ForceFullOverride = *req.ForceFullOverride
|
||||
}
|
||||
writeJSON(w, http.StatusOK, matrix.MaybeDowngrade(in))
|
||||
}
|
||||
|
||||
func (h *handlers) handleCorpora(w http.ResponseWriter, r *http.Request) {
|
||||
names, err := h.r.Corpora(r.Context())
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user