Skip to content

Commit dd98906

Browse files
committed
feat(docs): expand /go/ — Result/Options/Action/Error sub-pages + footer
Each primitive gets its own deep-dive page; the overview at /go/ links into them via a primitives table. Sidebar nests them under "core/go". Footer override (src/components/Footer.astro) keeps Starlight's default content, then adds dappco.re ecosystem links — lthn.ai, lthn.io, github.com/dappcore/go. 7 Starlight pages now: /, /go/, /go/result/, /go/options/, /go/action/, /go/error/, /404. Co-Authored-By: Virgil <virgil@lethean.io>
1 parent 4c59285 commit dd98906

7 files changed

Lines changed: 413 additions & 4 deletions

File tree

astro.config.mjs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ export default defineConfig({
1818
{ icon: 'github', label: 'GitHub', href: 'https://github.com/dappcore/go' },
1919
],
2020
customCss: ['./src/styles/styles.css'],
21+
components: {
22+
Footer: './src/components/Footer.astro',
23+
},
2124
head: [
2225
// Default go-import / go-source for the umbrella module. Per-package
2326
// MDX pages override these in their own `head` frontmatter.
@@ -41,6 +44,10 @@ export default defineConfig({
4144
label: 'core/go',
4245
items: [
4346
{ label: 'Overview', slug: 'go' },
47+
{ label: 'Result', slug: 'go/result' },
48+
{ label: 'Options', slug: 'go/options' },
49+
{ label: 'Actions', slug: 'go/action' },
50+
{ label: 'Errors', slug: 'go/error' },
4451
],
4552
},
4653
],

src/components/Footer.astro

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
---
2+
import Default from '@astrojs/starlight/components/Footer.astro';
3+
---
4+
5+
<Default><slot /></Default>
6+
7+
<footer class="site-footer">
8+
<div class="links">
9+
<a href="https://lthn.ai/" rel="noopener">lthn.ai</a>
10+
<span class="dot" aria-hidden="true">·</span>
11+
<a href="https://lthn.io/" rel="noopener">lthn.io</a>
12+
<span class="dot" aria-hidden="true">·</span>
13+
<a href="https://github.com/dappcore/go" rel="noopener">github.com/dappcore/go</a>
14+
</div>
15+
<div class="meta">EUPL-1.2 · part of the dAppCore ecosystem</div>
16+
</footer>
17+
18+
<style>
19+
.site-footer {
20+
margin-top: 2rem;
21+
padding: 1.5rem 0;
22+
border-top: 1px solid var(--sl-color-hairline);
23+
text-align: center;
24+
color: var(--sl-color-gray-3);
25+
font-size: var(--sl-text-sm);
26+
}
27+
28+
.site-footer .links a {
29+
color: var(--sl-color-text-accent);
30+
text-decoration: none;
31+
}
32+
33+
.site-footer .links a:hover {
34+
text-decoration: underline;
35+
}
36+
37+
.site-footer .dot {
38+
margin: 0 0.5rem;
39+
color: var(--sl-color-gray-4);
40+
}
41+
42+
.site-footer .meta {
43+
margin-top: 0.5rem;
44+
color: var(--sl-color-gray-4);
45+
}
46+
</style>

src/content/docs/go.mdx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,13 @@ go get dappco.re/go@latest
1515

1616
## Primitives
1717

18-
- **`core.Result`**`{Value any, OK bool}`. Replaces the `(T, error)` pair.
19-
- **`core.Options`** — typed key/value bag. The universal input.
20-
- **`core.Action`** — named, registered, invokable unit of work. The capability map.
21-
- **`core.Core`** — the fractal root. Every operation hangs off `c := core.New()`.
18+
| | |
19+
|---|---|
20+
| **[Result](/go/result/)** | `{Value any, OK bool}` — replaces the `(T, error)` pair. Constructors: `Ok`, `Fail`, `ResultOf`, `Try`. Unwrap: `Or`, `Must`, `Cast[T]`, `MustCast[T]`. |
21+
| **[Options](/go/options/)** | Typed key/value bag — the universal input. Accessors: `String`, `Int`, `Bool`, `Float64`, `Duration`. |
22+
| **[Actions](/go/action/)** | Named, registered, invokable unit of work. Lifecycle (`Enable`/`Disable`), composition (`Task`), background (`PerformAsync`). |
23+
| **[Errors](/go/error/)** | Structured `*Err` with operation context, stable codes, uniform introspection. Constructors: `E`, `NewError`, `NewCode`, `Wrap`. |
24+
| **`core.Core`** | The fractal root. Every operation hangs off `c := core.New()`. |
2225

2326
## Result
2427

src/content/docs/go/action.mdx

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
---
2+
title: Actions
3+
description: Named, registered, invokable units of work — the capability map of every Core instance.
4+
---
5+
6+
Actions are the atomic unit of work in `core/go`. Named, registered, invokable,
7+
inspectable. The Action registry **is** the capability map.
8+
9+
## Register
10+
11+
```go
12+
c.Action("git.log", func(ctx core.Context, opts core.Options) core.Result {
13+
dir := opts.String("dir")
14+
return c.Process().RunIn(ctx, dir, "git", "log")
15+
})
16+
```
17+
18+
The signature is invariant across every action:
19+
20+
```go
21+
type ActionHandler func(core.Context, core.Options) core.Result
22+
```
23+
24+
## Invoke
25+
26+
```go
27+
r := c.Action("git.log").Run(ctx, core.NewOptions(
28+
core.Option{Key: "dir", Value: "/path/to/repo"},
29+
))
30+
```
31+
32+
## Lifecycle — Enable / Disable
33+
34+
Actions are enabled at registration. Disable to soft-stop without unregistering:
35+
36+
```go
37+
c.Action("dangerous.purge").Disable()
38+
// ...later...
39+
c.Action("dangerous.purge").Enable()
40+
41+
if c.Action("dangerous.purge").Enabled() { /* will fire */ }
42+
```
43+
44+
`Run()` on a disabled action returns `Result{OK: false}` with code `"action.disabled"`.
45+
The capability stays queryable via `Exists()`.
46+
47+
## Inspect
48+
49+
| Method | What it returns |
50+
|---|---|
51+
| `c.Action(name).Exists()` | `true` if a handler is registered |
52+
| `c.Action(name).Enabled()` | `true` if it will run when invoked |
53+
| `c.Actions()` | All registered action names in registration order |
54+
55+
## Background — `PerformAsync`
56+
57+
For long-running work that shouldn't block the request path:
58+
59+
```go
60+
r := c.PerformAsync("agentic.dispatch", opts)
61+
taskID := r.Value.(string)
62+
// ActionTaskStarted / ActionTaskProgress / ActionTaskCompleted broadcast on IPC
63+
```
64+
65+
`Progress` updates a running task:
66+
67+
```go
68+
c.Progress(taskID, 0.5, "halfway done", "agentic.dispatch")
69+
```
70+
71+
## Composition — Tasks
72+
73+
A `Task` is a named sequence of action steps:
74+
75+
```go
76+
c.Task("agent.completion", core.Task{
77+
Steps: []core.Step{
78+
{Action: "agentic.qa"},
79+
{Action: "agentic.auto-pr"},
80+
{Action: "agentic.verify", Input: "previous"},
81+
{Action: "agentic.poke", Async: true},
82+
},
83+
})
84+
85+
r := c.Task("agent.completion").Run(ctx, c, opts)
86+
```
87+
88+
Sync steps run sequentially — failure stops the chain. Async steps fire-and-forget.
89+
`Input: "previous"` pipes the last sync step's output as `_input` on the next.
90+
91+
## Why named actions
92+
93+
The registry IS the capability map. Auditable, testable, swappable. Permission
94+
boundaries (entitlements) and observability (broadcast events) attach at a single
95+
choke point — `Action.Run` — instead of being threaded through every call site.
96+
97+
## Source
98+
99+
[`action.go`](https://github.com/dappcore/go/blob/main/action.go) — full
100+
implementation including the panic recovery, entitlement check, and Task runner.

src/content/docs/go/error.mdx

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
---
2+
title: Errors
3+
description: Structured errors with operation context, stable codes, and uniform introspection helpers.
4+
---
5+
6+
`core/go` ships its own structured error type — `*core.Err` — and constructors
7+
that always return it. Every introspection helper works uniformly across every
8+
constructor.
9+
10+
```go
11+
type Err struct {
12+
Operation string // hierarchical operation path: "agent.dispatch.commit"
13+
Message string
14+
Cause error
15+
Code string // stable error code (see codespace below)
16+
}
17+
```
18+
19+
## Construct
20+
21+
| Constructor | Use |
22+
|---|---|
23+
| `core.E(op, msg, err)` | The standard structured error. |
24+
| `core.NewError(text)` | Simple message. Returns `*Err` so introspection works. |
25+
| `core.NewCode(code, msg)` | Sentinel error with a stable code. |
26+
| `core.Wrap(err, op, msg)` | Wrap a cause with operation context. Preserves wrapped Code. |
27+
| `core.WrapCode(err, code, op, msg)` | Wrap with explicit code. |
28+
29+
```go
30+
return core.E("user.Save", "failed to save", err)
31+
32+
var ErrNotFound = core.NewCode("NOT_FOUND", "resource not found")
33+
34+
return core.Wrap(err, "db.Query", "database query failed")
35+
```
36+
37+
## Introspect
38+
39+
| Helper | Returns |
40+
|---|---|
41+
| `core.Operation(err)` | The operation path (or `""`) |
42+
| `core.ErrorCode(err)` | The stable code (or `""`) |
43+
| `core.ErrorMessage(err)` | Just the message (or `""`) |
44+
| `core.Root(err)` | The deepest cause in the chain |
45+
| `core.StackTrace(err)` | Operation chain as `[]string` outer→inner |
46+
| `core.FormatStackTrace(err)` | Same chain rendered as `"outer -> inner"` |
47+
| `core.AllOperations(err)` | Iterator over each operation in the chain |
48+
49+
## Stdlib bridges
50+
51+
| | |
52+
|---|---|
53+
| `core.Is(err, target)` | Wraps `errors.Is` |
54+
| `core.As(err, &target)` | Wraps `errors.As` |
55+
| `core.ErrorJoin(...)` | Wraps `errors.Join` |
56+
57+
## Stable code-space
58+
59+
Codes are a flat keyspace agents grep on. Use `Result.Code()` to dispatch:
60+
61+
```go
62+
switch r.Code() {
63+
case "fs.notfound": firstRun()
64+
case "http.timeout": retry()
65+
case "http.refused": fallback()
66+
case "json.invalid": malformedInput()
67+
case "crypto.algo.unsupported": badAlgoConfig()
68+
case "action.disabled": capabilityOff()
69+
case "action.notentitled": permissionDenied()
70+
}
71+
```
72+
73+
Conventions:
74+
75+
- `<package>.<failure>``fs.notfound`, `http.timeout`
76+
- `<package>.<feature>.<failure>``crypto.algo.unsupported`, `agent.token.expired`
77+
- `action.disabled`, `action.notentitled` — runtime capability gates
78+
79+
The full list lives in [`AGENTS.md` on the repo](https://github.com/dappcore/go/blob/main/AGENTS.md).
80+
81+
## Why one structured shape
82+
83+
`Operation`, `ErrorCode`, `ErrorMessage`, `Root`, `StackTrace`, `As` — every
84+
helper assumes `*Err`. Because every constructor returns `*Err`, the helpers
85+
work without hedging. Plain `errors.New` would silently bypass the introspection;
86+
our `NewError` doesn't.
87+
88+
## Source
89+
90+
[`error.go`](https://github.com/dappcore/go/blob/main/error.go) — type
91+
definition, constructor set, introspection helpers.

src/content/docs/go/options.mdx

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
---
2+
title: Options
3+
description: The universal input type — typed key/value bag every Core operation accepts.
4+
---
5+
6+
`Options` is the universal input type. Every Core operation that takes parameters
7+
takes one. A structured collection of key/value pairs with typed accessors.
8+
9+
```go
10+
type Option struct {
11+
Key string
12+
Value any
13+
}
14+
15+
type Options struct { /* opaque */ }
16+
```
17+
18+
## Construct
19+
20+
```go
21+
opts := core.NewOptions(
22+
core.Option{Key: "name", Value: "brain"},
23+
core.Option{Key: "port", Value: 8080},
24+
core.Option{Key: "debug", Value: true},
25+
)
26+
```
27+
28+
## Mutate
29+
30+
| Method | What it does |
31+
|---|---|
32+
| `opts.Set(key, v)` | Add or update a key. Mutates in place. |
33+
34+
## Read — typed accessors
35+
36+
Each accessor returns the type's zero value when the key is missing or the stored
37+
value isn't assignable to that type. This is the convenience surface; for strict
38+
checks use `opts.Get(key)` and inspect the `Result`.
39+
40+
| Accessor | Returns | Zero |
41+
|---|---|---|
42+
| `opts.String(key)` | `string` | `""` |
43+
| `opts.Int(key)` | `int` | `0` |
44+
| `opts.Bool(key)` | `bool` | `false` |
45+
| `opts.Float64(key)` | `float64` | `0` (promotes int / int64 / float32) |
46+
| `opts.Duration(key)` | `core.Duration` | `0` (parses string via `ParseDuration`) |
47+
48+
```go
49+
name := opts.String("name")
50+
port := opts.Int("port")
51+
debug := opts.Bool("debug")
52+
weight := opts.Float64("weight")
53+
timeout := opts.Duration("timeout")
54+
```
55+
56+
## Read — Result-shaped
57+
58+
For strict reads where missing-vs-typed-zero matters:
59+
60+
```go
61+
r := opts.Get("port")
62+
if !r.OK { return core.Fail(core.NewError("port required")) }
63+
port := r.Value.(int)
64+
```
65+
66+
## Inspect
67+
68+
| Method | What it returns |
69+
|---|---|
70+
| `opts.Has(key)` | `true` if the key exists, regardless of value type |
71+
| `opts.Len()` | Number of options |
72+
| `opts.Items()` | Copy of the underlying option slice — for iteration |
73+
74+
## Source
75+
76+
[`options.go`](https://github.com/dappcore/go/blob/main/options.go) — full type
77+
definition, accessor implementations, and docstrings with examples.

0 commit comments

Comments
 (0)