Repository navigation
Put ACT planning behind a planner trait - #20
Merged
Merged
Conversation
ACT now asks an ActPlanner for the model's answer instead of calling the model client directly. LlmPlanner holds today's planning: one structured call with Stagehand's prompts. The step loop keeps everything else: the snapshot, the check with PageSnapshot::decide, the action, the stale-ref retry, the fill read-back, and the report. The trait returns a boxed future, so the runner can hold one planner as Arc<dyn ActPlanner> and choose it at run time. This is the seam for a Jev planner with an LLM fallback. Behavior does not change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Sep 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #19. Step 1 of the ACT planner plan: the seam, with no behavior change.
Why
We want to swap how ACT chooses its action: the LLM by default, and TypeSafe Jev with an LLM fallback as an alternative (a
--jevflag, in the next PR). This PR adds the seam only.What
run/act/planner.rsadds theActPlannertrait.plan(PlanRequest) -> PlanFuturereturns the model's answer or aPlanError, and always the usage spent. The future is boxed, so the trait is dyn-compatible without anasync-traitdependency, and the runner holds one planner per run asArc<dyn ActPlanner>.LlmPlannerholds today's planning: Stagehand's prompts and one structured call throughModelClient.run_actasks the planner for the answer and keeps everything else in one place: the snapshot,PageSnapshot::decide, the action, the stale-ref retry, the fill read-back, and the report. Every planner shares that execution.FlowRun.model: Option<&ModelClient>becomesplanner: Option<&dyn ActPlanner>.The trait follows the plan's decision. The Rust style guide defaults to an enum when the crate owns every variant. The trait makes the planner an extension point: the Jev planner will hold its LLM fallback as another
ActPlanner, and a later ACT cache (SPEC §18) can wrap a planner the same way.Tests
No new tests: behavior is unchanged, and all 16 ACT acceptance tests and the unit tests pass as they are.
mise run checkpasses.🤖 Generated with Claude Code