first commit
This commit is contained in:
184
go-api/internal/definition/vocabulary.go
Normal file
184
go-api/internal/definition/vocabulary.go
Normal file
@@ -0,0 +1,184 @@
|
||||
package definition
|
||||
|
||||
import "strings"
|
||||
|
||||
// The closed vocabulary a definition is allowed to name.
|
||||
//
|
||||
// Every table here is a transcription of a frontend table, and the conformance
|
||||
// suite asserts each one against the vocabulary the JavaScript actually
|
||||
// exports (testdata/oracle.json, `vocabulary`) — so a page added to
|
||||
// surfaces.js or an icon added to vocabulary.js fails a test here rather than
|
||||
// silently making the two ends disagree about what is valid.
|
||||
//
|
||||
// Nothing is looked up dynamically and nothing is constructed: a definition
|
||||
// names a key and this file answers whether that key exists.
|
||||
|
||||
// pageSurfaces mirrors SKILL_SURFACES in src/lib/skills/surfaces.js — id first,
|
||||
// then the alternative spellings an author may use for it.
|
||||
var pageSurfaces = []struct {
|
||||
ID string
|
||||
Aliases []string
|
||||
}{
|
||||
{ID: "control-center"},
|
||||
{ID: "positions"},
|
||||
{ID: "create-position", Aliases: []string{"new-position"}},
|
||||
{ID: "candidates"},
|
||||
{ID: "hired-history", Aliases: []string{"hired"}},
|
||||
{ID: "talent-pool"},
|
||||
{ID: "krow-forge", Aliases: []string{"university", "forge"}},
|
||||
{ID: "analytics"},
|
||||
{ID: "activity"},
|
||||
{ID: "workspace-agent-configure"},
|
||||
{ID: "settings"},
|
||||
{ID: "workspace"},
|
||||
{ID: "workspace-agents"},
|
||||
{ID: "workspace-skills"},
|
||||
{ID: "workspace-skill-configure"},
|
||||
{ID: "skill-development"},
|
||||
{ID: "profile"},
|
||||
{ID: "candidates-analysis"},
|
||||
}
|
||||
|
||||
// surfaceByKey resolves every spelling — canonical or alias — to its canonical
|
||||
// id.
|
||||
var surfaceByKey = func() map[string]string {
|
||||
m := map[string]string{}
|
||||
for _, s := range pageSurfaces {
|
||||
m[s.ID] = s.ID
|
||||
for _, a := range s.Aliases {
|
||||
m[a] = s.ID
|
||||
}
|
||||
}
|
||||
return m
|
||||
}()
|
||||
|
||||
// SupportedPages is every canonical page id, in declaration order. The order is
|
||||
// the order the frontend lists them in when it refuses an unsupported page, and
|
||||
// that message is compared byte for byte.
|
||||
var SupportedPages = func() []string {
|
||||
out := make([]string, len(pageSurfaces))
|
||||
for i, s := range pageSurfaces {
|
||||
out[i] = s.ID
|
||||
}
|
||||
return out
|
||||
}()
|
||||
|
||||
// normalizeKey is surfaces.js's own: trimmed, lower-cased, with spaces and
|
||||
// underscores read as dashes. `Talent Pool` and `talent_pool` both reach
|
||||
// `talent-pool`; the canonical keys never widen, only what an author may type
|
||||
// to reach them.
|
||||
func normalizeKey(page any) string {
|
||||
s := strings.ToLower(jsTrim(jsString(page)))
|
||||
if page == nil {
|
||||
s = ""
|
||||
}
|
||||
return strings.Map(func(r rune) rune {
|
||||
if r == ' ' || r == '_' || jsIsSpace(r) {
|
||||
return '-'
|
||||
}
|
||||
return r
|
||||
}, s)
|
||||
}
|
||||
|
||||
// SurfaceExists reports whether a declared page name refers to a real surface.
|
||||
func SurfaceExists(page any) bool {
|
||||
_, ok := surfaceByKey[normalizeKey(page)]
|
||||
return ok
|
||||
}
|
||||
|
||||
// CanonicalPage is the canonical id a declared page name refers to, or "".
|
||||
func CanonicalPage(page any) string { return surfaceByKey[normalizeKey(page)] }
|
||||
|
||||
/* ── Agent vocabulary — src/lib/agents/vocabulary.js ─────────────────────── */
|
||||
|
||||
var (
|
||||
AgentStatuses = []string{"draft", "published", "archived"}
|
||||
ReasoningModes = []string{"fast", "balanced", "deep"}
|
||||
KnowledgeKinds = []string{"note", "link", "skill-reference"}
|
||||
AgentAccess = []string{"all", "specific"}
|
||||
PermissionRole = []string{"manager", "editor", "viewer"}
|
||||
AgentIcons = []string{
|
||||
"owliver", "sparkles", "briefcase", "users", "user-check",
|
||||
"layers", "graduation-cap", "bar-chart", "activity", "shield",
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultAgentStatus = "draft"
|
||||
DefaultReasoning = "balanced"
|
||||
DefaultAgentIcon = "owliver"
|
||||
DefaultKnowledgeKind = "note"
|
||||
DefaultAgentAccess = "all"
|
||||
DefaultPermission = "viewer"
|
||||
)
|
||||
|
||||
func contains(list []string, want string) bool {
|
||||
for _, v := range list {
|
||||
if v == want {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/* ── Skill vocabulary ────────────────────────────────────────────────────── */
|
||||
|
||||
// SkillStatuses is the whole of a skill's lifecycle. There is no version and no
|
||||
// publish step: migration 000005 states the same two values.
|
||||
var SkillStatuses = []string{"active", "inactive"}
|
||||
|
||||
// levelHeadings are the rungs a workforce skill may define, and the reason a
|
||||
// definition is read as workforce rather than assistant. Order is the ladder's.
|
||||
var levelHeadings = []string{"Beginner", "Intermediate", "Advanced", "Expert"}
|
||||
|
||||
// slugify is uiConfig.js's, used to derive an id from a name.
|
||||
//
|
||||
// String(value || '').toLowerCase().trim()
|
||||
// .replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')
|
||||
//
|
||||
// The lower-casing happens BEFORE the class replacement, so an upper-case
|
||||
// letter becomes itself rather than a dash.
|
||||
func slugify(value any) string {
|
||||
if !jsTruthy(value) {
|
||||
return ""
|
||||
}
|
||||
s := jsTrim(strings.ToLower(jsString(value)))
|
||||
|
||||
var b strings.Builder
|
||||
dash := false
|
||||
for _, r := range s {
|
||||
if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') {
|
||||
b.WriteRune(r)
|
||||
dash = false
|
||||
continue
|
||||
}
|
||||
if !dash {
|
||||
b.WriteByte('-')
|
||||
dash = true
|
||||
}
|
||||
}
|
||||
out := b.String()
|
||||
out = strings.TrimPrefix(out, "-")
|
||||
out = strings.TrimSuffix(out, "-")
|
||||
return out
|
||||
}
|
||||
|
||||
// isDefinitionID is the id format the frontend validator enforces and the
|
||||
// definition_id CHECK in migration 000005 restates.
|
||||
func isDefinitionID(id string) bool {
|
||||
if id == "" {
|
||||
return false
|
||||
}
|
||||
for i, r := range id {
|
||||
lower := r >= 'a' && r <= 'z'
|
||||
digit := r >= '0' && r <= '9'
|
||||
if lower || digit {
|
||||
continue
|
||||
}
|
||||
if r == '-' && i > 0 {
|
||||
continue
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user