diff --git a/pkg/lib/lib_test.go b/pkg/lib/lib_test.go index cde2c62..8f637b8 100644 --- a/pkg/lib/lib_test.go +++ b/pkg/lib/lib_test.go @@ -342,6 +342,58 @@ func TestLib_ExtractWorkspace_Good_ReferenceHeaders(t *testing.T) { } } +func TestLib_ExtractWorkspace_Good_ReferenceUsageExamples(t *testing.T) { + dir := t.TempDir() + data := &WorkspaceData{Repo: "test-repo", Task: "carry AX usage examples into workspace references"} + + if err := ExtractWorkspace("default", dir, data); err != nil { + t.Fatalf("ExtractWorkspace failed: %v", err) + } + + cases := map[string][]string{ + core.JoinPath(dir, ".core", "reference", "array.go"): { + `arr := core.NewArray("prep", "dispatch")`, + `arr.Add("verify", "merge")`, + `arr.AddUnique("verify", "verify", "merge")`, + }, + core.JoinPath(dir, ".core", "reference", "config.go"): { + `timeout := core.ConfigGet[int](c.Config(), "agent.timeout")`, + }, + core.JoinPath(dir, ".core", "reference", "embed.go"): { + `core.AddAsset("docs", "RFC.md", packed)`, + `r := core.GeneratePack(pkg)`, + }, + core.JoinPath(dir, ".core", "reference", "error.go"): { + `if core.Is(err, context.Canceled) { return }`, + `stack := core.FormatStackTrace(err)`, + `r := c.Error().Reports(10)`, + }, + core.JoinPath(dir, ".core", "reference", "log.go"): { + `log := core.NewLog(core.LogOptions{Level: core.LevelDebug, Output: os.Stdout})`, + `core.SetRedactKeys("token", "password")`, + `core.Security("entitlement.denied", "action", "process.run")`, + }, + core.JoinPath(dir, ".core", "reference", "runtime.go"): { + `r := c.ServiceStartup(context.Background(), nil)`, + `r := core.NewRuntime(app)`, + `name := runtime.ServiceName()`, + }, + } + + for path, snippets := range cases { + r := testFs.Read(path) + if !r.OK { + t.Fatalf("failed to read %s", path) + } + text := r.Value.(string) + for _, snippet := range snippets { + if !core.Contains(text, snippet) { + t.Errorf("%s missing usage example snippet %q", path, snippet) + } + } + } +} + func TestLib_MountEmbed_Bad(t *testing.T) { result := mountEmbed(promptFiles, "missing-dir") if result.OK { diff --git a/pkg/lib/workspace/default/.core/reference/array.go b/pkg/lib/workspace/default/.core/reference/array.go index ff085bb..6d8eab6 100644 --- a/pkg/lib/workspace/default/.core/reference/array.go +++ b/pkg/lib/workspace/default/.core/reference/array.go @@ -10,17 +10,23 @@ type Array[T comparable] struct { items []T } -// NewArray creates an empty Array. +// NewArray creates an Array with the provided items. +// +// arr := core.NewArray("prep", "dispatch") func NewArray[T comparable](items ...T) *Array[T] { return &Array[T]{items: items} } // Add appends values. +// +// arr.Add("verify", "merge") func (s *Array[T]) Add(values ...T) { s.items = append(s.items, values...) } // AddUnique appends values only if not already present. +// +// arr.AddUnique("verify", "verify", "merge") func (s *Array[T]) AddUnique(values ...T) { for _, v := range values { if !s.Contains(v) { @@ -40,6 +46,8 @@ func (s *Array[T]) Contains(val T) bool { } // Filter returns a new Array with elements matching the predicate. +// +// r := arr.Filter(func(step string) bool { return core.Contains(step, "prep") }) func (s *Array[T]) Filter(fn func(T) bool) Result { filtered := &Array[T]{} for _, v := range s.items { @@ -68,6 +76,8 @@ func (s *Array[T]) Remove(val T) { } // Deduplicate removes duplicate values, preserving order. +// +// arr.Deduplicate() func (s *Array[T]) Deduplicate() { seen := make(map[T]struct{}) result := make([]T, 0, len(s.items)) @@ -91,6 +101,8 @@ func (s *Array[T]) Clear() { } // AsSlice returns a copy of the underlying slice. +// +// items := arr.AsSlice() func (s *Array[T]) AsSlice() []T { if s.items == nil { return nil diff --git a/pkg/lib/workspace/default/.core/reference/config.go b/pkg/lib/workspace/default/.core/reference/config.go index 2f45f13..fd4e54d 100644 --- a/pkg/lib/workspace/default/.core/reference/config.go +++ b/pkg/lib/workspace/default/.core/reference/config.go @@ -117,6 +117,8 @@ func (e *Config) Int(key string) int { return ConfigGet[int](e, key) } func (e *Config) Bool(key string) bool { return ConfigGet[bool](e, key) } // ConfigGet retrieves a typed configuration value. +// +// timeout := core.ConfigGet[int](c.Config(), "agent.timeout") func ConfigGet[T any](e *Config, key string) T { r := e.Get(key) if !r.OK { diff --git a/pkg/lib/workspace/default/.core/reference/embed.go b/pkg/lib/workspace/default/.core/reference/embed.go index 7951543..21009ad 100644 --- a/pkg/lib/workspace/default/.core/reference/embed.go +++ b/pkg/lib/workspace/default/.core/reference/embed.go @@ -51,6 +51,8 @@ var ( ) // AddAsset registers a packed asset at runtime (called from generated init()). +// +// core.AddAsset("docs", "RFC.md", packed) func AddAsset(group, name, data string) { assetGroupsMu.Lock() defer assetGroupsMu.Unlock() @@ -214,6 +216,8 @@ func ScanAssets(filenames []string) Result { } // GeneratePack creates Go source code that embeds the scanned assets. +// +// r := core.GeneratePack(pkg) func GeneratePack(pkg ScannedPackage) Result { b := NewBuilder() diff --git a/pkg/lib/workspace/default/.core/reference/error.go b/pkg/lib/workspace/default/.core/reference/error.go index d562494..182fd14 100644 --- a/pkg/lib/workspace/default/.core/reference/error.go +++ b/pkg/lib/workspace/default/.core/reference/error.go @@ -120,18 +120,25 @@ func NewCode(code, msg string) error { // Is reports whether any error in err's tree matches target. // Wrapper around errors.Is for convenience. +// +// if core.Is(err, context.Canceled) { return } func Is(err, target error) bool { return errors.Is(err, target) } // As finds the first error in err's tree that matches target. // Wrapper around errors.As for convenience. +// +// var typed *core.Err +// if core.As(err, &typed) { core.Println(typed.Operation) } func As(err error, target any) bool { return errors.As(err, target) } // NewError creates a simple error with the given text. // Wrapper around errors.New for convenience. +// +// err := core.NewError("workspace not found") func NewError(text string) error { return errors.New(text) } @@ -147,6 +154,8 @@ func ErrorJoin(errs ...error) error { // Operation extracts the operation name from an error. // Returns empty string if the error is not an *Err. +// +// op := core.Operation(err) func Operation(err error) string { var e *Err if As(err, &e) { @@ -157,6 +166,8 @@ func Operation(err error) string { // ErrorCode extracts the error code from an error. // Returns empty string if the error is not an *Err or has no code. +// +// code := core.ErrorCode(err) func ErrorCode(err error) string { var e *Err if As(err, &e) { @@ -180,6 +191,8 @@ func ErrorMessage(err error) string { // Root returns the root cause of an error chain. // Unwraps until no more wrapped errors are found. +// +// cause := core.Root(err) func Root(err error) error { if err == nil { return nil @@ -212,6 +225,8 @@ func AllOperations(err error) iter.Seq[string] { // StackTrace returns the logical stack trace (chain of operations) from an error. // It returns an empty slice if no operational context is found. +// +// trace := core.StackTrace(err) func StackTrace(err error) []string { var stack []string for op := range AllOperations(err) { @@ -221,6 +236,8 @@ func StackTrace(err error) []string { } // FormatStackTrace returns a pretty-printed logical stack trace. +// +// stack := core.FormatStackTrace(err) func FormatStackTrace(err error) string { var ops []string for op := range AllOperations(err) { @@ -338,6 +355,8 @@ func (h *ErrorPanic) Recover() { } // SafeGo runs a function in a goroutine with panic recovery. +// +// c.Error().SafeGo(func() { runWorker() }) func (h *ErrorPanic) SafeGo(fn func()) { go func() { defer h.Recover() @@ -346,6 +365,8 @@ func (h *ErrorPanic) SafeGo(fn func()) { } // Reports returns the last n crash reports from the file. +// +// r := c.Error().Reports(10) func (h *ErrorPanic) Reports(n int) Result { if h.filePath == "" { return Result{} diff --git a/pkg/lib/workspace/default/.core/reference/log.go b/pkg/lib/workspace/default/.core/reference/log.go index 4a2dd9e..5114dd7 100644 --- a/pkg/lib/workspace/default/.core/reference/log.go +++ b/pkg/lib/workspace/default/.core/reference/log.go @@ -110,6 +110,8 @@ type LogOptions struct { var RotationWriterFactory func(RotationLogOptions) goio.WriteCloser // New creates a new Log with the given options. +// +// log := core.NewLog(core.LogOptions{Level: core.LevelDebug, Output: os.Stdout}) func NewLog(opts LogOptions) *Log { output := opts.Output if opts.Rotation != nil && opts.Rotation.Filename != "" && RotationWriterFactory != nil { @@ -286,6 +288,8 @@ func (l *Log) Security(msg string, keyvals ...any) { // Username returns the current system username. // It uses os/user for reliability and falls back to environment variables. +// +// user := core.Username() func Username() string { if u, err := user.Current(); err == nil { return u.Username @@ -307,46 +311,64 @@ func init() { } // Default returns the default logger. +// +// log := core.Default() func Default() *Log { return defaultLogPtr.Load() } // SetDefault sets the default logger. +// +// core.SetDefault(core.NewLog(core.LogOptions{Level: core.LevelWarn})) func SetDefault(l *Log) { defaultLogPtr.Store(l) } // SetLevel sets the default logger's level. +// +// core.SetLevel(core.LevelDebug) func SetLevel(level Level) { Default().SetLevel(level) } // SetRedactKeys sets the default logger's redaction keys. +// +// core.SetRedactKeys("token", "password") func SetRedactKeys(keys ...string) { Default().SetRedactKeys(keys...) } // Debug logs to the default logger. +// +// core.Debug("agent dispatched", "repo", "core/agent") func Debug(msg string, keyvals ...any) { Default().Debug(msg, keyvals...) } // Info logs to the default logger. +// +// core.Info("workspace ready", "path", "/srv/ws-42") func Info(msg string, keyvals ...any) { Default().Info(msg, keyvals...) } // Warn logs to the default logger. +// +// core.Warn("queue delay increased", "seconds", 30) func Warn(msg string, keyvals ...any) { Default().Warn(msg, keyvals...) } // Error logs to the default logger. +// +// core.Error("dispatch failed", "err", err) func Error(msg string, keyvals ...any) { Default().Error(msg, keyvals...) } // Security logs to the default logger. +// +// core.Security("entitlement.denied", "action", "process.run") func Security(msg string, keyvals ...any) { Default().Security(msg, keyvals...) } @@ -360,6 +382,8 @@ type LogErr struct { } // NewLogErr creates a LogErr bound to the given logger. +// +// logErr := core.NewLogErr(core.Default()) func NewLogErr(log *Log) *LogErr { return &LogErr{log: log} } @@ -381,6 +405,8 @@ type LogPanic struct { } // NewLogPanic creates a LogPanic bound to the given logger. +// +// panicLog := core.NewLogPanic(core.Default()) func NewLogPanic(log *Log) *LogPanic { return &LogPanic{log: log} } diff --git a/pkg/lib/workspace/default/.core/reference/runtime.go b/pkg/lib/workspace/default/.core/reference/runtime.go index c9a8223..55f7ec9 100644 --- a/pkg/lib/workspace/default/.core/reference/runtime.go +++ b/pkg/lib/workspace/default/.core/reference/runtime.go @@ -21,6 +21,8 @@ type ServiceRuntime[T any] struct { } // NewServiceRuntime creates a ServiceRuntime for a service constructor. +// +// svc.ServiceRuntime = core.NewServiceRuntime(c, ServiceOptions{Queue: "default"}) func NewServiceRuntime[T any](c *Core, opts T) *ServiceRuntime[T] { return &ServiceRuntime[T]{core: c, opts: opts} } @@ -43,6 +45,8 @@ func (r *ServiceRuntime[T]) Config() *Config { return r.core.Config() } // --- Lifecycle --- // ServiceStartup runs OnStart for all registered services that have one. +// +// r := c.ServiceStartup(context.Background(), nil) func (c *Core) ServiceStartup(ctx context.Context, options any) Result { c.shutdown.Store(false) c.context, c.cancel = context.WithCancel(ctx) @@ -63,6 +67,8 @@ func (c *Core) ServiceStartup(ctx context.Context, options any) Result { } // ServiceShutdown drains background tasks, then stops all registered services. +// +// r := c.ServiceShutdown(context.Background()) func (c *Core) ServiceShutdown(ctx context.Context) Result { c.shutdown.Store(true) c.cancel() // signal all context-aware tasks to stop @@ -116,6 +122,8 @@ type Runtime struct { type ServiceFactory func() Result // NewWithFactories creates a Runtime with the provided service factories. +// +// r := core.NewWithFactories(app, map[string]core.ServiceFactory{"brain": newBrainService}) func NewWithFactories(app any, factories map[string]ServiceFactory) Result { c := New(WithOptions(NewOptions(Option{Key: "name", Value: "core"}))) c.app.Runtime = app @@ -144,18 +152,27 @@ func NewWithFactories(app any, factories map[string]ServiceFactory) Result { } // NewRuntime creates a Runtime with no custom services. +// +// r := core.NewRuntime(app) func NewRuntime(app any) Result { return NewWithFactories(app, map[string]ServiceFactory{}) } // ServiceName returns "Core" — the Runtime's service identity. +// +// name := runtime.ServiceName() func (r *Runtime) ServiceName() string { return "Core" } // ServiceStartup starts all services via the embedded Core. +// +// runtime.ServiceStartup(context.Background(), nil) func (r *Runtime) ServiceStartup(ctx context.Context, options any) Result { return r.Core.ServiceStartup(ctx, options) } + // ServiceShutdown stops all services via the embedded Core. +// +// runtime.ServiceShutdown(context.Background()) func (r *Runtime) ServiceShutdown(ctx context.Context) Result { if r.Core != nil { return r.Core.ServiceShutdown(ctx)