Run #18 closed as the deepest run yet — seven of nine steps, Test's
first-ever success — with one open question: whether Approval and Release, sharing the Releaser
role's unconfigured gemini preference, would hit the identical provider-configuration wall Test
had just been fixed for. Before Run #19, that question got the same fix already proven twice.
What we did to prepare
Nothing new to fix yet — the preferredProviderKind: "anthropic" override that resolved Test's
gap in Run #18 was extended to approval and release, the same narrow, one-line-per-step pattern
already used for ux and test. No change to the Releaser role's own definition, no change to
provider resolution generally.
What happened in Run #19
Scope, Architecture, Security, and UX all completed cleanly. Then Builder failed on its first real attempt, differently from anything seen before:
Anthropic response was truncated (stop_reason: max_tokens) before it could complete.
The call ran 115,511 milliseconds — comfortably inside its own timeout — and produced exactly 8,192 output tokens: the hard ceiling this deployment configures for that call, hit precisely, not approached. Not a timeout, not a validation rejection, not a provider-configuration gap — a different failure shape than any of the last eighteen runs, on an input (25,522 tokens) in the same range Builder has handled cleanly before and since.
One occurrence, at exactly the configured ceiling, is consistent with either an unusually large
patch for this particular request or ordinary generation-length variance landing on the wrong side
of the limit once. Neither is distinguishable from a single data point, and this project's own
standing rule is that a limit doesn't move on one occurrence. The call was recommended to rerun
unchanged rather than guess at raising maxOutputTokens.
What happened in Run #20
Rerun, same request, no code changed. Scope through UX completed the same as always. Then:
Builder succeeded — on a near-identical input (24,832 tokens) that produced 6,807 output tokens, comfortably clear of the ceiling that stopped Run #19 cold. One occurrence of truncation followed immediately by one clean success on the same request is exactly what "not a pattern" looks like in practice, not just in argument.
Review, Test, Approval, and Release all succeeded in sequence — the first time any run in this entire series reached the end of the workflow. Approval and Release's provider-configuration fix worked on the first real attempt, same as Test's had in Run #18. Nine of nine steps, clean, no retries, no rejections:
- Scope: 34,355 ms
- Architecture: 63,804 ms
- Security: 21,086 ms · UX: 23,130 ms (parallel)
- Build: 97,097 ms
- Review: 25,948 ms · Test: 47,336 ms (parallel)
- Approval: 30,850 ms
- Release: 23,148 ms
Then the run never finished. No PR appeared. The UI showed the run as still running for far
longer than a normal post-completion delay, and it stayed that way — not blocked on any gate, just
stuck — until it was cancelled several hours later.
What we found tracing it
The run's own audit trail shows a run_completed event written the moment all nine steps finished.
243 milliseconds later, a durable failure event was recorded:
Real GitHub execution: branch creation failed — Refusing to write to the repository: the run is "completed", not actively executing. This check runs before any GitHub API call.
Traced to source, this is not a flaky failure — it's a deterministic collision between two pieces
of code that were each correct on their own terms. The engine marks a run "completed" the moment
its last step finishes, before it ever attempts the real GitHub write that turns that work into a
pull request. The GitHub write pipeline's own safety guard — added deliberately, so a write can
never fire against a run that isn't actively in flight — refuses to proceed unless the run's status
is exactly "running". By the time that guard runs, it always sees "completed", because the
engine set that status one step earlier in the same call. Every run that reaches this point hits
this refusal, every time, by construction. Run #20 wasn't unlucky — it was the first run good enough
to find a wall nothing before it had ever reached.
The refusal itself was handled exactly as designed: caught cleanly, converted to a truthful
branch_creation_failed outcome, logged durably, no branch or PR ever attempted on GitHub. That
part of the safety design worked. What came after it didn't — the run's own status was never
durably written past "running", and the request that should have recorded "failed" instead
returned a bare server error with no further detail. Tracing the write path that follows ruled out
the two most likely structural causes (a database constraint rejecting the new status, a trigger
blocking the transition) directly against the live database — neither is what happened. What is
confirmed, though, is why no trace of the actual failure exists anywhere: the route's own error
handling deliberately discards error detail before it would ever reach a log, to avoid leaking
internal detail to a client. That's the right instinct for a public API response and an active gap
for anyone trying to debug this from the server side afterward.
What the evidence actually supports
Builder's truncation was a one-off, not a pattern. The same request, rerun unchanged, succeeded comfortably under the ceiling that stopped it the first time. No limit was raised, and none needed to be — the standing rule that a fix requires more than one occurrence held here by not producing a fix.
Run #20 is the first complete run in this project's history, and that success is exactly what exposed a real, deterministic defect that no partial run could ever have reached: the engine declares a run finished before attempting the one action — the real GitHub write — that a finished run is supposed to have produced. This will recur on every future run that gets this far, not occasionally.
A second, independent gap sits downstream of the first: whatever the actual server error was after the GitHub write correctly refused, it left no diagnostic trace anywhere, by design of how this route reports unrecognized errors to its caller. Finding out what happens after a governance refusal requires that path to leave more behind than it currently does.
Where this leaves things
- Builder's output-truncation question is closed as a one-off — confirmed by an immediate clean rerun, not just an argument that it should be fine.
- The provider-configuration category (
ux,test,approval,release) is fully closed — four for four now, every previously-affected step confirmed working on its first real attempt. - A new, structural problem is now the open question, and it's the last one standing between this project and its first real pull request: the engine's own completion sequencing runs ahead of the GitHub write it's supposed to gate on, guaranteed to recur on every future full run until the ordering itself changes.
- No fix is proposed here — this is what read-only tracing established; what changes, if anything, is a decision for outside this post.