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
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,31 @@ All notable changes to `geny-executor` are recorded here. The format
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
this project adheres to [Semantic Versioning](https://semver.org/).

## [2.0.1] — 2026-05-18

Patch release. Fixes a crash when a manifest names ``"subagent_type"``
as the Stage 12 orchestrator strategy.

### Fixed

- ``SubagentTypeOrchestrator.__init__`` now accepts ``registry=None``
and falls back to an empty :class:`SubagentTypeRegistry`. The
``StrategySlot`` machinery zero-arg-constructs the orchestrator
during ``PipelineMutator.restore``, before
``Pipeline._wire_subagent_orchestrator`` has had a chance to bind
the real registry. In 2.0.0 that crashed with
``__init__() missing 1 required positional argument: 'registry'``;
in 2.0.1 the temporary empty instance is harmless — every delegate
request lands as ``"unknown_agent_type"`` until the wire step
replaces the orchestrator with one bound to the host's registry.

### Migration

None. Hosts that already pass ``subagent_registry=`` to
``Pipeline.from_manifest_async`` keep their existing behaviour; the
fix only matters during the brief window between strategy restore
and post-restore wiring.

## [2.0.0] — 2026-05-17

**Major release.** The LLM client layer is generalised to support every
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "geny-executor"
version = "2.0.0"
version = "2.0.1"
description = "Harness-engineered agent pipeline library with 21-stage dual-abstraction architecture, built on the Anthropic API"
readme = "README.md"
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion src/geny_executor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
ProviderDrivenStrategy,
)

__version__ = "2.0.0"
__version__ = "2.0.1"

__all__ = [
# Core
Expand Down
12 changes: 10 additions & 2 deletions src/geny_executor/stages/s12_agent/subagent_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,16 @@ class SubagentTypeOrchestrator(AgentOrchestrator):
the same way.
"""

def __init__(self, registry: SubagentTypeRegistry):
self._registry = registry
def __init__(self, registry: Optional[SubagentTypeRegistry] = None):
# ``registry`` is logically required, but accepting ``None`` lets
# zero-arg construction work — which the ``StrategySlot`` machinery
# uses while restoring a manifest that names ``"subagent_type"`` as
# the orchestrator. The pipeline immediately replaces this instance
# with one bound to the real registry via
# ``Pipeline._wire_subagent_orchestrator``; until that runs, the
# orchestrator behaves as if no descriptors are registered (every
# delegate request lands as an "unknown_agent_type" failure).
self._registry = registry if registry is not None else SubagentTypeRegistry()

@property
def name(self) -> str:
Expand Down
55 changes: 55 additions & 0 deletions tests/unit/test_subagent_orchestrator_zero_arg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"""Regression test for the 2.0.1 fix: SubagentTypeOrchestrator must
accept zero-arg construction so the StrategySlot restore path that
runs *before* Pipeline._wire_subagent_orchestrator doesn't crash.
"""

from __future__ import annotations

import sys
import os

sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "src"))

import pytest

from geny_executor.core.state import PipelineState
from geny_executor.stages.s12_agent.subagent_type import (
SubagentTypeDescriptor,
SubagentTypeOrchestrator,
SubagentTypeRegistry,
)


def test_orchestrator_accepts_zero_arg() -> None:
"""The StrategySlot machinery instantiates the strategy with no
constructor args during PipelineMutator.restore. 2.0.0 raised
TypeError here; 2.0.1 returns a usable instance with an empty
registry."""
orch = SubagentTypeOrchestrator()
assert orch._registry is not None
assert len(orch._registry) == 0


def test_orchestrator_explicit_registry_unchanged() -> None:
reg = SubagentTypeRegistry()
reg.register(SubagentTypeDescriptor(
agent_type="x", factory=lambda ctx: None, description="x"
))
orch = SubagentTypeOrchestrator(reg)
assert orch._registry is reg
assert len(orch._registry) == 1


@pytest.mark.asyncio
async def test_zero_arg_orchestrate_treats_unknown_agent_as_structured_failure() -> None:
"""Before _wire_subagent_orchestrator runs, the orchestrator has an
empty registry. Any delegate request must land as a structured
failure, not a crash."""
orch = SubagentTypeOrchestrator()
state = PipelineState(session_id="s")
state.delegate_requests = [{"agent_type": "missing", "task": "t"}]
result = await orch.orchestrate(state)
assert result.delegated is True
assert len(result.sub_results) == 1
assert result.sub_results[0]["success"] is False
assert "unknown_agent_type" in (result.sub_results[0].get("error") or "")
Loading