Skip to content

chess-evolve: path to (Task, Workflow) → outer loop with zero adapter code #1489

Description

@colehurwitz

Goal

chess-evolve (the reference factory-as-library demo) should define only two things:

task = PositionTask()
workflow = build_position_eval_workflow()
result = SwarmEngine.from_task(task, workflow, budget=100).run()

No adapter, no monkey-patches, no workspace hacks. Today we need a ChessSwarmEvaluator adapter (~30 lines) because of gaps in remote-factory. This issue tracks the path to eliminating it.

Current adapter and why each piece exists

class ChessSwarmEvaluator(SwarmEvaluator):
    def evaluate(self, workflow, ...):
        _prime_workspace(workspace)              # ← Gap A
        _runner.invoke_agent = _make_move_invoke(inner)  # ← Gap B + C
        loop = InnerLoop(project_dir=workspace, workflow=workflow)
        record = loop.step()
        _runner.invoke_agent = _orig              # ← Gap B cleanup
        return EvalResult(score=record.score_end)

Gap A: Workspace priming (_prime_workspace)

Creates .factory/chess/ dir and placeholder board_state.md, memory.md before the executor runs — otherwise the executor blocks on reads that don't exist yet.

Fix (chess-evolve side): Move priming into PositionTask.setup() — the Task contract already owns workspace preparation. No factory change needed. Can fix now.

Gap B: Agent backend swap (invoke_agent monkey-patch)

Swaps factory.agents.runner.invoke_agent (spawns claude CLI) for chess-evolve's _sdk_invoke_agent (Haiku API). Restored in finally.

Fix (remote-factory): WorkflowExecutor should accept an optional agent_fn parameter. Tracked in #1488 (Gap #1).

Gap C: Output persistence (_make_move_invoke wrapper)

Writes agent output to .factory/chess/move.md on disk after every call. Needed because WorkflowExecutor keeps output in memory only, but PositionTask.verify() reads from disk.

Fix (remote-factory): Executor should write agent output to paths declared in node.writes automatically. Akash's Factory Artifact Format design (Port-level write validation) is the upstream fix. Tracked in #1488 (Gap #2).

Sequencing

  1. Now (chess-evolve): Fix Gap A — Task.setup() handles priming
  2. Upstream: Fix Gap B — WorkflowExecutor(agent_fn=...) eliminates monkey-patch
  3. Upstream: Fix Gap C — executor writes to node.writes automatically (artifact format)
  4. Upstream: Add SwarmEngine.from_task(task, workflow, budget) convenience API
  5. Final: Delete ChessSwarmEvaluator. evolution.py becomes 5 lines.

End state

from chess_evolve.tasks import PositionTask
from chess_evolve.pipeline import build_position_eval_workflow
from factory.outer_loop import SwarmEngine

def main():
    task = PositionTask()
    workflow = build_position_eval_workflow()
    result = SwarmEngine.from_task(task, workflow, budget=100).run()
    print(f"Best score: {result.best_score}")

Related

🤖 Generated with Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions