updates on the ai agents and time series prediction and updates on the api to

This commit is contained in:
2026-10-09 13:50:30 +05:30
parent 29690c56f2
commit 153be40e5c
42 changed files with 4889 additions and 186 deletions

View File

@@ -36,6 +36,14 @@ var aiReads = []string{
"/api/v1/admin/ai/insights?days=30",
"/api/v1/admin/ai/decisions",
"/api/v1/admin/ai/status",
// Added with the agent-platform work. Listed here so the existing
// invariants cover them: every /admin/ai read must require a login and
// must refuse miler, hub-staff and customer roles. A new route that skips
// this table is a new route nobody proved is gated.
"/api/v1/admin/ai/findings",
"/api/v1/admin/ai/findings/stats",
"/api/v1/admin/ai/forecast/demand",
"/api/v1/admin/ai/engine/agents",
}
var aiWrites = []struct{ method, path, body string }{
@@ -45,6 +53,46 @@ var aiWrites = []struct{ method, path, body string }{
{http.MethodPost, "/api/v1/admin/ai/playground/run", `{"agentid":"EXCEPTION_AGENT","prompt":"hi"}`},
}
// Finding reports are NOT registry mutations, and deliberately not role-1-only.
//
// The aiWrites table above encodes "changing the registry is an operator
// decision, so role 1 only". Reporting what the rule skills noticed is a
// different thing: it is telemetry from the Exceptions page, which managers (3)
// and executives (4) open as part of their job. Gating it to role 1 would mean
// a manager's session silently reported nothing, and the "how long has this
// finding been open" measurement would depend on who happened to be logged in.
//
// What it must still refuse is everyone outside the console: miler, hub staff
// and customer tokens.
var aiFindingWrites = []struct{ method, path, body string }{
{http.MethodPost, "/api/v1/admin/ai/findings", `{"findings":[],"cleared":[]}`},
{http.MethodPost, "/api/v1/admin/ai/findings/abc/acted", `{"result":"ok"}`},
}
func TestFindingReportsAreOpenToConsoleRolesButNotOutsiders(t *testing.T) {
app := newApp()
for _, w := range aiFindingWrites {
// No token at all is refused.
if code, _ := do(t, app, w.method, w.path, "", w.body); code != http.StatusUnauthorized {
t.Errorf("%s %s with no token = %d, want 401", w.method, w.path, code)
}
// Outside the console: refused.
for _, role := range []int{5, 6, 9} { // miler, hub staff, customer
if code, _ := do(t, app, w.method, w.path, token(t, 1, role), w.body); code != http.StatusForbidden && code != http.StatusUnauthorized {
t.Errorf("%s %s as role %d = %d, want 401/403", w.method, w.path, role, code)
}
}
// Console roles: NOT forbidden. The handler may still fail without a
// database in this app; what matters here is that authorisation let it
// through rather than stopping it.
for _, role := range []int{1, 3, 4} {
if code, _ := do(t, app, w.method, w.path, token(t, 1, role), w.body); code == http.StatusForbidden {
t.Errorf("%s %s as role %d = 403; the Exceptions page must be able to report findings", w.method, w.path, role)
}
}
}
}
func TestAIRegistryRequiresALogin(t *testing.T) {
app := newApp()
for _, p := range aiReads {