For vm4006: one hsadmin-ng backend build next to a frontend build, never two backend builds. - New instance-level key `executor.maxConcurrentPerRepository` (unset = only the global cap). - A build takes its repository slot before a global one, so a waiting build never blocks another repository. - Sized once like `maxConcurrent`; a change needs a restart. PR-doc: `docs/prs/2026-10-03-PR#103-max-concurrent-per-repository.md` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: mhoennig <michael@hoennig.de> Reviewed-on: #103
3.1 KiB
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: 2andmaxConcurrentPerRepository: 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 — Scenario#103.01
- Without the key nothing changes: BuildExecutorIntegrationTest — "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 — Scenario#103.02
The Solution
ExecutorConfig.maxConcurrentPerRepository, instance-level like the wholeexecutorsection, so no branch can raise it.BuildExecutorsizes it once with the global semaphore and keeps one semaphore perRepoContext.- 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, theinittemplate,AGENTS.mdand the architecture skill describe the key.- Version 1.34.0 with its release note.