From 87f53ad8dd9fb3f012e1cdecadd6ea896d0db9bb Mon Sep 17 00:00:00 2001 From: Snider Date: Wed, 25 Mar 2026 15:34:24 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20v0.7.0=20implementation=20plan=20?= =?UTF-8?q?=E2=80=94=20align=20with=20core/go=20v0.8.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 7-step plan to update factory signature, register process.* Actions, remove global singleton, and align with Startable returning Result. Written with full core/go domain context from RFC implementation session. Co-Authored-By: Virgil --- .../plans/2026-03-25-v0.7.0-core-alignment.md | 151 ++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 docs/plans/2026-03-25-v0.7.0-core-alignment.md diff --git a/docs/plans/2026-03-25-v0.7.0-core-alignment.md b/docs/plans/2026-03-25-v0.7.0-core-alignment.md new file mode 100644 index 0000000..b4b2e93 --- /dev/null +++ b/docs/plans/2026-03-25-v0.7.0-core-alignment.md @@ -0,0 +1,151 @@ +# go-process v0.7.0 — Core Alignment + +> Written by Cladius with full core/go domain context (2026-03-25). +> Read core/go docs/RFC.md Section 17 for the full Process primitive spec. + +## What Changed in core/go + +core/go v0.8.0 added: +- `c.Process()` — primitive that delegates to `c.Action("process.*")` +- `c.Action("name")` — named action registry with panic recovery +- `Startable.OnStartup()` returns `core.Result` (not `error`) +- `Registry[T]` — universal thread-safe named collection +- `core.ID()` — unique identifier primitive + +go-process needs to align its factory signature and register process Actions. + +## Step 1: Fix Factory Signature + +Current (`service.go`): +```go +func NewService(opts Options) func(*core.Core) (any, error) { +``` + +Target: +```go +func Register(c *core.Core) core.Result { + svc := &Service{ + ServiceRuntime: core.NewServiceRuntime(c, Options{}), + processes: make(map[string]*ManagedProcess), + } + return core.Result{Value: svc, OK: true} +} +``` + +This matches `core.WithService(process.Register)` — the standard pattern. + +## Step 2: Register Process Actions During OnStartup + +```go +func (s *Service) OnStartup(ctx context.Context) core.Result { + c := s.Core() + + // Register named actions — these are what c.Process() calls + c.Action("process.run", s.handleRun) + c.Action("process.start", s.handleStart) + c.Action("process.kill", s.handleKill) + + return core.Result{OK: true} +} +``` + +Note: `OnStartup` now returns `core.Result` not `error`. + +## Step 3: Implement Action Handlers + +```go +func (s *Service) handleRun(ctx context.Context, opts core.Options) core.Result { + command := opts.String("command") + args, _ := opts.Get("args").Value.([]string) + dir := opts.String("dir") + env, _ := opts.Get("env").Value.([]string) + + // Use existing RunWithOptions internally + out, err := s.RunWithOptions(ctx, RunOptions{ + Command: command, + Args: args, + Dir: dir, + Env: env, + }) + if err != nil { + return core.Result{Value: err, OK: false} + } + return core.Result{Value: out, OK: true} +} + +func (s *Service) handleStart(ctx context.Context, opts core.Options) core.Result { + // Detached process — returns handle ID + command := opts.String("command") + args, _ := opts.Get("args").Value.([]string) + + handle, err := s.Start(ctx, StartOptions{ + Command: command, + Args: args, + Dir: opts.String("dir"), + Detach: opts.Bool("detach"), + }) + if err != nil { + return core.Result{Value: err, OK: false} + } + return core.Result{Value: handle.ID, OK: true} +} + +func (s *Service) handleKill(ctx context.Context, opts core.Options) core.Result { + id := opts.String("id") + pid := opts.Int("pid") + + if id != "" { + return s.KillByID(id) + } + return s.KillByPID(pid) +} +``` + +## Step 4: Remove Global Singleton Pattern + +Current: `process.SetDefault(svc)` and `process.Default()` global state. + +Target: Service registered in Core's conclave. No global state. + +The `ensureProcess()` hack in core/agent exists because go-process doesn't register properly. Once this is done, that bridge can be deleted. + +## Step 5: Update OnShutdown + +```go +func (s *Service) OnShutdown(ctx context.Context) core.Result { + // Kill all managed processes + for _, p := range s.processes { + p.Kill() + } + return core.Result{OK: true} +} +``` + +## Step 6: Use core.ID() for Process IDs + +Current: `fmt.Sprintf("proc-%d", s.idCounter.Add(1))` + +Target: `core.ID()` — consistent format across ecosystem. + +## Step 7: AX-7 Tests + +All tests renamed to `TestFile_Function_{Good,Bad,Ugly}`: +- `TestService_Register_Good` — factory returns Result +- `TestService_HandleRun_Good` — runs command via Action +- `TestService_HandleRun_Bad` — command not found +- `TestService_HandleKill_Good` — kills by ID +- `TestService_OnStartup_Good` — registers Actions +- `TestService_OnShutdown_Good` — kills all processes + +## What This Unlocks + +Once go-process v0.7.0 ships: +- `core.New(core.WithService(process.Register))` — standard registration +- `c.Process().Run(ctx, "git", "log")` — works end-to-end +- core/agent deletes `proc.go`, `ensureProcess()`, `ProcessRegister` +- Tests can mock process execution by registering a fake handler + +## Dependencies + +- core/go v0.8.0 (already done — Action system, Process primitive, Result lifecycle) +- No other deps change