Skip to content

Commit fca6593

Browse files
committed
Merge fix/core-dev-build — fixed latest core dev + dedup primitives
Brings github core dev (106 commits past v0.10.4) with the incomplete error->Result migration finished (see fix commit), merged with the dedup primitives (Pool/Coalesce/FirstPositive/SliceEqual/FirstNonBlank/ MapString/Result.Err). Full suite green. Co-Authored-By: Virgil <virgil@lethean.io>
2 parents d86a36a + c0ebb9e commit fca6593

136 files changed

Lines changed: 8998 additions & 3029 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎action_bench_test.go‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,3 +207,21 @@ func BenchmarkAction_Tasks(b *B) {
207207
actionSinkStrings = c.Tasks()
208208
}
209209
}
210+
211+
func BenchmarkCore_PerformAsync(b *B) {
212+
c := New()
213+
c.Action("bench.async", noopActionHandler())
214+
opts := NewOptions()
215+
b.ReportAllocs()
216+
for i := 0; i < b.N; i++ {
217+
actionSinkResult = c.PerformAsync("bench.async", opts)
218+
}
219+
}
220+
221+
func BenchmarkCore_Progress(b *B) {
222+
c := New()
223+
b.ReportAllocs()
224+
for i := 0; i < b.N; i++ {
225+
c.Progress("bench.task", 0.5, "halfway", "bench.async")
226+
}
227+
}

