From da2e5477ea3dad17ffabdb808c7aff66d82df806 Mon Sep 17 00:00:00 2001 From: Snider Date: Wed, 25 Mar 2026 16:57:31 +0000 Subject: [PATCH] =?UTF-8?q?fix(rfc):=20pass=205=20=E2=80=94=20PERFORM?= =?UTF-8?q?=E2=86=92Actions,=20Entitlement=E2=86=92Entitled,=20RunE,=20Tas?= =?UTF-8?q?k=20rename?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AX principle 5: PERFORM removed, now ACTION/QUERY + named Actions - Findings table: TaskDef → Task - Root Cause 2: removed stale v0.8.0 framing, Entitlement→Entitled method name - Root Cause 3: TaskDef→Task, linked to core/agent RFC not deleted plan file - Root Cause 4: Run()→RunE() in code example - Root Cause 5: Updated to show resolved items vs open items - Fixed triple blank line, cleaned whitespace Co-Authored-By: Virgil --- docs/RFC.md | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/docs/RFC.md b/docs/RFC.md index 3ba015d..8ce8e2b 100644 --- a/docs/RFC.md +++ b/docs/RFC.md @@ -971,14 +971,12 @@ This API follows RFC-025 Agent Experience (AX): 2. **Usage-example comments** — every public function shows HOW with real values 3. **Path is documentation** — `c.Data().ReadString("prompts/coding.md")` 4. **Universal types** — Option, Options, Result everywhere -5. **Event-driven** — ACTION/QUERY/PERFORM, not direct function calls between services +5. **Event-driven** — ACTION/QUERY broadcast + named Actions, not direct function calls between services 6. **Tests as spec** — `TestFile_Function_{Good,Bad,Ugly}` for every function 7. **Export primitives** — Core is Lego bricks, not an encapsulated library 8. **Naming encodes architecture** — CamelCase = primitive brick, UPPERCASE = consumer convenience 9. **File = concern** — one file, one job (ipc.go = registry, action.go = execution, contract.go = types) - - ## Findings Summary — 108 Findings Across 13 Passes (All Resolved) The full discovery process (Passes 2-13) produced 108 findings that reduce to 5 root causes. @@ -990,7 +988,7 @@ All findings are resolved in v0.8.0. Full pass detail preserved in git history. | 3 | Spec contradictions | 8 | Startable returns Result, Process returns Result, Registry lock modes | | 4 | Concurrency | 12 | Registry[T] per-subsystem mutex, WriteAtomic, PerformAsync shutdown race | | 5 | Consumer experience | 8 | ServiceRuntime + manual .core both valid, HandleIPCEvents documented | -| 6 | Cross-repo cascade | 8 | TaskDef replaces nested ACTION cascade (P6-1), MCP aggregator pattern | +| 6 | Cross-repo cascade | 8 | Task replaces nested ACTION cascade (P6-1), MCP aggregator pattern | | 7 | Failure modes | 8 | RunE() + defer shutdown, panic recovery in ACTION/Action, SafeGo | | 8 | Type safety | 8 | Result trade-off accepted, typed convenience methods, AX-7 Ugly tests | | 9 | Missing primitives | 8 | core.ID(), ValidateName, WriteAtomic, Fs.NewUnrestricted, c.Process() | @@ -1016,9 +1014,7 @@ All findings are resolved in v0.8.0. Full pass detail preserved in git history. `*Core` grants God Mode. Every service sees everything. The unexported fields were an attempt at boundaries but `unsafe.Pointer` proves they don't work. The conclave has no isolation. -**This is by design for v0.8.0.** All services are first-party trusted code. The Lego Bricks philosophy says "export everything." The tension is: Lego Bricks vs Least Privilege. - -**Resolution:** Section 21 (Entitlement primitive) — implemented. `c.Entitled()` gates Actions. Default permissive, consumer replaces checker. Port of RFC-004 concept: +**Resolution:** Section 21 (Entitlement primitive). `c.Entitled()` gates Actions. Default permissive (trusted conclave). Consumer packages replace the checker for SaaS/commerce gating. Port of RFC-004: ``` Registration = capability ("process.run action exists") @@ -1026,7 +1022,7 @@ Entitlement = permission ("this Core is ALLOWED to run processes") ``` ```go -c.Entitlement("process.run") // true if both registered AND permitted +c.Entitled("process.run") // true if both registered AND permitted c.Action("process.run").Run() // checks entitlement before executing ``` @@ -1040,7 +1036,7 @@ IPC dispatch is synchronous. Startup is synchronous. File I/O assumes no concurr **The cascade (P6-1) is the symptom.** The root cause is that Core was designed for sequential execution and concurrency was added incrementally without revisiting the foundations. -**Resolution:** The Action/Task system (Section 18) is implemented in core/go. `TaskDef` with `Steps` supports sequential chains, async dispatch, and previous-input piping. The cascade fix requires core/agent to wire its handlers as named Actions and replace the nested `c.ACTION()` calls with `c.Task("agent.completion").Run()`. See `core/agent/docs/plans/2026-03-25-core-go-v0.8.0-migration.md` Priority 5. +**Resolution:** The Action/Task system (Section 18) is implemented. `Task` with `Steps` supports sequential chains, async dispatch, and previous-input piping. The cascade fix requires core/agent to wire its handlers as named Actions and replace nested `c.ACTION()` calls with `c.Task("agent.completion").Run(ctx, c, opts)`. See `core/agent/docs/RFC.md` Section 3. ### Root Cause 4: No Recovery Path — 10 findings @@ -1049,24 +1045,25 @@ Every failure mode is "log and crash." `os.Exit(1)` bypasses defers. Startup fai **One fix resolves most of this cluster:** ```go -func (c *Core) Run() error { +func (c *Core) RunE() error { defer c.ServiceShutdown(context.Background()) - // ... no os.Exit, return errors + // ... returns error, no os.Exit } ``` -`defer` ensures cleanup always runs. Returning `error` lets `main()` handle the exit. Panic recovery in ACTION handlers prevents cascade crashes. Wire `SafeGo` as the standard goroutine launcher. +`defer` ensures cleanup always runs. `RunE()` returns `error`; `Run()` delegates and exits. Panic recovery in ACTION handlers and Action.Run() prevents cascade crashes. ### Root Cause 5: Missing Primitives — 8 findings -The guardrail coverage is incomplete. Strings have primitives. Paths have primitives. Errors have primitives. But JSON, time, IDs, validation, and health don't. Each gap means consumers reinvent the wheel — and weaker models get it wrong. +The guardrail coverage was incomplete. Strings have primitives. Paths have primitives. Errors have primitives. IDs, validation, and atomic writes didn't. -**Resolution:** Prioritise by usage frequency: -1. `core.ID()` — used everywhere, 3 different patterns today -2. `core.Validate(name/path)` — copy-pasted 3 times today -3. `core.Health()` — needed for production monitoring -4. `core.Time()` / timestamp convention — document RFC3339 -5. JSON — judgment call, may be unnecessary wrapping +**Resolved:** +- `core.ID()` — unique identifier (atomic counter + crypto/rand) +- `core.ValidateName()` / `core.SanitisePath()` — reusable validation +- `Fs.WriteAtomic()` — safe concurrent writes +- `Fs.NewUnrestricted()` — legitimate sandbox bypass + +**Open:** `core.Health()` (production monitoring), timestamp convention, JSON helpers. ### What This Means for v0.8.0