> **WARNING:** This document describes only the change applied in this PR. > It may already be outdated once the next PR is merged. > Historic PR-documentation is not maintained along with new PRs — treat it as a snapshot, not as current documentation. ## The Problem vm4006 builds hsadmin-ng (backend) and hsadmin-ng.frontend. The machine can take one backend build next to a frontend build, but not two backend builds. Werkator knew two limits: `executor.maxConcurrent` over all repositories, and one build per repository and branch. With `maxConcurrent: 2`, two backend branches could build at the same time; with `maxConcurrent: 1`, the frontend waited behind every backend build. ## Non-Goals - No limit per registry entry: one value for all repositories covers the case; an exception per entry can follow when someone needs it. - No build-definition key: what a machine can take is the host's decision, so ADR 0010 does not apply. ## The Scenarios ### Feature: A cap per repository below the global one #### Scenario#103.01: A second build of the same repository waits, another repository's runs So that a heavy repository never builds twice at once, and a light one is not held up by it. - **Given** `maxConcurrent: 2` and `maxConcurrentPerRepository: 1` - **And** a running build of one repository - **When** a second build of that repository and then a build of another repository are queued - **Then** the other repository's build takes the second slot, although it was queued last - **And** the second build of the first repository starts once its repository's slot is free ##### Verified by - [BuildExecutorIntegrationTest](../../src/test/kotlin/de/hoennig/werkator/build/BuildExecutorIntegrationTest.kt) — Scenario#103.01 - Without the key nothing changes: [BuildExecutorIntegrationTest](../../src/test/kotlin/de/hoennig/werkator/build/BuildExecutorIntegrationTest.kt) — "builds of different branches run concurrently" #### Scenario#103.02: Unset by default, read like the rest of the section So that every existing instance keeps its behaviour. - **Given** a configuration without and with `executor.maxConcurrentPerRepository` - **When** it is loaded - **Then** it is unset, or the value given ##### Verified by - [ConfigLoaderUnitTest](../../src/test/kotlin/de/hoennig/werkator/config/ConfigLoaderUnitTest.kt) — Scenario#103.02 ## The Solution - `ExecutorConfig.maxConcurrentPerRepository`, instance-level like the whole `executor` section, so no branch can raise it. - `BuildExecutor` sizes it once with the global semaphore and keeps one semaphore per `RepoContext`. - A build takes its repository's slot first and the global one second: a build waiting for its repository never sits on a global slot that another repository's build could use. A build cancelled while it waits for its repository's slot gives it back without waiting for a global one. - Like `maxConcurrent`, a change takes effect with the next restart. ## Additional Changes - `docs/configuration.md`, `docs/deployment.md`, `docs/llms.md`, the `init` template, `AGENTS.md` and the architecture skill describe the key. - Version 1.34.0 with its release note.