mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-07 10:45:59 -10:00
fix(control): a turn without a runner reports ErrNoRunner, not a nil fault
runSettled and runComposedSyntheticTurn called c.runner.Run directly. On a controller assembled without a runner, that was a nil dereference. spawnGuardedTurn's recover turned it into "internal error: invalid memory address or nil pointer dereference". On Windows the fault is a hardware exception whether or not it is recovered, the class that corrupts the heap on AMX runners (golang/go#81238, see3d5b0b68d). This was an audit of the recover() sites left after3d5b0b68d: - Three re-panic after recording: the CLI crash capture, the subagent progress tracker and an SDK path. Nothing to change. - The others isolate faults: background task jobs, guarded turns, the rpcwire after-write callback, the jobs runner and the SDK handlers. Isolation is their job, so they stay. - What mattered was whether anything routinely sent a real fault through them. Temporary instrumentation over control, serve, agent, extension, jobs, boot and sdk/go found two nil dereferences, both this runner call, plus six explicit panic("boom") values tests raise on purpose, which are not exceptions. runWithRunner returns ErrNoRunner when the runner is nil. Both call sites use it, and the identity is a sentinel a caller can test with errors.Is. Re-running the instrumented control and serve suites records no nil dereference, only the two intentional panics. TestATurnWithoutARunnerReportsErrNoRunner holds the identity. The sentinel lives in turn_projection.go beside its use; controller.go is already past its size budget.
This commit is contained in:
1 parent
62a6870065
commit
2835cd5a58
3 files changed
+32
-1
No files matched your search
@@ -0,0 +1,18 @@
|
||||
package control
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// A controller assembled without a runner reports that by identity. Calling
|
||||
// through the nil runner instead was a nil dereference the turn guard
|
||||
// recovered, which on Windows is a hardware exception either way.
|
||||
func TestATurnWithoutARunnerReportsErrNoRunner(t *testing.T) {
|
||||
c := New(Options{})
|
||||
defer c.Close()
|
||||
if err := c.runSettled(context.Background(), "hello"); !errors.Is(err, ErrNoRunner) {
|
||||
t.Fatalf("runSettled = %v, want ErrNoRunner", err)
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,7 @@ func (o *turnOrchestrator) runComposedSyntheticTurn(ctx context.Context, text st
|
||||
ctx = c.continueAnnouncedTurn(ctx)
|
||||
ctx = agent.WithRawUserInput(ctx, text)
|
||||
ctx = c.withPlannerTurnMetadata(ctx, text, true)
|
||||
return c.runner.Run(ctx, c.ComposeSynthetic(text))
|
||||
return c.runWithRunner(ctx, c.ComposeSynthetic(text))
|
||||
}
|
||||
|
||||
// runSubagentSkillGoalLoop executes a slash-invoked runAs=subagent skill as a
|
||||
|
||||
@@ -2,6 +2,7 @@ package control
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"reasonix/internal/ablation"
|
||||
@@ -136,6 +137,18 @@ func (c *Controller) recallTail(source string, hasNotes bool) string {
|
||||
// cleared by one of those leaves the model holding state nobody sent it.
|
||||
func (c *Controller) runSettled(ctx context.Context, input string) error {
|
||||
c.settleTurnProjections()
|
||||
return c.runWithRunner(ctx, input)
|
||||
}
|
||||
|
||||
// ErrNoRunner reports a turn started on a controller assembled without a
|
||||
// runner. Returned instead of calling through the nil interface: that fault is
|
||||
// a hardware exception on Windows, recovered or not.
|
||||
var ErrNoRunner = errors.New("controller has no runner")
|
||||
|
||||
func (c *Controller) runWithRunner(ctx context.Context, input string) error {
|
||||
if c.runner == nil {
|
||||
return ErrNoRunner
|
||||
}
|
||||
return c.runner.Run(ctx, input)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user