Make Sandcastle a Blocker-Aware Orchestrator#177
Merged
Conversation
I replaced the simple-loop runner with a custom orchestrator modeled on the tubarr setup, adapted to Scrython. A run now lists open `ready-for-agent` issues, fans out one sandboxed agent per issue, and on the host pushes each branch, opens a PR, and relabels the issue to `needs-review`. The agent commits only; it no longer closes issues or pushes, which was closing them before the work was reviewed. The orchestrator reads the `## Blocked by` section of each issue and only dispatches an issue once every blocker it names is cleared. A blocker counts as cleared when its issue is closed or when the target branch already carries a commit referencing it (e.g. `(#170)`). That commit check is what lets PRD branches work, since merging a slice into a `prd/<slug>` branch lands the commit but does not auto-close the issue. Issues with a milestone target their `prd/<slug>` branch (created from develop on first use); everything else targets develop. DRY_RUN=1 prints the dispatch plan and the blocked list without spawning anything, and ONLY_ISSUE=N restricts a run to a single issue for smoke tests. The prompt is now a per-issue template with ruff, mypy, and pytest gates.
The prompt gates omitted black, but CI runs black --check first and fails the whole run before pytest. #176 opened red for exactly this. Gates now mirror tests.yml: black, ruff check scrython tests, mypy, pytest.
run() returns RunResult.completionSignal: the matched signal, or undefined if the agent never signaled before the iteration limit. Open a PR only when the agent both committed and signaled completion; otherwise leave the branch for review instead of opening a red PR on partial work.
Adds a section covering the ready-for-agent to needs-review label flow, how a run works (blocker-aware dispatch, milestone PRD branches, CI-matching gates), one-time setup, and the run commands, so agent-opened PRs and the label flow are not a mystery to contributors.
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.
Description
This replaces the sandcastle
simple-looprunner with a blocker-aware issue orchestrator. A run picks up openready-for-agentissues, works each in an isolated Docker sandbox, opens a PR, and relabels the issue toneeds-review.The initial setup (PR #175) shipped the Python-adapted
simple-looptemplate. This builds on it: the agent now commits only, and the host pushes, opens the PR, and relabels. That split fixes the behavior I did not want from the template, where the agent closed issues before the work was reviewed.The orchestrator reads the
## Blocked bysection of each issue and only dispatches one once every blocker it names is cleared. A blocker is cleared when its issue is closed or when the target branch already carries a commit referencing it (e.g.(#170)). That commit check is what lets PRD branches work: merging a slice into aprd/<slug>branch lands the commit but does not auto-close the issue, since auto-close only fires on the default branch. Issues with a milestone target theirprd/<slug>branch; everything else targetsdevelop. This matters for the connector series (#170 to #171 to #172/#173), where a later slice must never branch off a base that lacks the earlier slice's work.Two correctness fixes came out of the first live run. The agent's gates were missing
black, which CI checks first, so #176 opened red even though the agent passed its own checks: the prompt gates now mirror CI exactly. And the host was opening a PR whenever the agent produced any commits, so I gated PR creation on the agent's completion signal (RunResult.completionSignal) so partial work leaves a branch for review instead of a red PR.Changes:
main.mtswith a host orchestrator: listready-for-agent, fan out one agent per issue, push, open a PR, relabelready-for-agenttoneeds-review.## Blocked bylist, clearing a blocker on issue-closed or a(#N)commit on the target branch.prd/<slug>branch (created from develop on first use) and everything else to develop.black,ruff check scrython tests,mypy,pytest.DRY_RUN=1(print the plan, spawn nothing) andONLY_ISSUE=N(single-issue smoke test) controls.Contributing.md: the label flow, how a run works, setup, and the run commands.Testing
DRY_RUN=1 npx tsx .sandcastle/main.mtsprints the correct dispatch plan and blocked list (Add paginate to Connector ABC and rewire iter_all to delegate #171 waits on Connector seam: ABC + ScryfallConnector + handler rewiring + ContextVar resolution #170; MockConnector and retire mock_urlopen across the test suite #172/CachedConnector decorator and deprecate cache= / cache_ttl= kwargs #173 wait on Connector seam: ABC + ScryfallConnector + handler rewiring + ContextVar resolution #170 and Add paginate to Connector ABC and rewire iter_all to delegate #171)ONLY_ISSUE=170 npx tsx .sandcastle/main.mtsran live end-to-end: createdprd/scryfallconnector-refactor, the agent passed its gates and committed, the host opened Connector seam: ABC + ScryfallConnector + handler rewiring + ContextVar resolution #176 against the PRD branch and relabeled Connector seam: ABC + ScryfallConnector + handler rewiring + ContextVar resolution #170 toneeds-review