v1.11.0: the kit learns to drive itself, and its gates learn to admit ignorance
Released 2026-08-18. Thirty-two commits, and the largest entry in the changelog, but only three things really happened in it.
What we were trying to fix
The kit could build one pull request and land one pull request. It could not run a backlog. Every
issue still needed a human to pick it up, start implement-issue on it, wait, then start merge-pr.
The lifecycle skills existed; the thing that would keep several of them in flight did not, and it was
sitting in a consumer repository (horizon-hub) where it had been written and where nothing generic
belonged.
Second, requirements.json declared RoselineMCP at level: required, and scripts/preflight.sh
hard-failed without it. That proved the server was connected. It proved nothing at all about the
server being used, and the whole argument for requiring it is that C# gets read through Roslyn
rather than through Read on a .cs file
(#109).
Third, and quieter: a lot of this repo’s checks could not tell the difference between passing and not
having run. A find that failed for a reason other than a missing path reported a missing path
(#124). A suite counted as CI-enforced
whether or not the workflow that named it ever triggered on main
(#133). A config section scored configs it
had no way to evaluate (#99). A gate armed
itself from live repository state, so it disarmed whenever that state moved
(#96). None of these were failing loudly.
That was the problem with them.
What we decided
The fleet came home. auto-dev
(#173) is the layer above the lifecycle
trio: it surveys the open issues, orders them smallest-effort first, and keeps N area-isolated
workers running, each carrying one issue from plan to merged PR before its slot is refilled. It
arrived with its deterministic parts as scripts (survey.sh, reconcile.sh, wait-ci.sh), three
transcript analysers, and references/token-economics.md, which is where the cost claims live
including the lever that was tried and did not work. Recording the failed lever alongside the
successful one was a deliberate choice, and it is the reason those numbers can still be argued with
today.
The one lever that did measure was splitting the worker into two phases, implement to a ready PR and
then land it in a fresh context: 74 percent off the merge phase, roughly 11 percent of all worker
tokens. wait-ci.sh came out of a different kind of finding. Dispatching phase 2 into a still
pending CI run is a dispatch-timing bug, not a prompt-wording one, so no amount of instructing the
worker to wait would have fixed it. It cost five worker sessions in a single run before the wait moved
up to the supervisor, after which the same merges took 17 to 55 seconds.
systematic-debugging came across at the same time, reworked so it stands alone: its trigger
description now names the situations it must fire in and the ones it must stay out of, the fabricated
numbers are gone, and its references to the suite it came from were removed. It has no dependency of
any kind, which is why it joined as a plain skill rather than a lifecycle step.
RoselineMCP got teeth. .mcp.json installs the server with the plugin, and a PreToolUse hook
denies Read on a .cs file while naming the roseline tool that replaces it
(#110). A declared requirement became an
enforced one.
And every gate learned to say “I could not tell.” That is the one thread running through the long
tail of this release, and it is worth reading as one decision rather than fifteen. The roseline gate
itself fails open when the server cannot run on this host
(#112) and probes the launcher the manifest
actually declares, with an override for a user who wants enforcement anyway
(#155). guarded-push learned to tell
its exit 4’s three conditions apart (#93),
so “the remote disagrees” stopped being reported the same way as “the remote could not be read”.
Reading HEAD’s sha got one home, so no receipt can name no commit
(#129). The bundle gate arms from a
committed config rather than repository state
(#96). Section 9 of the xunit v3 check
refuses the configs it cannot evaluate
(#99). The CI wiring check requires a
push-to-main trigger before calling a suite enforced
(#133). Different files, one rule: a check
that did not run must not look like a check that passed. The strongest form of it is now written down
as ADR 0002: every failure path of the roseline
hook exits 0 and lets the read through, because it blocks only on positive evidence that enforcement
is meaningful.
What got cut
The roseline gate’s downward project scan was demoted rather than kept as the design. It survives only as a fallback behind the upward walk, because after the review it was clear the downward direction answers a different question from the one the gate asks.
systematic-debugging came in without the rest of the suite it belonged to. Porting its siblings
would have been easy and was not done: it has no dependencies, so it entered alone, and the
cross-references that would have implied otherwise were stripped on the way in.
The consumer repository kept only its own domain skills. Everything generic moved here, which is the smaller-sounding half of a decision that made the kit rather than a fork of it the place this work lives.
What bit us
The roseline gate’s review, at high effort, returned 10 findings, all accepted and none dismissed,
and two of them are the kind worth writing down.
Detection ran the wrong direction. The gate found a project by searching downward from the
current directory with find -maxdepth 3, which misses src/Company.Product/Api/Api.csproj at depth
four. That is a mainstream layout, so the gate was silently off in exactly the repositories it was
built for. It now walks up from the file, which is the direction that actually answers “is this
.cs file inside a project”.
The one-shot escape latched. The marker permitting a single retry was cleared only by the retry
that consumed it, so the common path (the model complies, uses roseline, never retries) left one
behind, and a Read of that same file hours later, or after a --resume that preserves the session
id, was silently allowed. Markers expire now.
Two smaller ones from the same review generalise beyond it. The gate’s own test fixtures were
the same directory, because n=$((n + 1)) inside a command substitution increments a subshell’s
copy of the variable, a trap tests/_lib.sh documents at length and which caught us anyway. And the
test never asserted the exit status, only stdout, so a regression turning a fail-open path into
exit 2 would have scored as a pass while blocking every Read in production.
Elsewhere, tick-plan was taking half an hour to return from a plan PATCH
(#113), and one suite turned out not to
parse at all under the bash 3.2 that ships on macOS, which quietly cost the documented local fast path
(#131). The sweep that now catches that
class runs on the developer’s machine and not in CI, deliberately: bash 5 parses the broken form
happily, so a CI job would have proved nothing.