mirror of
https://github.com/hanzoai/personas.git
synced 2026-10-30 15:03:20 +00:00
team/: the thirty-two managed agents in six departments (Des, Dev, Fin, ... Gov). Seats, functions and ownership are the owner's. Each file carries its department, seat, designed OCEAN scores with a reason per domain, tools, greeting and a compiled prompt of at most 300 tokens that ends in who it hands off to. Nora, Maya, Leo, Qual, Feynman and Einstein leave the team (Feynman and Einstein stay in the roster as personas anyone can build on). Member gains group, seat, owns, tools, greeting and `grouped()`; the Go Member gains Group. people/: Antje and Zach, with a consent record that gates their agents. src/: the persona schema, `compile()`, the IPIP 50-item OCEAN test (public domain), in TypeScript. tools/: personas.ts writes the team files from a local model (or --spec); memoji.ts draws the thirty-two faces from one parts set (a ground per department, a badge per function). Claude-Session: https://claude.ai/code/session_017vrVus6PPBgP5YRzKV1iqW
77 lines
2.3 KiB
Go
77 lines
2.3 KiB
Go
package personas
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// TestTeam checks what a consumer relies on: thirty-two members, each named for a
|
|
// person to read, keyed by the lower-case handle, on an Enso tier, with a
|
|
// system prompt that opens with the member's own name.
|
|
func TestTeam(t *testing.T) {
|
|
team, err := Team()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var ids []string
|
|
for _, m := range team {
|
|
ids = append(ids, m.ID)
|
|
}
|
|
if got, want := strings.Join(ids, ","), "art,audit,auto,com,comp,corp,cust,cyber,data,des,dev,doc,edu,fin,gov,huma,it,lang,law,mana,mark,mu,opera,proc,qa,rev,risk,sal,sci,su,supply,vi"; got != want {
|
|
t.Fatalf("team = %s, want %s", got, want)
|
|
}
|
|
word := regexp.MustCompile(`^[A-Z][A-Za-z]+$`)
|
|
enso := regexp.MustCompile(`^enso(-[a-z]+)?$`)
|
|
roles := map[string]string{}
|
|
for _, m := range team {
|
|
if !word.MatchString(m.Name) || strings.ToLower(m.Name) != m.ID {
|
|
t.Errorf("%s: name %q is not the capitalized handle", m.ID, m.Name)
|
|
}
|
|
if !enso.MatchString(m.Model) {
|
|
t.Errorf("%s: model %q is not an Enso tier", m.ID, m.Model)
|
|
}
|
|
if !strings.HasPrefix(m.Instructions, "You are "+m.Name+",") {
|
|
t.Errorf("%s: instructions open %q", m.ID, m.Instructions[:min(40, len(m.Instructions))])
|
|
}
|
|
if other, dup := roles[m.Role]; dup {
|
|
t.Errorf("%s and %s share the role %q", other, m.ID, m.Role)
|
|
}
|
|
roles[m.Role] = m.ID
|
|
}
|
|
}
|
|
|
|
// TestParseMatchesTheFile holds Parse to the file: the body after the front
|
|
// matter is the instructions, whole.
|
|
func TestParseMatchesTheFile(t *testing.T) {
|
|
raw, err := os.ReadFile(filepath.Join("team", "dev.md"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
m, err := Parse("dev", string(raw))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, body, _ := strings.Cut(strings.TrimPrefix(string(raw), "---\n"), "\n---\n")
|
|
if m.Instructions != strings.TrimSpace(body) {
|
|
t.Fatal("instructions differ from the file's body")
|
|
}
|
|
if m.Name != "Dev" || m.Role != "Development" || m.Model != "enso" {
|
|
t.Fatalf("dev = %+v", m)
|
|
}
|
|
}
|
|
|
|
func TestParseRefuses(t *testing.T) {
|
|
for name, text := range map[string]string{
|
|
"no front matter": "You are nobody.",
|
|
"unclosed": "---\nname: X\n",
|
|
"no model": "---\nname: X\nrole: Y\n---\nYou are X,",
|
|
} {
|
|
if _, err := Parse("x", text); err == nil {
|
|
t.Errorf("%s: Parse accepted it", name)
|
|
}
|
|
}
|
|
}
|