Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion go/internal/node/discovery_surface_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,10 @@ func TestProExecuteIsInternal(t *testing.T) {
if m.Description == "" {
t.Error("pro_execute lost its description")
}
assertSurface(t, "swe-planner[pro][entrypoint]", entrypointNames(n), wantEntrypoints)
// With the engine on the classic entry points are withheld, so the node
// advertises no entry points of its own — the swe-pro sidecar carries the
// coding entry (code_task/code_resume).
assertSurface(t, "swe-planner[pro][entrypoint]", entrypointNames(n), nil)
}

// TestExecuteDescribesItsPlanResultInput: execute's plan_result is the one input
Expand Down
11 changes: 6 additions & 5 deletions go/internal/node/pro_surface_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ func fakeEngineBin(t *testing.T) {
}

// TestRegisterPlannerProSurfaceGated: with SWE_PRO_ENGINE set and the engine
// binary present, the planner surface is the default 31 names plus exactly the
// pro handlers — and nothing on the fast node changes (the pro surface is
// planner-only).
// binary present, the pro engine REPLACES the classic surface — the internal
// role reasoners and the pro handlers register, but the opencode-driven
// orchestrators and implement_issue are withheld (they'd fail wherever opencode
// is absent; the swe-pro sidecar is the working coding entry). Nothing on the
// fast node changes (the pro surface is planner-only).
func TestRegisterPlannerProSurfaceGated(t *testing.T) {
t.Setenv(pro.EnvEnabled, "1")
fakeEngineBin(t)
Expand All @@ -33,8 +35,7 @@ func TestRegisterPlannerProSurfaceGated(t *testing.T) {
}
n.RegisterPlanner()

want := append(append([]string(nil), pythonRoleSurface...), pythonOrchestrators...)
want = append(want, pythonIssueReasoners...)
want := append([]string(nil), pythonRoleSurface...)
for name := range pro.Handlers() {
want = append(want, name)
}
Expand Down
21 changes: 17 additions & 4 deletions go/internal/node/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,28 @@ const (
tagInternal = "internal"
)

// RegisterPlanner registers the full swe-planner surface: 25 role reasoners +
// 5 orchestrators + the issue-level entry point (31 total). Ports swe_af/app.py.
// RegisterPlanner registers the swe-planner surface. With the pro engine off it
// is the full classic surface: 25 role reasoners + 5 orchestrators + the
// issue-level entry point (31 total), porting swe_af/app.py. With the pro engine
// on (pro.Available()), the classic entry points are withheld and only the pro
// executor is added — see the body for why.
func (n *Node) RegisterPlanner() {
n.registerRoles()
n.registerOrchestrators()
n.registerIssueReasoner()
// Pro engine on (the af-install / desktop default): the bundled swe-pro
// sidecar is the coding surface and needs no opencode, so register only the
// pro executor and withhold the classic opencode-driven entry points. The
// orchestrators (plan/build/execute/resolve/resume_build) and implement_issue
// drive the opencode role harness and fail wherever opencode is absent — the
// desktop bundle ships aforge, not opencode — so advertising them just
// surfaces broken entries next to swe-pro's working code_task. The role
// reasoners stay registered (internal, undiscoverable) so pro_execute can
// still call them. SWE_PRO_ENGINE=0 restores the full classic surface.
if pro.Available() {
n.registerProReasoners()
return
}
n.registerOrchestrators()
n.registerIssueReasoner()
}

// RegisterFast registers the swe-fast surface: the same 25 role reasoners + the
Expand Down
Loading