fix: rewrite CLAUDE.md and llm.txt — badly stale, wrong API documented

CLAUDE.md was telling agents NOT to use WithService (the actual API).
Tests path was wrong (tests/ vs root *_test.go). core.New() example
showed deleted DTO pattern. PERFORM listed as current. Missing 6
subsystem accessors.

llm.txt had pkg/core/ path (doesn't exist), PERFORM reference,
missing 8 key types.

Both now match v0.8.0 implementation.

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Snider 2026-03-25 17:05:07 +00:00
parent c91f96d89d
commit cd452791e5
2 changed files with 65 additions and 53 deletions

View file

@ -4,16 +4,15 @@ Guidance for Claude Code and Codex when working with this repository.
## Module
`dappco.re/go/core` — dependency injection, service lifecycle, command routing, and message-passing for Go.
`dappco.re/go/core` — dependency injection, service lifecycle, permission, and message-passing for Go.
Source files live at the module root (not `pkg/core/`). Tests live in `tests/`.
Source files and tests live at the module root. No `pkg/` nesting.
## Build & Test
```bash
go test ./tests/... # run all tests
go build . # verify compilation
GOWORK=off go test ./tests/ # test without workspace
go test ./... -count=1 # run all tests (483 tests, 84.7% coverage)
go build ./... # verify compilation
```
Or via the Core CLI:
@ -25,28 +24,23 @@ core go qa # fmt + vet + lint + test
## API Shape
CoreGO uses the DTO/Options/Result pattern, not functional options:
```go
c := core.New(core.Options{
{Key: "name", Value: "myapp"},
})
c.Service("cache", core.Service{
OnStart: func() core.Result { return core.Result{OK: true} },
OnStop: func() core.Result { return core.Result{OK: true} },
})
c.Command("deploy/to/homelab", core.Command{
Action: func(opts core.Options) core.Result {
return core.Result{Value: "deployed", OK: true}
},
})
r := c.Cli().Run("deploy", "to", "homelab")
c := core.New(
core.WithOption("name", "myapp"),
core.WithService(mypackage.Register),
core.WithServiceLock(),
)
c.Run() // or: if err := c.RunE(); err != nil { ... }
```
**Do not use:** `WithService`, `WithName`, `WithApp`, `WithServiceLock`, `Must*`, `ServiceFor[T]` — these no longer exist.
Service factory:
```go
func Register(c *core.Core) core.Result {
svc := &MyService{ServiceRuntime: core.NewServiceRuntime(c, MyOpts{})}
return core.Result{Value: svc, OK: true}
}
```
## Subsystems
@ -54,26 +48,37 @@ r := c.Cli().Run("deploy", "to", "homelab")
|----------|---------|---------|
| `c.Options()` | `*Options` | Input configuration |
| `c.App()` | `*App` | Application identity |
| `c.Data()` | `*Data` | Embedded filesystem mounts |
| `c.Drive()` | `*Drive` | Named transport handles |
| `c.Fs()` | `*Fs` | Local filesystem I/O |
| `c.Config()` | `*Config` | Runtime settings |
| `c.Cli()` | `*Cli` | CLI surface |
| `c.Command("path")` | `Result` | Command tree |
| `c.Service("name")` | `Result` | Service registry |
| `c.Lock("name")` | `*Lock` | Named mutexes |
| `c.IPC()` | `*Ipc` | Message bus |
| `c.I18n()` | `*I18n` | Locale + translation |
| `c.Config()` | `*Config` | Runtime settings, feature flags |
| `c.Data()` | `*Data` | Embedded assets (Registry[*Embed]) |
| `c.Drive()` | `*Drive` | Transport handles (Registry[*DriveHandle]) |
| `c.Fs()` | `*Fs` | Filesystem I/O (sandboxable) |
| `c.Cli()` | `*Cli` | CLI command framework |
| `c.IPC()` | `*Ipc` | Message bus internals |
| `c.Process()` | `*Process` | Managed execution (Action sugar) |
| `c.API()` | `*API` | Remote streams (protocol handlers) |
| `c.Action(name)` | `*Action` | Named callable (register/invoke) |
| `c.Task(name)` | `*Task` | Composed Action sequence |
| `c.Entitled(name)` | `Entitlement` | Permission check |
| `c.RegistryOf(n)` | `*Registry` | Cross-cutting queries |
| `c.I18n()` | `*I18n` | Internationalisation |
## Messaging
| Method | Pattern |
|--------|---------|
| `c.ACTION(msg)` | Broadcast to all handlers |
| `c.ACTION(msg)` | Broadcast to all handlers (panic recovery per handler) |
| `c.QUERY(q)` | First responder wins |
| `c.QUERYALL(q)` | Collect all responses |
| `c.PERFORM(task)` | First executor wins |
| `c.PerformAsync(task)` | Background goroutine |
| `c.PerformAsync(action, opts)` | Background goroutine with progress |
## Lifecycle
```go
type Startable interface { OnStartup(ctx context.Context) Result }
type Stoppable interface { OnShutdown(ctx context.Context) Result }
```
`RunE()` always calls `defer ServiceShutdown` — even on startup failure or panic.
## Error Handling
@ -83,13 +88,15 @@ Use `core.E()` for structured errors:
return core.E("service.Method", "what failed", underlyingErr)
```
## Test Naming
**Never** use `fmt.Errorf`, `errors.New`, `os/exec`, or `unsafe.Pointer` on Core types.
`_Good` (happy path), `_Bad` (expected errors), `_Ugly` (panics/edge cases).
## Test Naming (AX-7)
`TestFile_Function_{Good,Bad,Ugly}` — 100% compliance.
## Docs
Full documentation in `docs/`. Start with `docs/getting-started.md`.
Full API contract: `docs/RFC.md` (1476 lines, 21 sections).
## Go Workspace

31
llm.txt
View file

@ -1,41 +1,46 @@
# core/go — CoreGO DI Framework
# core/go — CoreGO Framework
> dappco.re/go/core — Dependency injection, service lifecycle, and message-passing
> framework for Go. Foundation layer for the Lethean ecosystem.
> dappco.re/go/core — Dependency injection, service lifecycle, permission,
> and message-passing framework for Go. Foundation layer for the Lethean ecosystem.
## Entry Points
- CLAUDE.md — Agent instructions, build commands, architecture overview
- docs/RFC.md — API contract specification (start here for the full spec)
- CLAUDE.md — Agent instructions, build commands, subsystem table
- docs/RFC.md — API contract specification (21 sections, the authoritative spec)
## Package Layout
- pkg/core/ — The Core struct: DI container, service registry, lifecycle, IPC
- No subpackages — everything is in the root core package
All source files at module root. No pkg/ nesting. Tests are *_test.go alongside source.
## Key Types
- Core — Central application container (created via core.New())
- Core — Central application container (core.New() returns *Core)
- Option — Single key-value pair {Key: string, Value: any}
- Options — Collection of Option with typed accessors
- Result — Universal return type {Value: any, OK: bool}
- Service — Managed component with lifecycle (Startable/Stoppable)
- Message — IPC message interface for ACTION/QUERY/PERFORM
- Service — Managed component with lifecycle (Startable/Stoppable return Result)
- Action — Named callable with panic recovery and entitlement enforcement
- Task — Composed sequence of Actions (Steps, Async, Input piping)
- Registry[T] — Thread-safe named collection (universal brick)
- Process — Managed execution (sugar over Actions)
- API — Remote streams (protocol handlers, Drive integration)
- Entitlement — Permission check result (Allowed, Limit, Used, Remaining)
- Message — IPC broadcast type for ACTION
- Query — IPC request/response type for QUERY
## Service Pattern
Services register via factory functions:
core.New(
core.WithService(mypackage.Register),
)
func Register(c *core.Core) core.Result {
svc := &MyService{runtime: core.NewServiceRuntime(c, opts)}
svc := &MyService{ServiceRuntime: core.NewServiceRuntime(c, opts)}
return core.Result{Value: svc, OK: true}
}
## Conventions
Follows RFC-025 Agent Experience (AX) principles.
Tests: TestFile_Function_{Good,Bad,Ugly} — 100% AX-7 naming.
See: https://core.help/specs/RFC-025-AGENT-EXPERIENCE/