‎action_example_test.go‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,33 @@ func ExampleCore_PerformAsync() {
205205

206206
// ExampleCore_Progress reports task progress through `Core.Progress` for background task
207207
// progress. Asynchronous work reports progress through Core task helpers.
208+
// ExampleAction_Enable re-enables a disabled action through `Action.Enable`.
209+
func ExampleAction_Enable() {
210+
c := New()
211+
a := c.Action("deploy", func(_ Context, _ Options) Result { return Result{OK: true} })
212+
a.Disable()
213+
a.Enable()
214+
Println(a.Enabled())
215+
// Output: true
216+
}
217+
218+
// ExampleAction_Disable soft-disables an action through `Action.Disable`.
219+
func ExampleAction_Disable() {
220+
c := New()
221+
a := c.Action("deploy", func(_ Context, _ Options) Result { return Result{OK: true} })
222+
a.Disable()
223+
Println(a.Enabled())
224+
// Output: false
225+
}
226+
227+
// ExampleAction_Enabled reports whether an action is active through `Action.Enabled`.
228+
func ExampleAction_Enabled() {
229+
c := New()
230+
a := c.Action("deploy", func(_ Context, _ Options) Result { return Result{OK: true} })
231+
Println(a.Enabled())
232+
// Output: true
233+
}
234+
208235
func ExampleCore_Progress() {
209236
c := New()
210237
var progress float64

‎action_test.go‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -633,9 +633,10 @@ func TestAction_Core_Progress_Good(t *T) {
633633
func TestAction_Core_Progress_Bad(t *T) {
634634
c := New()
635635

636-
c.Progress("task-absent", -1, "refused", "agent.dispatch")
637-
638-
AssertTrue(t, true)
636+
// Reporting progress for an unknown task is a safe no-op, not a panic.
637+
AssertNotPanics(t, func() {
638+
c.Progress("task-absent", -1, "refused", "agent.dispatch")
639+
})
639640
}
640641

641642
func TestAction_Core_Progress_Ugly(t *T) {

‎alloc_gate_test.go‎

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// SPDX-License-Identifier: EUPL-1.2
2+
3+
// Allocation regression gates. Unlike a Benchmark (which only prints a
4+
// number nobody compares), each case here asserts a hard ceiling on
5+
// allocations-per-call via testing.AllocsPerRun — so the suite FAILS if a
6+
// hot primitive starts allocating where it didn't before. ns/op is
7+
// deliberately not gated (machine-dependent); allocs are stable.
8+
//
9+
// Ceilings are the measured floor as of this commit:
10+
// 0 — pure / zero-copy primitive (no heap touch)
11+
// 1 — one unavoidable alloc: Result{Value:<non-pointer>} boxes the value
12+
// into `any` (string/int → 1 alloc; pointer values box for free)
13+
//
14+
// Tightening a ceiling that's been beaten is welcome; raising one is a
15+
// regression that needs justifying in review.
16+
//
17+
// Run: go test -run 'TestAllocs_' .
18+
19+
package core_test
20+
21+
import (
22+
"testing"
23+
24+
. "dappco.re/go"
25+
)
26+
27+
// gate sinks — keep the measured call from being elided as dead code.
28+
var (
29+
gateInt int
30+
gateBool bool
31+
gateStr string
32+
gateBytes []byte
33+
gateResult Result
34+
gateAny any
35+
gateDiags []LSPDiagnostic
36+
)
37+
38+
func TestAllocs_HotPrimitives(t *T) {
39+
ptr := &struct{ n int }{}
40+
bs := []byte("homelab")
41+
var ai32 AtomicInt32
42+
var ab AtomicBool
43+
var au64 AtomicUint64
44+
cases := []struct {
45+
name string
46+
ceiling int
47+
fn func()
48+
}{
49+
// math / ordering — pure, must stay 0
50+
{"Compare", 0, func() { gateInt = Compare(3, 7) }},
51+
{"Min", 0, func() { gateInt = Min(3, 7) }},
52+
{"Max", 0, func() { gateInt = Max(3, 7) }},
53+
{"Abs", 0, func() { gateInt = Abs(-42) }},
54+
{"Clamp", 0, func() { gateInt = Clamp(15, 0, 10) }},
55+
{"Sign", 0, func() { gateInt = Sign(-3) }},
56+
57+
// string predicates / index — pure, must stay 0
58+
{"Contains", 0, func() { gateBool = Contains("agent ready", "ready") }},
59+
{"HasPrefix", 0, func() { gateBool = HasPrefix("agent.go", "agent") }},
60+
{"HasSuffix", 0, func() { gateBool = HasSuffix("agent.go", ".go") }},
61+
{"EqualFold", 0, func() { gateBool = EqualFold("Bearer", "bearer") }},
62+
{"Index", 0, func() { gateInt = Index("agent", "e") }},
63+
{"Count", 0, func() { gateInt = Count("banana", "a") }},
64+
{"Trim", 0, func() { gateStr = Trim(" x ") }},
65+
66+
// slice search — pure, must stay 0
67+
{"SliceContains", 0, func() { gateBool = SliceContains([]int{1, 2, 3}, 2) }},
68+
{"SliceIndex", 0, func() { gateInt = SliceIndex([]int{1, 2, 3}, 2) }},
69+
70+
// unsafe zero-copy conversions — must stay 0
71+
{"AsBytes", 0, func() { gateBytes = AsBytes("homelab") }},
72+
{"AsString", 0, func() { gateStr = AsString(bs) }},
73+
74+
// Result constructors — pointer value boxes for free (0); a
75+
// non-pointer value pays the one inherent Result-boxing alloc.
76+
{"Ok_Pointer", 0, func() { gateResult = Ok(ptr) }},
77+
{"Fail", 0, func() { gateResult = Fail(AnError) }},
78+
{"Ok_String", 1, func() { gateResult = Ok("ready") }},
79+
{"ResultOf_String", 1, func() { gateResult = ResultOf("ready", nil) }},
80+
81+
// Result accessors — pure reads, must stay 0
82+
{"Result_Or", 0, func() { gateAny = (Result{OK: false}).Or("fallback") }},
83+
84+
// atomics — lock-free, must stay 0
85+
{"AtomicInt32_Load", 0, func() { gateInt = int(ai32.Load()) }},
86+
{"AtomicInt32_Store", 0, func() { ai32.Store(7) }},
87+
{"AtomicInt32_Add", 0, func() { gateInt = int(ai32.Add(1)) }},
88+
{"AtomicBool_Load", 0, func() { gateBool = ab.Load() }},
89+
{"AtomicBool_Store", 0, func() { ab.Store(true) }},
90+
{"AtomicUint64_Add", 0, func() { gateInt = int(au64.Add(1)) }},
91+
92+
// more math — pure, must stay 0
93+
{"Pow", 0, func() { gateInt = int(Pow(2, 8)) }},
94+
{"Floor", 0, func() { gateInt = int(Floor(3.7)) }},
95+
{"Ceil", 0, func() { gateInt = int(Ceil(3.1)) }},
96+
{"Round", 0, func() { gateInt = int(Round(3.5)) }},
97+
98+
// slice — Clone copies (1 alloc for the backing array); Reverse is in-place (0)
99+
{"SliceClone", 1, func() { gateBytes = SliceClone(bs) }},
100+
}
101+
for _, c := range cases {
102+
avg := int(testing.AllocsPerRun(1000, c.fn))
103+
AssertLessOrEqual(t, avg, c.ceiling, c.name)
104+
}
105+
}
106+
107+
// TestAllocs_LSPComputeDiagnostics locks the regex-hoist win (c1cf230):
108+
// with per-call regexp.Compile it allocated ~186/call; compiling the
109+
// naming-diagnostic patterns once dropped it to ~47. A regression to
110+
// per-call compilation jumps back over 100, so the ceiling catches it.
111+
func TestAllocs_LSPComputeDiagnostics(t *T) {
112+
uri := "file:///agent/worker.go"
113+
content := []byte("package worker\n\nfunc process(in string) (string, error) {\n\treturn in, nil\n}\n")
114+
LSPComputeDiagnostics(uri, content) // warm the per-dir cache → measure steady state
115+
avg := int(testing.AllocsPerRun(200, func() {
116+
gateDiags = LSPComputeDiagnostics(uri, content)
117+
}))
118+
AssertLessOrEqual(t, avg, 70, "LSPComputeDiagnostics")
119+
}
120+
121+
var gateStrSlice []string
122+
123+
// TestAllocs_FilterArgs locks the pre-size win (1f59e31): was an unpresized
124+
// 'var clean []string' + append (geometric regrow), now the single presized
125+
// result allocation. (Typed sink — assigning to `any` would box and add a
126+
// spurious alloc.)
127+
func TestAllocs_FilterArgs(t *T) {
128+
args := []string{"deploy", "--target", "homelab", "-v"}
129+
fa := int(testing.AllocsPerRun(1000, func() { gateStrSlice = FilterArgs(args) }))
130+
AssertLessOrEqual(t, fa, 1, "FilterArgs")
131+
}

‎api.go‎

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -356,15 +356,21 @@ func HTTPStripPrefix(prefix string, h Handler) Handler {
356356
return http.StripPrefix(prefix, h)
357357
}
358358

359-
// HTTPListenAndServe runs an HTTPServer on the given address with the
360-
// given handler. Returns ErrHTTPServerClosed after graceful shutdown,
361-
// other errors otherwise.
362-
//
363-
// if err := core.HTTPListenAndServe(":8080", mux); !core.Is(err, core.ErrHTTPServerClosed) {
364-
// core.Error("listen", "err", err)
359+
// HTTPListenAndServe runs an HTTPServer on the given address with the given
360+
// handler, returning a Result. ListenAndServe only returns on error, so a
361+
// non-OK Result is the normal outcome — Value carries ErrHTTPServerClosed after
362+
// a graceful shutdown, other errors otherwise.
363+
//
364+
// if r := core.HTTPListenAndServe(":8080", mux); !r.OK {
365+
// if err, _ := r.Value.(error); !core.Is(err, core.ErrHTTPServerClosed) {
366+
// core.Error("listen", "err", err)
367+
// }
365368
// }
366-
func HTTPListenAndServe(addr string, handler Handler) error {
367-
return http.ListenAndServe(addr, handler)
369+
func HTTPListenAndServe(addr string, handler Handler) Result {
370+
if err := http.ListenAndServe(addr, handler); err != nil {
371+
return Result{Value: err, OK: false}
372+
}
373+
return Result{OK: true}
368374
}
369375

370376
// HTTPFileServer returns a Handler that serves HTTP requests with the

‎api_bench_test.go‎

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,60 @@ func BenchmarkNewHTTPTestTLSServer(b *B) {
260260
}
261261
}
262262

263-
// HTTPListenAndServe + RemoteAction are not benched — they bind a real
264-
// socket / make a real outbound HTTP call. A unit-bench cannot do that
265-
// deterministically. They are exercised end-to-end in api_test.go.
263+
// --- remote-endpoint surface (Drive + protocol resolution) ---
264+
//
265+
// A mock protocol factory stands in for a live transport so these bench
266+
// the resolve + scheme-extraction + call dispatch path, not the network.
267+
// HTTPListenAndServe is omitted — it blocks serving until shut down.
268+
269+
var apiSinkResult Result
270+
271+
func apiRemoteFixture() *Core {
272+
c := New()
273+
c.API().RegisterProtocol("http", mockFactory("pong"))
274+
c.Drive().New(NewOptions(
275+
Option{Key: "name", Value: "charon"},
276+
Option{Key: "transport", Value: "http://127.0.0.1:9101/mcp"},
277+
))
278+
return c
279+
}
280+
281+
func BenchmarkAPI_Stream(b *B) {
282+
c := apiRemoteFixture()
283+
b.ReportAllocs()
284+
for i := 0; i < b.N; i++ {
285+
r := c.API().Stream("charon")
286+
if r.OK {
287+
r.Value.(Stream).Close()
288+
}
289+
}
290+
}
291+
292+
func BenchmarkAPI_Call(b *B) {
293+
c := apiRemoteFixture()
294+
opts := NewOptions()
295+
b.ReportAllocs()
296+
for i := 0; i < b.N; i++ {
297+
apiSinkResult = c.API().Call("charon", "agentic.status", opts)
298+
}
299+
}
300+
301+
func BenchmarkCore_RemoteAction(b *B) {
302+
c := apiRemoteFixture()
303+
ctx := Background()
304+
opts := NewOptions()
305+
b.ReportAllocs()
306+
for i := 0; i < b.N; i++ {
307+
apiSinkResult = c.RemoteAction("charon:agentic.status", ctx, opts)
308+
}
309+
}
310+
311+
// HTTPListenAndServe is benched on its error path: an invalid address
312+
// fails the bind immediately and returns the error Result, with no real
313+
// socket. The success path blocks serving, so it can't be unit-benched.
314+
func BenchmarkHTTPListenAndServe(b *B) {
315+
b.ReportAllocs()
316+
for i := 0; i < b.N; i++ {
317+
apiSinkResult = Result{Value: HTTPListenAndServe("127.0.0.1:-1", nil)}
318+
}
319+
}

‎api_example_test.go‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,12 @@ func ExampleHTTPFS() {
412412
// ExampleHTTPError writes a plain-text error body with the given status
413413
// code. The handler exits after this call; the Recorder captures the
414414
// response for assertion in tests.
415+
// ExampleCore_API returns the HTTP API subsystem through `Core.API`.
416+
func ExampleCore_API() {
417+
Println(New().API() != nil)
418+
// Output: true
419+
}
420+
415421
func ExampleHTTPError() {
416422
rec := NewHTTPTestRecorder()
417423
HTTPError(rec, "missing field", StatusBadRequest)

0 commit comments

Comments
 (0)