From 390b392decc66be09022eeeb222f7480d1f67da9 Mon Sep 17 00:00:00 2001 From: Snider Date: Wed, 25 Mar 2026 16:35:33 +0000 Subject: [PATCH] =?UTF-8?q?chore:=20remove=20completed=20implementation=20?= =?UTF-8?q?plans=20=E2=80=94=20RFC.md=20is=20the=20single=20source?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plans 1-5 are implemented. Plan 6 (ecosystem sweep) is in consumer RFCs. RFC.plan.md, RFC.plan.1.md, RFC.plan.2.md served their purpose as session continuity docs — the work they described is done. RFC.md (1935 lines) is now the single source of truth for core/go. Removed: - RFC.implementation.{1-6}.md - RFC.plan.md, RFC.plan.1.md, RFC.plan.2.md Co-Authored-By: Virgil --- docs/RFC.implementation.1.md | 78 ---------------------- docs/RFC.implementation.2.md | 65 ------------------- docs/RFC.implementation.3.md | 98 ---------------------------- docs/RFC.implementation.4.md | 100 ----------------------------- docs/RFC.implementation.5.md | 121 ----------------------------------- docs/RFC.implementation.6.md | 83 ------------------------ docs/RFC.plan.1.md | 85 ------------------------ docs/RFC.plan.2.md | 43 ------------- docs/RFC.plan.md | 110 ------------------------------- 9 files changed, 783 deletions(-) delete mode 100644 docs/RFC.implementation.1.md delete mode 100644 docs/RFC.implementation.2.md delete mode 100644 docs/RFC.implementation.3.md delete mode 100644 docs/RFC.implementation.4.md delete mode 100644 docs/RFC.implementation.5.md delete mode 100644 docs/RFC.implementation.6.md delete mode 100644 docs/RFC.plan.1.md delete mode 100644 docs/RFC.plan.2.md delete mode 100644 docs/RFC.plan.md diff --git a/docs/RFC.implementation.1.md b/docs/RFC.implementation.1.md deleted file mode 100644 index 1794736..0000000 --- a/docs/RFC.implementation.1.md +++ /dev/null @@ -1,78 +0,0 @@ -# Implementation Plan 1 — Critical Bug Fixes (Phase 1) - -> Ship as v0.7.1. Zero consumer breakage. Fix the 3 critical bugs. - -## P4-3: ACTION !OK Stops Broadcast Chain - -**File:** `ipc.go` -**Change:** ACTION dispatch must call ALL handlers regardless of individual results. - -```go -// Current (broken): -for _, h := range handlers { - if r := h(c, msg); !r.OK { - return r // STOPS — remaining handlers never called - } -} - -// Fix: -for _, h := range handlers { - h(c, msg) // call all, ignore individual results for broadcast -} -``` - -**Test:** `TestIpc_Action_Ugly` — register 3 handlers, second returns !OK, verify third still fires. - -## P7-2: No Cleanup on Startup Failure - -**File:** `core.go` — `Run()` -**Change:** Call ServiceShutdown before exit on startup failure. - -```go -// Current: -if !r.OK { os.Exit(1) } - -// Fix: -if !r.OK { - c.ServiceShutdown(context.Background()) - os.Exit(1) -} -``` - -## P7-3: ACTION Handlers No Panic Recovery - -**File:** `ipc.go` -**Change:** Wrap each handler in defer/recover. - -```go -for _, h := range handlers { - func() { - defer func() { - if r := recover(); r != nil { - Error("ACTION handler panicked", "panic", r) - } - }() - h(c, msg) - }() -} -``` - -**Test:** `TestIpc_Action_Ugly` — handler that panics, verify other handlers still execute. - -## P7-4: Run() Needs defer ServiceShutdown - -**File:** `core.go` -**Change:** Add defer as first line of Run. - -```go -func (c *Core) Run() { - defer c.ServiceShutdown(context.Background()) - // ... rest unchanged, but remove os.Exit calls -} -``` - -## Additional Phase 1 (safe) - -- **I3:** Remove `Embed()` accessor (0 consumers) -- **I15:** Fix stale comment on `New()` — update to show `*Core` return -- **P9-1:** Remove `os/exec` import from `app.go` — move `App.Find()` to go-process diff --git a/docs/RFC.implementation.2.md b/docs/RFC.implementation.2.md deleted file mode 100644 index 8631fb2..0000000 --- a/docs/RFC.implementation.2.md +++ /dev/null @@ -1,65 +0,0 @@ -# Implementation Plan 2 — Registry[T] Primitive (Section 20) - -> Foundation brick. Most other plans depend on this. - -## The Type - -**New file:** `registry.go` - -```go -type Registry[T any] struct { - items map[string]T - order []string // insertion order (fixes P4-1) - mu sync.RWMutex - mode registryMode // open, sealed, locked -} - -type registryMode int -const ( - registryOpen registryMode = iota // anything goes - registrySealed // update existing, no new keys - registryLocked // fully frozen -) -``` - -## Methods - -```go -func NewRegistry[T any]() *Registry[T] -func (r *Registry[T]) Set(name string, item T) Result -func (r *Registry[T]) Get(name string) Result -func (r *Registry[T]) Has(name string) bool -func (r *Registry[T]) Names() []string // insertion order -func (r *Registry[T]) List(pattern string) []T // glob match -func (r *Registry[T]) Each(fn func(string, T)) -func (r *Registry[T]) Len() int -func (r *Registry[T]) Delete(name string) Result -func (r *Registry[T]) Disable(name string) // soft — exists but skipped -func (r *Registry[T]) Lock() // fully frozen -func (r *Registry[T]) Seal() // no new, updates OK -func (r *Registry[T]) Open() // default -``` - -## Migration - -Replace internal registries one at a time: - -1. `serviceRegistry` → `ServiceRegistry` embedding `Registry[*Service]` -2. `commandRegistry` → `CommandRegistry` embedding `Registry[*Command]` -3. `Drive.handles` → embed `Registry[*DriveHandle]` -4. `Data.mounts` → embed `Registry[*Embed]` -5. `Lock.locks` → `Registry[*sync.RWMutex]` (fixes P4-8 allocation) - -Each migration is a separate commit. Tests before and after. - -## Core Accessor - -```go -func (c *Core) Registry(name string) *Registry[any] -``` - -Returns named registries for cross-cutting queries. - -## Resolves - -P4-1 (startup order), P4-8 (lock allocation), I6 (serviceRegistry unexported), I12 (Ipc data-only), I13 (Lock allocation), I14 (Startables return type). diff --git a/docs/RFC.implementation.3.md b/docs/RFC.implementation.3.md deleted file mode 100644 index e3af066..0000000 --- a/docs/RFC.implementation.3.md +++ /dev/null @@ -1,98 +0,0 @@ -# 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}, - }, -}) -``` diff --git a/docs/RFC.implementation.4.md b/docs/RFC.implementation.4.md deleted file mode 100644 index 6018ead..0000000 --- a/docs/RFC.implementation.4.md +++ /dev/null @@ -1,100 +0,0 @@ -# Implementation Plan 4 — c.Process() Primitive (Section 17) - -> Depends on: Plan 2 (Registry), Plan 3 (Actions). -> go-process v0.7.0 update required. - -## Phase A: Update go-process to v0.7.0 Core API - -**Repo:** core/go-process - -1. Change `NewService` factory to return `core.Result` (not `(any, error)`) -2. Add `Register` function matching `WithService` signature -3. Remove `ProcessRegister` bridge from core/agent -4. Register process Actions during OnStartup: - -```go -func (s *Service) OnStartup(ctx context.Context) core.Result { - c := s.Core() - c.Action("process.run", s.handleRun) - c.Action("process.start", s.handleStart) - c.Action("process.kill", s.handleKill) - return core.Result{OK: true} -} -``` - -## Phase B: Add Process Primitive to core/go - -**New file in core/go:** `process.go` - -```go -type Process struct { - core *Core -} - -func (c *Core) Process() *Process { return c.process } - -func (p *Process) Run(ctx context.Context, command string, args ...string) Result { - return p.core.Action("process.run").Run(ctx, NewOptions( - Option{Key: "command", Value: command}, - Option{Key: "args", Value: args}, - )) -} - -func (p *Process) RunIn(ctx context.Context, dir, command string, args ...string) Result { - return p.core.Action("process.run").Run(ctx, NewOptions( - Option{Key: "command", Value: command}, - Option{Key: "args", Value: args}, - Option{Key: "dir", Value: dir}, - )) -} -``` - -No new dependencies in core/go — Process is just Action sugar. - -## Phase C: Migrate core/agent proc.go - -Replace standalone helpers with Core methods: - -```go -// Current: proc.go standalone with ensureProcess() hack -out, err := runCmd(ctx, dir, "git", "log") - -// Target: method on PrepSubsystem using Core -out := s.core.Process().RunIn(ctx, dir, "git", "log") -``` - -Delete `proc.go`. Delete `ensureProcess()`. Delete `ProcessRegister`. - -## Phase D: Replace syscall.Kill calls - -5 call sites in core/agent use `syscall.Kill(pid, 0)` and `syscall.Kill(pid, SIGTERM)`. - -Replace with: -```go -processIsRunning(st.ProcessID, st.PID) // already in proc.go -processKill(st.ProcessID, st.PID) // already in proc.go -``` - -These use `process.Get(id).IsRunning()` when ProcessID is available. - -## Phase E: Atomic File Writes (P4-10) - -Add to core/go `fs.go`: - -```go -func (m *Fs) WriteAtomic(p, content string) Result { - tmp := p + ".tmp." + strconv.FormatInt(time.Now().UnixNano(), 36) - if r := m.Write(tmp, content); !r.OK { return r } - if err := os.Rename(tmp, m.path(p)); err != nil { - m.Delete(tmp) - return Result{err, false} - } - return Result{OK: true} -} -``` - -Migrate `writeStatus` in core/agent to use `WriteAtomic`. Fixes P4-9 (51 race sites). - -## Resolves - -I7 (no c.Process), P9-1 (os/exec in core), P4-9 (status.json race), P4-10 (Fs.Write not atomic), P11-2 (unsafe.Pointer Fs bypass — add Fs.NewUnrestricted instead). diff --git a/docs/RFC.implementation.5.md b/docs/RFC.implementation.5.md deleted file mode 100644 index e94ab33..0000000 --- a/docs/RFC.implementation.5.md +++ /dev/null @@ -1,121 +0,0 @@ -# Implementation Plan 5 — Missing Primitives + AX-7 (Phase 1-2) - -> Independent of Plans 2-4. Can ship alongside or before. - -## core.ID() — Unique Identifier - -**File:** `utils.go` or new `id.go` - -```go -var idCounter atomic.Uint64 - -func ID() string { - return Sprintf("id-%d-%s", idCounter.Add(1), shortRand()) -} - -func shortRand() string { - b := make([]byte, 3) - crand.Read(b) - return hex.EncodeToString(b) -} -``` - -Replaces 3 different patterns: crypto/rand hex, atomic counter, fmt.Sprintf. - -## core.ValidateName() / core.ValidatePath() - -**File:** `utils.go` or new `validate.go` - -```go -func ValidateName(name string) Result { - if name == "" || name == "." || name == ".." { - return Result{E("validate", "invalid name: "+name, nil), false} - } - if Contains(name, "/") || Contains(name, "\\") { - return Result{E("validate", "name contains path separator: "+name, nil), false} - } - return Result{name, true} -} - -func SanitisePath(path string) string { - safe := PathBase(path) - if safe == "." || safe == ".." || safe == "" { - return "invalid" - } - return safe -} -``` - -Replaces copy-pasted validation in prep.go, plan.go, command.go. - -## Fs.WriteAtomic() - -See Plan 4 Phase E. Fixes P4-9 and P4-10. - -## Fs.NewUnrestricted() - -**File:** `fs.go` - -```go -func (m *Fs) NewUnrestricted() *Fs { - return m.New("/") -} -``` - -Gives consumers a legitimate door instead of unsafe.Pointer crowbar. -Fixes P11-2. Add go vet / linter rule to flag unsafe.Pointer on Core types. - -## AX-7 for core/go - -Currently 14% AX-7 (83.6% statement coverage, wrong naming). - -Steps: -1. Run the rename script from core/agent (same Python script) -2. Gap analysis: find functions missing Good/Bad/Ugly -3. Fill gaps — 212 functions × 3 categories = 636 target -4. Currently 91/636 filled → need 545 more - -Prioritise by section: -- Section 3 (services): ServiceFor, RegisterService, Service -- Section 4 (IPC): Action, Query, RegisterAction -- Section 8 (Fs): Read, Write, WriteAtomic, Delete -- Section 5 (Config): Get, Set, Enable, ConfigGet - -## RunE() — Backwards-Compatible Run Fix - -**File:** `core.go` - -```go -func (c *Core) RunE() error { - defer c.ServiceShutdown(context.Background()) - - r := c.ServiceStartup(c.context, nil) - if !r.OK { - if err, ok := r.Value.(error); ok { - return err - } - return E("core.Run", "startup failed", nil) - } - - if cli := c.Cli(); cli != nil { - r = cli.Run() - } - - if !r.OK { - if err, ok := r.Value.(error); ok { - return err - } - } - return nil -} - -// Run keeps backwards compatibility -func (c *Core) Run() { - if err := c.RunE(); err != nil { - Error(err.Error()) - os.Exit(1) - } -} -``` - -Fixes P7-5 (os.Exit bypasses defer) without breaking 15 main.go files. diff --git a/docs/RFC.implementation.6.md b/docs/RFC.implementation.6.md deleted file mode 100644 index 7d5ba7a..0000000 --- a/docs/RFC.implementation.6.md +++ /dev/null @@ -1,83 +0,0 @@ -# Implementation Plan 6 — Ecosystem Sweep (Phase 3) - -> Depends on: Plans 1-5 complete. 44 repos need updating. -> Use Codex dispatch with RFC as the spec. - -## Breaking Changes - -### StartableV2 (if P3-1 adopted) - -```go -type StartableV2 interface { - OnStartup(ctx context.Context) Result -} -``` - -Core checks V2 first, falls back to V1. No breakage. Deprecation notice on V1. - -**Codex template:** "If this repo implements OnStartup returning error, add a V2 variant returning core.Result alongside it." - -### RunE() exists alongside Run() - -No breakage — Run() still works. New code uses RunE(). - -### Command.Managed replaces CommandLifecycle - -```go -// Old: -Command{Lifecycle: myLifecycle} - -// New: -Command{Managed: "process.daemon"} -``` - -**Codex template:** "If this repo uses Command.Lifecycle, replace with Managed field." - -## Per-Repo Sweep Checklist - -For each of 44 repos: - -1. [ ] Update `go.mod` to core v0.8.0 -2. [ ] Replace `OnStartup() error` with `OnStartup() Result` (if V2 adopted) -3. [ ] Replace `os/exec` with `c.Process()` calls -4. [ ] Replace manual `unsafe.Pointer` Fs hacks with `Fs.NewUnrestricted()` -5. [ ] Run AX-7 gap analysis, fill missing Good/Bad/Ugly -6. [ ] Rename tests to `TestFile_Function_{Good,Bad,Ugly}` -7. [ ] Add `llm.txt` and `AGENTS.md` -8. [ ] Verify `go test ./...` passes -9. [ ] Create PR to dev - -## Dispatch Strategy - -``` -Batch 1 (core packages — we control): - core/go, core/agent, core/mcp, go-process, go-io, go-log - -Batch 2 (utility packages): - go-config, go-git, go-scm, go-cache, go-store, go-session - -Batch 3 (domain packages): - go-forge, go-api, go-build, go-devops, go-container - -Batch 4 (application packages): - gui, cli, ts, php, ide, lint - -Batch 5 (research/experimental): - go-ai, go-ml, go-mlx, go-inference, go-rag, go-rocm -``` - -Each batch is one Codex sweep. Verify batch N before starting N+1. - -## Success Criteria for v0.8.0 - -- [ ] All 3 critical bugs fixed (Plan 1) -- [ ] Registry[T] implemented and migrated (Plan 2) -- [ ] Action system working (Plan 3 Phase A-C) -- [ ] c.Process() primitive working (Plan 4 Phase A-C) -- [ ] Missing primitives added (Plan 5) -- [ ] core/go AX-7 at 100% -- [ ] core/agent AX-7 at 100% -- [ ] Zero os/exec in core/go -- [ ] Zero unsafe.Pointer on Core types in ecosystem -- [ ] All Phase 1-2 changes shipped -- [ ] Phase 3 sweep at least started (batch 1-2) diff --git a/docs/RFC.plan.1.md b/docs/RFC.plan.1.md deleted file mode 100644 index ae709e8..0000000 --- a/docs/RFC.plan.1.md +++ /dev/null @@ -1,85 +0,0 @@ -# RFC Plan 1 — First Session Priorities (COMPLETED 2026-03-25) - -> All items shipped. See RFC.plan.md "What Was Shipped" section. - -## Priority 1: Fix the 3 Critical Bugs (Plan 1) - -These are one-line to five-line changes. Ship as v0.7.1. - -### Bug 1: ACTION stops on !OK (ipc.go line ~33) - -```go -// CURRENT (broken — handler 3 failing silences handlers 4 and 5): -for _, h := range handlers { - if r := h(c, msg); !r.OK { return r } -} - -// FIX: -for _, h := range handlers { - func() { - defer func() { if r := recover(); r != nil { Error("handler panic", "err", r) } }() - h(c, msg) - }() -} -``` - -This also fixes P7-3 (no panic recovery) in the same change. - -### Bug 2: Run() leaks on startup failure (core.go Run method) - -Add one line: -```go -func (c *Core) Run() { - defer c.ServiceShutdown(context.Background()) // ADD THIS - // ... rest unchanged -} -``` - -### Bug 3: Remove stale Embed() and fix comment - -Delete `func (c *Core) Embed() Result` from core.go. -Fix the `New()` comment to show `*Core` return. - -### Test all 3 with AX-7 naming: -``` -TestIpc_Action_Ugly_HandlerFailsChainContinues -TestIpc_Action_Ugly_HandlerPanicsChainContinues -TestCore_Run_Ugly_StartupFailureCallsShutdown -``` - -## Priority 2: AX-7 Rename for core/go - -Run the same Python rename script used on core/agent: - -```python -# Same script from core/agent session — applies to any Go package -# Changes TestFoo_Good to TestFile_Foo_Good -``` - -This is mechanical. No logic changes. Just naming. - -Then run gap analysis: -```bash -python3 -c "... same gap analysis script ..." -``` - -## Priority 3: Start Registry[T] (Plan 2) - -Create `registry.go` with the type. Write tests FIRST (AX-7 complete from day one): - -``` -TestRegistry_Set_Good -TestRegistry_Set_Bad -TestRegistry_Set_Ugly -TestRegistry_Get_Good -... -``` - -Then migrate `serviceRegistry` first (most tested, most used). - -## What Was Skipped (shipped in same session instead) - -All items originally marked "skip" were shipped because Registry and Actions were built in the same session: -- Plan 3 (Actions) — DONE: ActionDef, TaskDef, c.Action(), c.Task() -- Plan 4 (Process) — DONE for core/go: c.Process() sugar over Actions -- Breaking changes — DONE: Startable returns Result, CommandLifecycle removed diff --git a/docs/RFC.plan.2.md b/docs/RFC.plan.2.md deleted file mode 100644 index f891e23..0000000 --- a/docs/RFC.plan.2.md +++ /dev/null @@ -1,43 +0,0 @@ -# RFC Plan 2 — Registry + Actions Sessions (COMPLETED 2026-03-25) - -> All core/go items shipped. core/agent migration and go-process v0.7.0 are separate repo scope. - -## Session Goal: Registry[T] + First Migration - -1. Build `registry.go` with full AX-7 tests -2. Migrate `serviceRegistry` → `ServiceRegistry` embedding `Registry[*Service]` -3. Verify all existing tests still pass -4. Commit + push - -## Session Goal: Action System - -1. Rename `task.go` → `action.go` -2. Move `RegisterAction`/`RegisterActions`/`RegisterTask` to `ipc.go` -3. Build `ActionDef` type with `Run()`, `Exists()`, `Def()` -4. Wire `c.Action("name")` dual-purpose accessor -5. Full AX-7 tests -6. Commit + push - -## Session Goal: Migrate core/agent Handlers - -1. Register named Actions in `agentic.Register()` -2. Replace nested `c.ACTION()` cascade with Task pipeline -3. Test that queue drains properly after agent completion -4. This is the P6-1 fix — the queue starvation bug - -## Session Goal: c.Process() + go-process v0.7.0 - -1. Update go-process factory to return `core.Result` -2. Add `process.Register` direct factory -3. Remove `agentic.ProcessRegister` bridge -4. Add `Process` primitive to core/go (sugar over Actions) -5. Migrate core/agent `proc.go` → `s.core.Process()` calls -6. Delete `proc.go` and `ensureProcess()` - -## Between Sessions - -Each session should produce: -- Working code (all tests pass) -- A commit with conventional message -- Updated coverage numbers -- Any new findings added to RFC.md passes diff --git a/docs/RFC.plan.md b/docs/RFC.plan.md deleted file mode 100644 index 6376f34..0000000 --- a/docs/RFC.plan.md +++ /dev/null @@ -1,110 +0,0 @@ -# RFC Plan — How to Work With This Spec - -> For future Claude sessions. Read this FIRST before touching code. - -## What Exists - -- `docs/RFC.md` — 3,845-line API spec with 108 findings across 13 passes -- `docs/RFC.implementation.{1-6}.md` — ordered implementation plans -- `llm.txt` — agent entry point -- `CLAUDE.md` — session-specific instructions - -## The 108 Findings Reduce to 5 Root Causes - -1. **Type erasure** (16 findings) — `Result{Value: any}` loses compile-time safety. Mitigate with typed methods + AX-7 tests. Not fixable without abandoning Result. - -2. **No internal boundaries** (14 findings) — `*Core` grants God Mode. Solved by Section 21 (Entitlement primitive). v0.8.0 scope — designed, implementation pending. - -3. **Synchronous everything** (12 findings) — IPC dispatch blocks. ACTION cascade in core/agent blocks queue for minutes. Fixed by Action/Task system (Plan 3). - -4. **No recovery path** (10 findings) — `os.Exit` bypasses defer. No cleanup on failure. Fixed by Plan 1 (defer + RunE + panic recovery). - -5. **Missing primitives** (8 findings) — No ID, validation, health, atomic writes. Fixed by Plan 5. - -## Implementation Order - -``` -Plan 1 → v0.7.1 (ship immediately, zero breakage) -Plan 2 → Registry[T] (foundation — Plans 3-4 depend on this) -Plan 3 → Action/Task (execution primitive — Plan 4 depends on this) -Plan 4 → c.Process() (needs go-process v0.7.0 update first) -Plan 5 → Missing primitives + AX-7 (independent, do alongside 2-4) -Plan 6 → Ecosystem sweep (after 1-5, dispatched via Codex) -``` - -## 3 Critical Bugs — Fix First - -1. **P4-3:** `ipc.go` — ACTION handler returning `!OK` stops entire broadcast chain. Other handlers never fire. Fix: call all handlers, don't stop on failure. - -2. **P6-1:** core/agent `handlers.go` — Nested `c.ACTION()` calls create synchronous cascade 4 levels deep. QA → PR → Verify → Merge blocks Poke handler for minutes. Queue doesn't drain. Fix: replace with Task pipeline (needs Plan 3). - -3. **P7-2:** `core.go` — `Run()` calls `os.Exit(1)` on startup failure without calling `ServiceShutdown()`. Running services leak. Fix: add `defer c.ServiceShutdown()` + replace `os.Exit` with error return. - -## Key Design Decisions Already Made - -- **CamelCase = primitive** (brick), **UPPERCASE = convenience** (sugar) -- **Core is Lego bricks** — export the bricks, hide the safety mechanisms -- **Fs.root is the ONE exception** — security boundaries stay unexported -- **Registration IS permission** — no handler = no capability -- **`error` at Go interface boundary, `Result` at Core contract boundary** -- **Dual-purpose methods** (Service, Command, Action) — keep as sugar, Registry has explicit Get/Set -- **Array[T] and ConfigVar[T] are guardrail primitives** — model-proof, not speculative -- **ServiceRuntime[T] and manual `.core = c` are both valid** — document both -- **Startable returns Result** — clean break, no V2 compat shim (pre-v1, breaking is expected) -- **`RunE()` alongside `Run()`** — no breakage -- **CommandLifecycle removed** — replaced with `Command.Managed` string field - -## Existing RFCs That Solve Open Problems - -| Problem | RFC | Core Provides | Consumer Implements | -|---------|-----|---------------|-------------------| -| Permissions | RFC-004 Entitlements | `c.Entitlement()` interface | go-entitlements package | -| Config context | RFC-003 Config Channels | `c.Config()` with channel | config channel service | -| Secrets | RFC-012 SMSG | `c.Secret()` interface | go-smsg / env fallback | -| Validation | RFC-009 Sigil | Transform chain interface | validator implementations | -| Containers | RFC-014 TIM | `c.Fs()` sandbox | TIM = OS isolation | -| In-memory fs | RFC-013 DataNode | `c.Data()` mounts fs.FS | DataNode / Borg | -| Lazy startup | RFC-002 Event Modules | Event declaration | Lazy instantiation | - -Core stays stdlib-only. Consumers bring implementations via WithService. - -## What NOT to Do - -- Don't add dependencies to core/go (it's stdlib + go-io + go-log only) -- Don't use `os/exec` — go-process is the only allowed user (P9-1: core/go itself violates this in app.go — fix it) -- Don't use `unsafe.Pointer` on Core types — add legitimate APIs instead -- Don't call `os.Exit` inside Core — return errors, let main() exit -- Don't create global mutable state — use Core's Registry -- Don't auto-discover via reflect — use explicit registration (HandleIPCEvents is the last magic method) - -## AX-7 Status - -- core/agent: 92% (840 tests, 79.9% coverage) -- core/go: **100%** (457 tests, 84.4% coverage) — renamed 2026-03-25 -- All 457 tests have `TestFile_Function_{Good,Bad,Ugly}` naming - -## What Was Shipped (2026-03-25 session) - -Plans 1-5 complete for core/go scope. 457 tests, 84.4% coverage, 100% AX-7 naming. - -- P4-3 + P7-3: ACTION broadcast — calls all handlers, panic recovery per handler -- P7-2 + P7-4: `RunE()` with `defer ServiceShutdown`, `Run()` delegates -- P3-1: Startable/Stoppable return `Result` (breaking, clean — no V2) -- P9-1: Zero `os/exec` in core/go — `App.Find()` rewritten with `os.Stat` + PATH -- P11-2: `Fs.NewUnrestricted()` — legitimate door replaces unsafe.Pointer -- P4-10: `Fs.WriteAtomic()` — write-to-temp-then-rename -- I3: `Embed()` removed, I15: `New()` comment fixed -- I9: `CommandLifecycle` interface removed → `Command.Managed` string field -- Section 17: `c.Process()` primitive (Action sugar, no deps) -- Section 18: `c.Action("name")` + `ActionDef` + `c.Task("name", TaskDef{Steps})` composition -- Section 20: `Registry[T]` + all 5 migrations (services, commands, drive, data, lock) -- Section 20.4: `c.RegistryOf("name")` cross-cutting accessor -- Plan 5: `core.ID()`, `ValidateName()`, `SanitisePath()` - -## Session Context That Won't Be In Memory - -- The ACTION cascade (P6-1) — core/go now has TaskDef for the fix, core/agent needs to wire it -- status.json has 51 unprotected read-modify-write sites (P4-9) — `WriteAtomic` exists, core/agent needs to use it -- `core.Env("DIR_HOME")` is cached at init — `t.Setenv` doesn't override it (P2-5) — use `CORE_WORKSPACE` in tests -- go-process `NewService` returns `(any, error)` not `core.Result` — needs v0.7.0 update (go-process repo) -- Multiple Core instances share global state (assetGroups, systemInfo, defaultLog)