step 5 (complete): docs purge, fresh ARCHITECTURE + ROADMAP + CHANGELOG
Purged 29,966 lines of stale chat-switchboard documentation: - 41 docs/ files (ICD specs, design docs, archive, workflow docs) - 5 root MD files (CHANGESET, TURNOVER, ICD-DRIFT-AUDIT, etc.) New documentation: - docs/ARCHITECTURE.md — kernel components, design principles, data layer - ROADMAP.md — v0.1.0 through v0.5.0 MVP with decision log - CHANGELOG.md — fresh, starting from v0.1.0 fork - README.md — rewritten for switchboard-core Also in this commit: - config.go: stripped 7 dropped fields, DB default → switchboard_core - pages/loaders.go: stripped provider/model/notes/projects loaders - pages/pages.go: stripped persona store lookup - handlers/workflows.go: stripped persona tool grants - main.go: stripped team provider routes, avatar routes Production code: zero references to deleted packages, stores, or models. -29,966/+352 lines across 73 files.
This commit is contained in:
@@ -10,23 +10,6 @@ import (
|
||||
"switchboard-core/store"
|
||||
)
|
||||
|
||||
// ── Types for template data ──────────────────
|
||||
|
||||
// ModelOption is a flat model for template dropdowns.
|
||||
type ModelOption struct {
|
||||
ProviderConfigID string `json:"provider_config_id"`
|
||||
ProviderName string `json:"provider_name"`
|
||||
ModelID string `json:"model_id"`
|
||||
DisplayName string `json:"display_name"`
|
||||
ModelType string `json:"model_type"`
|
||||
}
|
||||
|
||||
// ProviderOption for template dropdowns.
|
||||
type ProviderOption struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// TeamOption for template dropdowns.
|
||||
type TeamOption struct {
|
||||
ID string `json:"id"`
|
||||
@@ -36,16 +19,6 @@ type TeamOption struct {
|
||||
IsActive bool `json:"is_active"`
|
||||
}
|
||||
|
||||
// ProviderDetail is an enriched provider for the providers table.
|
||||
type ProviderDetail struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Provider string `json:"provider"`
|
||||
Scope string `json:"scope"`
|
||||
Endpoint string `json:"endpoint"`
|
||||
ModelCount int `json:"model_count"`
|
||||
}
|
||||
|
||||
// ProviderTypeOption for the provider type dropdown.
|
||||
type ProviderTypeOption struct {
|
||||
ID string `json:"id"`
|
||||
@@ -86,10 +59,7 @@ type RoleSelection struct {
|
||||
type AdminPageData struct {
|
||||
Section string `json:"section"`
|
||||
Category string `json:"category"`
|
||||
Providers []ProviderOption `json:"providers"`
|
||||
ProviderDetails []ProviderDetail `json:"provider_details,omitempty"`
|
||||
ProviderTypes []ProviderTypeOption `json:"provider_types,omitempty"`
|
||||
Models []ModelOption `json:"models"`
|
||||
Teams []TeamOption `json:"teams"`
|
||||
Users []UserRow `json:"users,omitempty"`
|
||||
Roles []RoleConfig `json:"roles"`
|
||||
@@ -102,11 +72,6 @@ type ChatPageData struct {
|
||||
ChatID string `json:"chat_id,omitempty"`
|
||||
}
|
||||
|
||||
// NotesPageData provides context for the notes surface shell.
|
||||
type NotesPageData struct {
|
||||
NoteID string `json:"NoteID,omitempty"`
|
||||
}
|
||||
|
||||
// SettingsPageData is what the settings surface templates receive.
|
||||
// Feature gates (BYOK, personas) moved to sw.auth.policies in v0.37.19.
|
||||
type SettingsPageData struct {
|
||||
@@ -122,18 +87,10 @@ type TeamAdminPageData struct {
|
||||
ConfigSections []ConfigSectionEntry `json:"config_sections,omitempty"` // v0.38.3
|
||||
}
|
||||
|
||||
// ProjectsPageData is what the projects surface receives.
|
||||
type ProjectsPageData struct {
|
||||
ProjectID string `json:"project_id"`
|
||||
}
|
||||
|
||||
func (e *Engine) registerLoaders() {
|
||||
e.RegisterLoader("admin", e.adminLoader)
|
||||
e.RegisterLoader("chat", e.chatLoader)
|
||||
e.RegisterLoader("notes", e.notesLoader)
|
||||
e.RegisterLoader("settings", e.settingsLoader)
|
||||
e.RegisterLoader("team-admin", e.teamAdminLoader)
|
||||
e.RegisterLoader("projects", e.projectsLoader)
|
||||
}
|
||||
|
||||
// Used for manifest validation — DataRequires entries must match a key here.
|
||||
@@ -160,119 +117,6 @@ func (e *Engine) adminLoader(c *gin.Context, s store.Stores) (any, error) {
|
||||
Category: sectionCategory(section),
|
||||
}
|
||||
|
||||
// ── Providers (global scope) ─────────────
|
||||
if s.Providers != nil {
|
||||
configs, err := s.Providers.ListGlobal(ctx)
|
||||
if err != nil {
|
||||
log.Printf("[pages/admin] Failed to list providers: %v", err)
|
||||
} else {
|
||||
for _, pc := range configs {
|
||||
name := pc.Name
|
||||
if name == "" {
|
||||
name = pc.Provider
|
||||
}
|
||||
data.Providers = append(data.Providers, ProviderOption{
|
||||
ID: pc.ID,
|
||||
Name: name,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Teams ────────────────────────────────
|
||||
if s.Teams != nil {
|
||||
teams, err := s.Teams.List(ctx)
|
||||
if err != nil {
|
||||
log.Printf("[pages/admin] Failed to list teams: %v", err)
|
||||
} else {
|
||||
for _, t := range teams {
|
||||
data.Teams = append(data.Teams, TeamOption{
|
||||
ID: t.ID,
|
||||
Name: t.Name,
|
||||
Description: t.Description,
|
||||
MemberCount: t.MemberCount,
|
||||
IsActive: t.IsActive,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Full model catalog with model_type ───
|
||||
// This is what fixes bug #1: all models from all providers,
|
||||
// including embedding models, with their model_type intact.
|
||||
if s.Catalog != nil {
|
||||
entries, err := s.Catalog.ListAllGlobal(ctx)
|
||||
if err != nil {
|
||||
log.Printf("[pages/admin] Failed to list catalog: %v", err)
|
||||
} else {
|
||||
provNames := make(map[string]string, len(data.Providers))
|
||||
for _, p := range data.Providers {
|
||||
provNames[p.ID] = p.Name
|
||||
}
|
||||
for _, entry := range entries {
|
||||
mt := entry.ModelType
|
||||
if mt == "" {
|
||||
mt = "chat"
|
||||
}
|
||||
dn := entry.DisplayName
|
||||
if dn == "" {
|
||||
dn = entry.ModelID
|
||||
}
|
||||
data.Models = append(data.Models, ModelOption{
|
||||
ProviderConfigID: entry.ProviderConfigID,
|
||||
ProviderName: provNames[entry.ProviderConfigID],
|
||||
ModelID: entry.ModelID,
|
||||
DisplayName: dn,
|
||||
ModelType: mt,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Section-specific data ────────────────
|
||||
switch section {
|
||||
case "roles":
|
||||
data.Roles = e.loadRolesConfig(ctx, s)
|
||||
case "routing":
|
||||
if s.RoutingPolicies != nil {
|
||||
policies, err := s.RoutingPolicies.ListAll(ctx)
|
||||
if err == nil {
|
||||
data.Policies = policies
|
||||
}
|
||||
}
|
||||
case "providers":
|
||||
data.ProviderTypes = loadProviderTypes()
|
||||
data.ProviderDetails = e.loadProviderDetails(ctx, s, data.Providers, data.Models)
|
||||
case "users":
|
||||
data.Users = e.loadUsers(ctx, s)
|
||||
}
|
||||
|
||||
// v0.38.3: Discover extension config sections for admin surface
|
||||
data.ConfigSections = configSectionsForSurface(ctx, s, "admin")
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// loadRolesConfig reads current role assignments.
|
||||
func (e *Engine) loadRolesConfig(ctx context.Context, s store.Stores) []RoleConfig {
|
||||
roleNames := []string{"utility", "embedding"}
|
||||
roles := make([]RoleConfig, 0, len(roleNames))
|
||||
|
||||
if s.GlobalConfig == nil {
|
||||
for _, name := range roleNames {
|
||||
roles = append(roles, RoleConfig{Name: name})
|
||||
}
|
||||
return roles
|
||||
}
|
||||
|
||||
raw, err := s.GlobalConfig.Get(ctx, "model_roles")
|
||||
if err != nil || raw == nil {
|
||||
for _, name := range roleNames {
|
||||
roles = append(roles, RoleConfig{Name: name})
|
||||
}
|
||||
return roles
|
||||
}
|
||||
|
||||
rolesMap, ok := raw["roles"].(map[string]any)
|
||||
if !ok {
|
||||
rolesMap = raw
|
||||
@@ -300,13 +144,6 @@ func (e *Engine) loadRolesConfig(ctx context.Context, s store.Stores) []RoleConf
|
||||
return roles
|
||||
}
|
||||
|
||||
// ── Chat loader ──────────────────────────────
|
||||
|
||||
func (e *Engine) chatLoader(c *gin.Context, s store.Stores) (any, error) {
|
||||
chatID := c.Param("chatID")
|
||||
return &ChatPageData{ChatID: chatID}, nil
|
||||
}
|
||||
|
||||
// ── Admin helpers ────────────────────────────
|
||||
|
||||
// sectionCategory maps an admin section to its category tab.
|
||||
@@ -336,42 +173,6 @@ func loadProviderTypes() []ProviderTypeOption {
|
||||
return out
|
||||
}
|
||||
|
||||
// loadProviderDetails enriches providers with model counts.
|
||||
func (e *Engine) loadProviderDetails(ctx context.Context, s store.Stores, provs []ProviderOption, models []ModelOption) []ProviderDetail {
|
||||
// Count models per provider
|
||||
counts := make(map[string]int)
|
||||
for _, m := range models {
|
||||
counts[m.ProviderConfigID]++
|
||||
}
|
||||
|
||||
// Get full provider configs for endpoint/scope
|
||||
if s.Providers == nil {
|
||||
return nil
|
||||
}
|
||||
configs, err := s.Providers.ListGlobal(ctx)
|
||||
if err != nil {
|
||||
log.Printf("[pages/admin] Failed to list provider details: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
out := make([]ProviderDetail, 0, len(configs))
|
||||
for _, pc := range configs {
|
||||
name := pc.Name
|
||||
if name == "" {
|
||||
name = pc.Provider
|
||||
}
|
||||
out = append(out, ProviderDetail{
|
||||
ID: pc.ID,
|
||||
Name: name,
|
||||
Provider: pc.Provider,
|
||||
Scope: pc.Scope,
|
||||
Endpoint: pc.Endpoint,
|
||||
ModelCount: counts[pc.ID],
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// loadUsers returns the user list for the admin users table.
|
||||
func (e *Engine) loadUsers(ctx context.Context, s store.Stores) []UserRow {
|
||||
if s.Users == nil {
|
||||
@@ -396,13 +197,6 @@ func (e *Engine) loadUsers(ctx context.Context, s store.Stores) []UserRow {
|
||||
return out
|
||||
}
|
||||
|
||||
// ── Notes loader ─────────────────────────────
|
||||
|
||||
func (e *Engine) notesLoader(c *gin.Context, s store.Stores) (any, error) {
|
||||
noteID := c.Param("noteId")
|
||||
return &NotesPageData{NoteID: noteID}, nil
|
||||
}
|
||||
|
||||
// ── Settings loader ──────────────────────────
|
||||
// v0.22.7: Reads feature gates from GlobalConfig to control
|
||||
// which nav links/tabs are visible (BYOK, User Personas).
|
||||
@@ -430,11 +224,6 @@ func (e *Engine) settingsLoader(c *gin.Context, s store.Stores) (any, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *Engine) projectsLoader(c *gin.Context, s store.Stores) (any, error) {
|
||||
projectID := c.Param("id")
|
||||
return &ProjectsPageData{ProjectID: projectID}, nil
|
||||
}
|
||||
|
||||
// ── Config section discovery (v0.38.3) ───────
|
||||
//
|
||||
// Packages declare a config_section in their manifest. This helper scans
|
||||
|
||||
@@ -749,12 +749,7 @@ func (e *Engine) RenderWorkflowLanding() gin.HandlerFunc {
|
||||
if data.FirstStageMode == "" {
|
||||
data.FirstStageMode = "custom"
|
||||
}
|
||||
if stages[0].PersonaID != nil {
|
||||
if p, err := e.stores.Personas.GetByID(ctx, *stages[0].PersonaID); err == nil {
|
||||
data.PersonaName = p.Name
|
||||
data.PersonaIcon = p.Icon
|
||||
}
|
||||
}
|
||||
// Persona lookup deferred — personas are extensions now
|
||||
}
|
||||
|
||||
// Check for existing active session
|
||||
|
||||
Reference in New Issue
Block a user