feat(lib): migrate to Core Embed system with Result returns

- All public functions return core.Result instead of (string, error)
- Mount scopes basedir — no path prefix needed in ReadString calls
- Add Bundle struct replacing (string, map, error) anti-pattern
- listDir takes *core.Embed not embed.FS
- ListTasks/ListPersonas use FS() + BaseDirectory() for WalkDir
- Remove bytes, os, text/template imports
- 22 tests: Prompt, Task, TaskBundle, Flow, Persona, Template,
  List functions, ExtractWorkspace (Good/Bad patterns)

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Snider 2026-03-22 09:08:35 +00:00
parent 3c5e6d6498
commit ede5d6f561
3 changed files with 300 additions and 119 deletions

View file

@ -14,104 +14,134 @@
//
// Usage:
//
// prompt, _ := lib.Prompt("coding")
// task, _ := lib.Task("code/review")
// persona, _ := lib.Persona("secops/developer")
// flow, _ := lib.Flow("go")
// r := lib.Prompt("coding") // r.Value.(string)
// r := lib.Task("code/review") // r.Value.(string)
// r := lib.Persona("secops/dev") // r.Value.(string)
// r := lib.Flow("go") // r.Value.(string)
// lib.ExtractWorkspace("default", "/tmp/ws", data)
package lib
import (
"bytes"
"embed"
"io/fs"
"os"
"path/filepath"
"text/template"
core "dappco.re/go/core"
)
//go:embed prompt/*.md
var promptFS embed.FS
//go:embed all:prompt
var promptFiles embed.FS
//go:embed all:task
var taskFS embed.FS
var taskFiles embed.FS
//go:embed flow/*.md
var flowFS embed.FS
//go:embed all:flow
var flowFiles embed.FS
//go:embed persona
var personaFS embed.FS
//go:embed all:persona
var personaFiles embed.FS
//go:embed all:workspace
var workspaceFS embed.FS
var workspaceFiles embed.FS
var (
promptFS = mustMount(promptFiles, "prompt")
taskFS = mustMount(taskFiles, "task")
flowFS = mustMount(flowFiles, "flow")
personaFS = mustMount(personaFiles, "persona")
workspaceFS = mustMount(workspaceFiles, "workspace")
)
func mustMount(fsys embed.FS, basedir string) *core.Embed {
r := core.Mount(fsys, basedir)
if !r.OK {
panic(r.Value)
}
return r.Value.(*core.Embed)
}
// --- Prompts ---
// Template tries Prompt then Task (backwards compat).
func Template(slug string) (string, error) {
if content, err := Prompt(slug); err == nil {
return content, nil
//
// r := lib.Template("coding")
// if r.OK { content := r.Value.(string) }
func Template(slug string) core.Result {
if r := Prompt(slug); r.OK {
return r
}
return Task(slug)
}
func Prompt(slug string) (string, error) {
data, err := promptFS.ReadFile("prompt/" + slug + ".md")
if err != nil {
return "", err
}
return string(data), nil
// Prompt reads a system prompt by slug.
//
// r := lib.Prompt("coding")
// if r.OK { content := r.Value.(string) }
func Prompt(slug string) core.Result {
return promptFS.ReadString(slug + ".md")
}
func Task(slug string) (string, error) {
// Task reads a structured task plan by slug. Tries .md, .yaml, .yml.
//
// r := lib.Task("code/review")
// if r.OK { content := r.Value.(string) }
func Task(slug string) core.Result {
for _, ext := range []string{".md", ".yaml", ".yml"} {
data, err := taskFS.ReadFile("task/" + slug + ext)
if err == nil {
return string(data), nil
if r := taskFS.ReadString(slug + ext); r.OK {
return r
}
}
return "", fs.ErrNotExist
return core.Result{Value: fs.ErrNotExist}
}
func TaskBundle(slug string) (string, map[string]string, error) {
main, err := Task(slug)
if err != nil {
return "", nil, err
// Bundle holds a task's main content plus companion files.
//
// r := lib.TaskBundle("code/review")
// if r.OK { b := r.Value.(lib.Bundle) }
type Bundle struct {
Main string
Files map[string]string
}
// TaskBundle reads a task and its companion files.
//
// r := lib.TaskBundle("code/review")
// if r.OK { b := r.Value.(lib.Bundle) }
func TaskBundle(slug string) core.Result {
main := Task(slug)
if !main.OK {
return main
}
bundleDir := "task/" + slug
entries, err := fs.ReadDir(taskFS, bundleDir)
if err != nil {
return main, nil, nil
b := Bundle{Main: main.Value.(string), Files: make(map[string]string)}
r := taskFS.ReadDir(slug)
if !r.OK {
return core.Result{Value: b, OK: true}
}
bundle := make(map[string]string)
for _, e := range entries {
for _, e := range r.Value.([]fs.DirEntry) {
if e.IsDir() {
continue
}
data, err := taskFS.ReadFile(bundleDir + "/" + e.Name())
if err == nil {
bundle[e.Name()] = string(data)
if fr := taskFS.ReadString(slug + "/" + e.Name()); fr.OK {
b.Files[e.Name()] = fr.Value.(string)
}
}
return main, bundle, nil
return core.Result{Value: b, OK: true}
}
func Flow(slug string) (string, error) {
data, err := flowFS.ReadFile("flow/" + slug + ".md")
if err != nil {
return "", err
}
return string(data), nil
// Flow reads a build/release workflow by slug.
//
// r := lib.Flow("go")
// if r.OK { content := r.Value.(string) }
func Flow(slug string) core.Result {
return flowFS.ReadString(slug + ".md")
}
func Persona(path string) (string, error) {
data, err := personaFS.ReadFile("persona/" + path + ".md")
if err != nil {
return "", err
}
return string(data), nil
// Persona reads a domain/role persona by path.
//
// r := lib.Persona("secops/developer")
// if r.OK { content := r.Value.(string) }
func Persona(path string) core.Result {
return personaFS.ReadString(path + ".md")
}
// --- Workspace Templates ---
@ -138,67 +168,36 @@ type WorkspaceData struct {
// ExtractWorkspace creates an agent workspace from a template.
// Template names: "default", "security", "review".
func ExtractWorkspace(tmplName, targetDir string, data *WorkspaceData) error {
wsDir := "workspace/" + tmplName
if err := os.MkdirAll(targetDir, 0755); err != nil {
return err
r := workspaceFS.Sub(tmplName)
if !r.OK {
if err, ok := r.Value.(error); ok {
return err
}
return core.E("ExtractWorkspace", "template not found: "+tmplName, nil)
}
return fs.WalkDir(workspaceFS, wsDir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
result := core.Extract(r.Value.(*core.Embed).FS(), targetDir, data)
if !result.OK {
if err, ok := result.Value.(error); ok {
return err
}
// Get relative path from template root
rel, err := filepath.Rel(wsDir, path)
if err != nil || rel == "." {
return nil
}
targetPath := filepath.Join(targetDir, rel)
if d.IsDir() {
return os.MkdirAll(targetPath, 0755)
}
content, err := fs.ReadFile(workspaceFS, path)
if err != nil {
return err
}
// Process .tmpl files through text/template
outputName := filepath.Base(targetPath)
if core.HasSuffix(outputName, ".tmpl") {
outputName = core.TrimSuffix(outputName, ".tmpl")
targetPath = filepath.Join(filepath.Dir(targetPath), outputName)
tmpl, err := template.New(outputName).Parse(string(content))
if err != nil {
return err
}
var buf bytes.Buffer
if err := tmpl.Execute(&buf, data); err != nil {
return err
}
content = buf.Bytes()
}
return os.WriteFile(targetPath, content, 0644)
})
}
return nil
}
// --- List Functions ---
func ListPrompts() []string { return listDir(promptFS, "prompt") }
func ListFlows() []string { return listDir(flowFS, "flow") }
func ListWorkspaces() []string { return listDir(workspaceFS, "workspace") }
func ListPrompts() []string { return listDir(promptFS) }
func ListFlows() []string { return listDir(flowFS) }
func ListWorkspaces() []string { return listDir(workspaceFS) }
func ListTasks() []string {
var slugs []string
fs.WalkDir(taskFS, "task", func(path string, d fs.DirEntry, err error) error {
base := taskFS.BaseDirectory()
fs.WalkDir(taskFS.FS(), base, func(path string, d fs.DirEntry, err error) error {
if err != nil || d.IsDir() {
return nil
}
rel := core.TrimPrefix(path, "task/")
rel := core.TrimPrefix(path, base+"/")
ext := filepath.Ext(rel)
slugs = append(slugs, core.TrimSuffix(rel, ext))
return nil
@ -208,12 +207,13 @@ func ListTasks() []string {
func ListPersonas() []string {
var paths []string
fs.WalkDir(personaFS, "persona", func(path string, d fs.DirEntry, err error) error {
base := personaFS.BaseDirectory()
fs.WalkDir(personaFS.FS(), base, func(path string, d fs.DirEntry, err error) error {
if err != nil || d.IsDir() {
return nil
}
if core.HasSuffix(path, ".md") {
rel := core.TrimPrefix(path, "persona/")
rel := core.TrimPrefix(path, base+"/")
rel = core.TrimSuffix(rel, ".md")
paths = append(paths, rel)
}
@ -222,13 +222,13 @@ func ListPersonas() []string {
return paths
}
func listDir(fsys embed.FS, dir string) []string {
entries, err := fsys.ReadDir(dir)
if err != nil {
func listDir(emb *core.Embed) []string {
r := emb.ReadDir(".")
if !r.OK {
return nil
}
var slugs []string
for _, e := range entries {
for _, e := range r.Value.([]fs.DirEntry) {
name := e.Name()
if e.IsDir() {
slugs = append(slugs, name)

View file

@ -1,11 +1,202 @@
package lib
import (
"io/fs"
"os"
"path/filepath"
"testing"
)
// --- Prompt ---
func TestPrompt_Good(t *testing.T) {
r := Prompt("coding")
if !r.OK {
t.Fatal("Prompt('coding') returned !OK")
}
if r.Value.(string) == "" {
t.Error("Prompt('coding') returned empty string")
}
}
func TestPrompt_Bad(t *testing.T) {
r := Prompt("nonexistent-slug")
if r.OK {
t.Error("Prompt('nonexistent-slug') should return !OK")
}
}
// --- Task ---
func TestTask_Good_Yaml(t *testing.T) {
r := Task("bug-fix")
if !r.OK {
t.Fatal("Task('bug-fix') returned !OK")
}
if r.Value.(string) == "" {
t.Error("Task('bug-fix') returned empty string")
}
}
func TestTask_Good_Md(t *testing.T) {
r := Task("code/review")
if !r.OK {
t.Fatal("Task('code/review') returned !OK")
}
if r.Value.(string) == "" {
t.Error("Task('code/review') returned empty string")
}
}
func TestTask_Bad(t *testing.T) {
r := Task("nonexistent-slug")
if r.OK {
t.Error("Task('nonexistent-slug') should return !OK")
}
if r.Value != fs.ErrNotExist {
t.Error("Task('nonexistent-slug') should return fs.ErrNotExist")
}
}
// --- TaskBundle ---
func TestTaskBundle_Good(t *testing.T) {
r := TaskBundle("code/review")
if !r.OK {
t.Fatal("TaskBundle('code/review') returned !OK")
}
b := r.Value.(Bundle)
if b.Main == "" {
t.Error("Bundle.Main is empty")
}
if len(b.Files) == 0 {
t.Error("Bundle.Files is empty — expected companion files")
}
}
func TestTaskBundle_Bad(t *testing.T) {
r := TaskBundle("nonexistent")
if r.OK {
t.Error("TaskBundle('nonexistent') should return !OK")
}
}
// --- Flow ---
func TestFlow_Good(t *testing.T) {
r := Flow("go")
if !r.OK {
t.Fatal("Flow('go') returned !OK")
}
if r.Value.(string) == "" {
t.Error("Flow('go') returned empty string")
}
}
// --- Persona ---
func TestPersona_Good(t *testing.T) {
// Use first persona from list to avoid hardcoding
personas := ListPersonas()
if len(personas) == 0 {
t.Skip("no personas found")
}
r := Persona(personas[0])
if !r.OK {
t.Fatalf("Persona(%q) returned !OK", personas[0])
}
if r.Value.(string) == "" {
t.Errorf("Persona(%q) returned empty string", personas[0])
}
}
// --- Template ---
func TestTemplate_Good_Prompt(t *testing.T) {
r := Template("coding")
if !r.OK {
t.Fatal("Template('coding') returned !OK")
}
if r.Value.(string) == "" {
t.Error("Template('coding') returned empty string")
}
}
func TestTemplate_Good_TaskFallback(t *testing.T) {
r := Template("bug-fix")
if !r.OK {
t.Fatal("Template('bug-fix') returned !OK — should fall through to Task")
}
}
func TestTemplate_Bad(t *testing.T) {
r := Template("nonexistent-slug")
if r.OK {
t.Error("Template('nonexistent-slug') should return !OK")
}
}
// --- List Functions ---
func TestListPrompts(t *testing.T) {
prompts := ListPrompts()
if len(prompts) == 0 {
t.Error("ListPrompts() returned empty")
}
}
func TestListTasks(t *testing.T) {
tasks := ListTasks()
if len(tasks) == 0 {
t.Fatal("ListTasks() returned empty")
}
// Verify nested paths are included (e.g., "code/review")
found := false
for _, s := range tasks {
if s == "code/review" {
found = true
break
}
}
if !found {
t.Error("ListTasks() missing nested path 'code/review'")
}
}
func TestListPersonas(t *testing.T) {
personas := ListPersonas()
if len(personas) == 0 {
t.Error("ListPersonas() returned empty")
}
// Should have nested paths like "code/go"
hasNested := false
for _, p := range personas {
if len(p) > 0 && filepath.Dir(p) != "." {
hasNested = true
break
}
}
if !hasNested {
t.Error("ListPersonas() has no nested paths")
}
}
func TestListFlows(t *testing.T) {
flows := ListFlows()
if len(flows) == 0 {
t.Error("ListFlows() returned empty")
}
}
func TestListWorkspaces(t *testing.T) {
workspaces := ListWorkspaces()
if len(workspaces) == 0 {
t.Error("ListWorkspaces() returned empty")
}
}
// --- ExtractWorkspace ---
func TestExtractWorkspace_CreatesFiles(t *testing.T) {
dir := t.TempDir()
data := &WorkspaceData{Repo: "test-repo", Task: "test task"}
@ -15,7 +206,6 @@ func TestExtractWorkspace_CreatesFiles(t *testing.T) {
t.Fatalf("ExtractWorkspace failed: %v", err)
}
// Check top-level template files exist
for _, name := range []string{"CODEX.md", "CLAUDE.md", "PROMPT.md", "TODO.md", "CONTEXT.md"} {
path := filepath.Join(dir, name)
if _, err := os.Stat(path); os.IsNotExist(err) {
@ -33,19 +223,16 @@ func TestExtractWorkspace_CreatesSubdirectories(t *testing.T) {
t.Fatalf("ExtractWorkspace failed: %v", err)
}
// Check .core/reference/ directory exists with files
refDir := filepath.Join(dir, ".core", "reference")
if _, err := os.Stat(refDir); os.IsNotExist(err) {
t.Fatalf(".core/reference/ directory not created")
}
// Check AX spec exists
axSpec := filepath.Join(refDir, "RFC-025-AGENT-EXPERIENCE.md")
if _, err := os.Stat(axSpec); os.IsNotExist(err) {
t.Errorf("AX spec not extracted: %s", axSpec)
}
// Check Core source files exist
entries, err := os.ReadDir(refDir)
if err != nil {
t.Fatalf("failed to read reference dir: %v", err)
@ -61,7 +248,6 @@ func TestExtractWorkspace_CreatesSubdirectories(t *testing.T) {
t.Error("no .go files in .core/reference/")
}
// Check docs subdirectory
docsDir := filepath.Join(refDir, "docs")
if _, err := os.Stat(docsDir); os.IsNotExist(err) {
t.Errorf(".core/reference/docs/ not created")
@ -77,7 +263,6 @@ func TestExtractWorkspace_TemplateSubstitution(t *testing.T) {
t.Fatalf("ExtractWorkspace failed: %v", err)
}
// TODO.md should contain the task
content, err := os.ReadFile(filepath.Join(dir, "TODO.md"))
if err != nil {
t.Fatalf("failed to read TODO.md: %v", err)

View file

@ -1,4 +0,0 @@
.idea/
.vscode/
*.log
.core/