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:
2026-03-26 05:10:40 -04:00
parent e4b7ee98a5
commit 7b6e54d5b7
73 changed files with 349 additions and 29963 deletions

View File

@@ -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

View File

@@ -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