Files
2026-08-24 13:06:29 +05:30

185 lines
5.4 KiB
Go

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
}