Added: Embed/Mount/Extract, error wrapping/codes/introspection, Print, Array[T], Config, IPC (Action/Query/Perform), Services, Commands, Drive, I18n. Every Core primitive documented with WRONG/CORRECT examples. Co-Authored-By: Virgil <virgil@lethean.io>
316 lines
7.4 KiB
Cheetah
316 lines
7.4 KiB
Cheetah
# CODEX.md
|
|
|
|
Instructions for Codex when working with code in this workspace.
|
|
|
|
## Core Framework
|
|
|
|
This project uses `dappco.re/go/core` as its foundation. Core provides primitives that REPLACE standard library and third-party packages. Implementation reference is in `.core/reference/*.go`.
|
|
|
|
## Mandatory Patterns
|
|
|
|
### Errors — use `core.E()`, never `fmt.Errorf` or `errors.New`
|
|
|
|
```go
|
|
// WRONG
|
|
return fmt.Errorf("failed to read: %w", err)
|
|
return errors.New("not found")
|
|
|
|
// CORRECT
|
|
return core.E("readConfig", "failed to read config", err)
|
|
return core.E("findUser", "user not found", nil)
|
|
```
|
|
|
|
### Logging — use `core.Error/Info/Warn/Debug`, never `log.*` or `fmt.Print*`
|
|
|
|
```go
|
|
// WRONG
|
|
log.Printf("starting server on %s", addr)
|
|
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
|
|
|
// CORRECT
|
|
core.Info("starting server", "addr", addr)
|
|
core.Error("operation failed", "err", err)
|
|
```
|
|
|
|
### Filesystem — use `core.Fs{}`, never `os.ReadFile/WriteFile/MkdirAll`
|
|
|
|
```go
|
|
var fs = &core.Fs{}
|
|
r := fs.Read(path) // returns core.Result
|
|
if !r.OK { return r }
|
|
content := r.Value.(string)
|
|
|
|
fs.Write(path, content) // returns core.Result
|
|
fs.EnsureDir(dir) // returns core.Result
|
|
```
|
|
|
|
### Returns — use `core.Result`, never `(value, error)`
|
|
|
|
```go
|
|
// WRONG
|
|
func LoadConfig(path string) (string, error) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil { return "", err }
|
|
return string(data), nil
|
|
}
|
|
|
|
// CORRECT
|
|
func LoadConfig(path string) core.Result {
|
|
return fs.Read(path)
|
|
}
|
|
```
|
|
|
|
### Strings — use `core.*`, never `strings.*` or `fmt.Sprintf`
|
|
|
|
| Do NOT use | Use instead |
|
|
|------------|-------------|
|
|
| `strings.Contains` | `core.Contains` |
|
|
| `strings.HasPrefix` | `core.HasPrefix` |
|
|
| `strings.HasSuffix` | `core.HasSuffix` |
|
|
| `strings.TrimSpace` | `core.Trim` |
|
|
| `strings.TrimPrefix` | `core.TrimPrefix` |
|
|
| `strings.TrimSuffix` | `core.TrimSuffix` |
|
|
| `strings.Split` | `core.Split` |
|
|
| `strings.SplitN` | `core.SplitN` |
|
|
| `strings.Join(parts, sep)` | `core.Join(sep, parts...)` |
|
|
| `strings.ReplaceAll` | `core.Replace` |
|
|
| `strings.ToLower` | `core.Lower` |
|
|
| `strings.ToUpper` | `core.Upper` |
|
|
| `strings.NewReader` | `core.NewReader` |
|
|
| `strings.Builder{}` | `core.NewBuilder()` |
|
|
| `fmt.Sprintf` | `core.Sprintf` |
|
|
| `fmt.Sprint` | `core.Sprint` |
|
|
|
|
### Imports — alias stdlib `io` as `goio`
|
|
|
|
```go
|
|
import goio "io"
|
|
```
|
|
|
|
### Comments — usage examples, not descriptions
|
|
|
|
```go
|
|
// WRONG
|
|
// LoadConfig loads configuration from a file path.
|
|
|
|
// CORRECT
|
|
// LoadConfig reads and parses a YAML configuration file.
|
|
//
|
|
// r := LoadConfig("/home/user/.core/agents.yaml")
|
|
// if r.OK { cfg := r.Value.(*AgentsConfig) }
|
|
```
|
|
|
|
### Names — predictable, never abbreviated
|
|
|
|
```
|
|
Config not Cfg
|
|
Service not Srv
|
|
Options not Opts
|
|
Error not Err (as subsystem name)
|
|
```
|
|
|
|
### UK English in comments
|
|
|
|
```
|
|
initialise not initialize
|
|
colour not color
|
|
organisation not organization
|
|
serialise not serialize
|
|
```
|
|
|
|
### Compile-time interface assertions
|
|
|
|
```go
|
|
var _ mcp.Subsystem = (*MySubsystem)(nil)
|
|
```
|
|
|
|
### Keyed struct literals
|
|
|
|
```go
|
|
// WRONG
|
|
core.Result{err, false}
|
|
|
|
// CORRECT
|
|
core.Result{Value: err, OK: false}
|
|
```
|
|
|
|
### Embedded content — use `core.Mount` + `core.Extract`, never raw `embed.FS`
|
|
|
|
```go
|
|
// WRONG
|
|
//go:embed templates
|
|
var templatesFS embed.FS
|
|
data, _ := templatesFS.ReadFile("templates/config.yaml")
|
|
|
|
// CORRECT
|
|
//go:embed templates
|
|
var templatesFS embed.FS
|
|
var templates = mustMount(templatesFS, "templates")
|
|
|
|
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)
|
|
}
|
|
|
|
r := templates.ReadString("config.yaml")
|
|
if r.OK { content := r.Value.(string) }
|
|
|
|
// Extract template directory with data substitution
|
|
core.Extract(templates.FS(), targetDir, data)
|
|
```
|
|
|
|
### Error wrapping — use `core.Wrap`, never manual chaining
|
|
|
|
```go
|
|
// WRONG
|
|
return fmt.Errorf("save failed: %w", err)
|
|
|
|
// CORRECT
|
|
return core.Wrap(err, "saveConfig", "failed to save config")
|
|
```
|
|
|
|
### Error codes — use `core.WrapCode` for machine-readable errors
|
|
|
|
```go
|
|
return core.WrapCode(err, "VALIDATION_ERROR", "user.Validate", "invalid email")
|
|
var ErrNotFound = core.NewCode("NOT_FOUND", "resource not found")
|
|
```
|
|
|
|
### Error introspection
|
|
|
|
```go
|
|
core.Operation(err) // extract operation name
|
|
core.ErrorCode(err) // extract error code
|
|
core.ErrorMessage(err) // extract message
|
|
core.Root(err) // root cause
|
|
core.StackTrace(err) // logical stack trace
|
|
core.Is(err, target) // errors.Is wrapper
|
|
core.As(err, &target) // errors.As wrapper
|
|
```
|
|
|
|
### Formatted output — use `core.Print`, never `fmt.Fprintf`
|
|
|
|
```go
|
|
// WRONG
|
|
fmt.Fprintf(os.Stderr, "server on %s\n", addr)
|
|
fmt.Println("done")
|
|
|
|
// CORRECT
|
|
core.Print(os.Stderr, "server on %s", addr) // writer + format
|
|
core.Print(nil, "done") // nil = stdout
|
|
```
|
|
|
|
### Arrays — use `core.Array[T]`, never manual slice management
|
|
|
|
```go
|
|
arr := core.NewArray[string]("a", "b", "c")
|
|
arr.AddUnique("d")
|
|
arr.Contains("a") // true
|
|
arr.Filter(func(s string) bool { return s != "b" })
|
|
arr.Deduplicate()
|
|
```
|
|
|
|
### Config — use `core.Config`, never raw maps
|
|
|
|
```go
|
|
c.Config().Set("port", 8080)
|
|
port := c.Config().Int("port")
|
|
c.Config().Enable("debug")
|
|
if c.Config().Enabled("debug") { ... }
|
|
```
|
|
|
|
### IPC — use `core.Action/Query/Perform` for inter-service communication
|
|
|
|
```go
|
|
// Fire-and-forget broadcast
|
|
c.ACTION(MyEvent{Data: "hello"})
|
|
|
|
// Query first responder
|
|
r := c.QUERY(FindUser{ID: 123})
|
|
if r.OK { user := r.Value.(*User) }
|
|
|
|
// Perform task (side effects)
|
|
r := c.PERFORM(SendEmail{To: "user@example.com"})
|
|
|
|
// Register handler
|
|
c.RegisterAction(func(c *core.Core, msg core.Message) core.Result {
|
|
switch m := msg.(type) {
|
|
case MyEvent:
|
|
core.Info("received event", "data", m.Data)
|
|
}
|
|
return core.Result{OK: true}
|
|
})
|
|
```
|
|
|
|
### Services — use `c.Service()` DTO pattern
|
|
|
|
```go
|
|
c.Service("cache", core.Service{
|
|
OnStart: func() core.Result { return core.Result{OK: true} },
|
|
OnStop: func() core.Result { return core.Result{OK: true} },
|
|
})
|
|
|
|
r := c.Service("cache")
|
|
if r.OK { svc := r.Value.(*core.Service) }
|
|
```
|
|
|
|
### Commands — use `c.Command()` path-based registration
|
|
|
|
```go
|
|
c.Command("deploy", core.Command{
|
|
Description: "Deploy to production",
|
|
Action: func(opts core.Options) core.Result {
|
|
target := opts.String("target")
|
|
return core.Result{Value: "deployed to " + target, OK: true}
|
|
},
|
|
})
|
|
|
|
// Nested commands use path notation
|
|
c.Command("deploy/to/homelab", core.Command{...})
|
|
|
|
// Run CLI
|
|
c.Cli().Run()
|
|
```
|
|
|
|
### Drive — use `c.Drive()` for transport handles
|
|
|
|
```go
|
|
c.Drive().New(core.Options{
|
|
{Key: "name", Value: "api"},
|
|
{Key: "transport", Value: "https://api.lthn.ai"},
|
|
})
|
|
|
|
r := c.Drive().Get("api")
|
|
if r.OK { handle := r.Value.(*core.DriveHandle) }
|
|
```
|
|
|
|
### I18n — use `c.I18n()` for translations
|
|
|
|
```go
|
|
r := c.I18n().Translate("greeting", "name", "World")
|
|
if r.OK { text := r.Value.(string) }
|
|
|
|
c.I18n().SetLanguage("en-GB")
|
|
```
|
|
|
|
## What NOT to import
|
|
|
|
| Do NOT import | Use instead |
|
|
|---------------|-------------|
|
|
| `fmt` | `core.Sprintf`, `core.Print` |
|
|
| `log` | `core.Error`, `core.Info` |
|
|
| `strings` | `core.Contains`, `core.Split` etc |
|
|
| `errors` | `core.E`, `core.Wrap` |
|
|
| `io/ioutil` | `core.Fs{}` |
|
|
| `os` (file ops) | `core.Fs{}` |
|
|
|
|
Acceptable stdlib: `os.Getenv`, `os.Exit`, `os.Stderr`, `os.UserHomeDir`, `context`, `sync`, `time`, `net/http`, `encoding/json`, `path/filepath`.
|
|
|
|
## Build & Test
|
|
|
|
```bash
|
|
go build ./...
|
|
go test ./...
|
|
go vet ./...
|
|
```
|