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.