go/docs/RFC.implementation.3.md
Snider 9cd83daaae feat: 6 implementation plans for v0.8.0
Plan 1: Critical bug fixes (v0.7.1, zero breakage)
  - ACTION chain, panic recovery, defer shutdown, stale code removal

Plan 2: Registry[T] primitive (Section 20)
  - Foundation brick, migration of 5 internal registries

Plan 3: Action/Task system (Section 18)
  - Named callables, task composition, cascade fix

Plan 4: c.Process() primitive (Section 17)
  - go-process v0.7.0, proc.go migration, atomic writes

Plan 5: Missing primitives + AX-7
  - core.ID(), ValidateName, WriteAtomic, RunE(), test coverage

Plan 6: Ecosystem sweep (Phase 3)
  - 44 repos in 5 batches, Codex dispatch with RFC as spec

Each plan lists: files to change, code examples, what it resolves,
dependencies on other plans, and migration strategy.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 13:31:11 +00:00

98 lines
2.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Implementation Plan 3 — Action/Task System (Section 18)
> Depends on: Plan 2 (Registry). The execution primitive.
## Phase A: Action Registry
**New file:** `action.go` (renamed from `task.go`)
```go
type ActionDef struct {
Name string
Handler ActionHandler
Description string
Schema Options // expected input keys
enabled bool // for Disable()
}
type ActionHandler func(context.Context, Options) Result
```
**Core accessor:**
```go
// Dual-purpose (like Service):
c.Action("process.run", handler) // register
c.Action("process.run").Run(ctx, opts) // invoke
c.Action("process.run").Exists() // check
c.Action("process.run").Def() // metadata
```
Internally uses `Registry[*ActionDef]`.
## Phase B: Move Registration to IPC
Move from `task.go`:
- `RegisterAction``ipc.go` (registers in `Registry[ActionHandler]`)
- `RegisterActions``ipc.go`
- `RegisterTask``ipc.go`
Keep in `action.go`:
- `Perform` → becomes `c.Action("name").Run()`
- `PerformAsync` → becomes `c.Action("name").RunAsync()`
- `Progress` → stays
## Phase C: Panic Recovery on All Actions
Every `Action.Run()` wraps in recover:
```go
func (a *ActionDef) Run(ctx context.Context, opts Options) (result Result) {
defer func() {
if r := recover(); r != nil {
result = Result{E("action.Run", Sprint("panic: ", r), nil), false}
}
}()
return a.Handler(ctx, opts)
}
```
## Phase D: Task Composition (v0.8.0 stretch)
```go
type TaskDef struct {
Name string
Steps []Step
}
type Step struct {
Action string
With Options
Async bool // run in background
Input string // "previous" = output of last step
}
```
`c.Task("name", def)` registers. `c.Task("name").Run(ctx)` executes steps in order.
## Resolves
I16 (task.go concerns), P6-1 (cascade → Task pipeline), P6-2 (O(N×M) → direct dispatch), P7-3 (panic recovery), P7-8 (circuit breaker via Disable), P10-2 (PERFORM not ACTION for request/response).
## Migration in core/agent
After Actions exist, refactor `handlers.go`:
```go
// Current: nested c.ACTION() cascade
// Target:
c.Task("agent.completion", TaskDef{
Steps: []Step{
{Action: "agentic.qa"},
{Action: "agentic.auto-pr"},
{Action: "agentic.verify"},
{Action: "agentic.ingest", Async: true},
{Action: "agentic.poke", Async: true},
},
})
```