01 · Status

Now: the S0 clean-path baseline is established — cold fire plus reproduction on 13 Aug, zero stops in both runs; the columns are on the results page. Next: the first avoidable-stop scenario, authored and admitted through the same gates.

02 · Problem

The pipeline takes a ticket and delivers finished work in seven stages. It gathers context, checks the change against the architecture, plans, writes the code, verifies it, reviews its own output, opens a pull request. Verification and review loop back until they pass. Against a single repository it needs no supervision.

It breaks down when one ticket spans several repositories in sequence. An API change lands in the service that provides it, then in the client library that calls it, then in the tool that wraps that client. The pipeline stops at the first repository boundary. It stops deliberately, rather than make a change it cannot check.

What a stop is

A stop is the pipeline halting and handing back to a person. It reports what it found, what it expected and what it needs, then does nothing further.

A stop is the pipeline declining to guess, and usually that is correct. Carrying on past a mismatch produces code that compiles, passes its own tests, and breaks the contract the next repository depends on.

The goal: reduce how often a stop is necessary, without turning a legitimate stop into a guess.

Why it is hard

A bigger context window does not solve it. Four questions have no answer today once a task crosses a repository boundary:

  • Which repositories should this change be validated against?
  • What carries forward to the next step, and what gets dropped?
  • When something turns out wrong, which repository do we go back to?
  • Is this a real fix, or the same failure again? Inside one repository the pipeline already counts repeat failures and gives up rather than thrashing. Across repositories there is no equivalent.

Where a run stops

One multi-repo run, and the four points where it can stop A ticket is split into repositories. Each repository in turn is delivered, what it now provides is extracted, and that is checked against what the next repository expects. An integration gate then checks the whole graph before the pull requests are cross-linked. Four numbered markers show where the run can stop: at the split, while planning a repository, at the check between repositories, and at the integration gate. repeated for each repository, in dependency order Ticket Split intorepositories Deliver thechange Extract what itnow provides Check it againstwhat the nextone expects Integrationgate Cross-linkedpull requests 1 2 3 4

The work cannot be split safely

BLOCKED-UNMAPPED-SCOPE

The ticket cannot become an ordered list of per-repository jobs, because the dependencies form a cycle or a repository it needs is not in the workspace. Nothing has been built, so nothing is at risk.

A repository cannot plan its work

BLOCKED-CONTRACT-GAP · BLOCKED-DECISION-GAP

Either the plan needs something from a dependency that no contract states, or the ticket forces an architecture decision nobody has made. The first is a missing fact. The second is a missing human judgement.

What was built contradicts what the next repository expects

BLOCKED-CONTRACT-DRIFT

A repository delivered, and what it provides no longer matches what the next one was told to expect. The run stops before the next repository starts, so no work is done against a stale assumption. The pipeline found the problem and named the right repository; it is not permitted to act.

A consumed surface is missing at the end

BLOCKED-CONTRACT-DRIFT

The final gate checks the whole graph, including edges the step-by-step check cannot see. Those are the edges where a repository was never told what to expect and only declared afterwards what it used. Same shape as point 3, but everything is already built.

03 · Test bed

Repository Role in the chain
moby/moby The engine. Defines the API and the Go client library everything else calls.
docker/cli The docker command. Consumes the engine's client library.
docker/compose docker compose. Consumes both the engine's client and the CLI.

One ticket produces three dependency edges in a forced order — engine before CLI before Compose. The dependencies are real and versioned, each repository has a substantial test suite, and a ticket's surface travels visibly — from an engine API option to a command-line flag to a Compose flag.

tickets/TB-401.md

TB-401 — Support a memory burst allowance on containers end-to-end

Type: feature · Workspace ticket (spans: moby, cli, compose)

Summary

Add a memory burst allowance to container configuration: a new Engine API Resources field, a docker run/docker create flag, and a docker compose up flag applying it to all created containers. The value is validated by a single shared helper exported from the docker CLI, so both CLIs reject the same inputs for the same reason.

Description

Operators want an advisory burst allowance alongside the existing hard Memory limit: a byte figure recording how far above the limit a container may briefly go, for monitoring and scheduling tooling to read back. This ticket covers plumbing the value end-to-end from both CLIs to the Engine API type; engine-side enforcement is out of scope (the field is carried, stored, and echoed back on inspect — semantics mirror MemoryReservation's transport, not its enforcement).

The validation rule is shared rather than duplicated. A burst allowance only means something relative to the limit it bursts above, so the check ("unset, or strictly greater than Memory") has to be identical on both CLIs. The docker CLI owns it and exports it; compose consumes that exported surface rather than reimplementing the rule. This is deliberate: a rule reimplemented twice is a rule that drifts.

Targets: the moby Engine API + Go client (transport only), the docker CLI, and the docker compose CLI.

Acceptance criteria

moby

  1. Resources (package github.com/moby/moby/api/types/container, embedded in container.HostConfig) gains the field MemoryBurst int64, declared next to MemoryReservation, with the doc comment: Memory burst allowance (in bytes); 0 means unset. The field round-trips JSON marshal/unmarshal under the name MemoryBurst.

cli

  1. Package github.com/docker/cli/opts gains the exported function ValidateMemoryBurst(burst, limit int64) error, following the package's existing ValidateXxx convention. It returns nil when burst == 0 (unset) or when burst > limit, and otherwise returns an error whose message is exactly: memory burst must be greater than the memory limit Unit-tested in opts for: unset, burst above limit, burst equal to limit, burst below limit, and burst set while limit is 0.
  2. docker run --memory-burst 512m <image> produces a container-create request whose HostConfig.MemoryBurst equals 536870912 (unit test at the container-options level, mirroring the existing --memory-reservation coverage). The flag is typed opts.MemBytes, so it accepts the same human-readable suffixes as --memory.
  3. docker run --help lists --memory-burst with the help text exactly: Memory burst allowance above the memory limit
  4. docker run --memory 256m --memory-burst 128m <image> fails before any API call, with the error from criterion 2.

compose

  1. docker compose up --memory-burst 512m applies MemoryBurst = 536870912 to the HostConfig of every container it creates (unit test at the service-convert/create level, mirroring the existing memory-limit mapping coverage).
  2. docker compose up --help lists --memory-burst with the help text exactly: Memory burst allowance above the memory limit for all created containers
  3. compose rejects an invalid pairing by calling opts.ValidateMemoryBurst — not by reimplementing the comparison — and surfaces that function's error message unchanged.

Consumed surfaces (per dependency edge)

  • cli → moby: the field MemoryBurst int64 on container.Resources (module github.com/moby/moby/api, package .../api/types/container); value semantics: 0 = unset, positive = the burst allowance in bytes.
  • compose → moby: the same field, MemoryBurst int64 on container.Resources, same semantics, set via the container-create request compose issues per service.
  • compose → cli: the function ValidateMemoryBurst(burst, limit int64) error, exported from module github.com/docker/cli, package github.com/docker/cli/opts. compose already imports this package (cmd/compose/build.go uses cliopts.MemBytes), so the surface is consumed through an existing edge, not a new one.

Verification

Local only — this workspace has no Docker engine and CI is disabled on the origins. No step may contact a live daemon.

  • Build: each member compiles after its change, with the workspace linkage rules in AGENTS.md applied.
  • Unit tests: the packages touched by criteria 1, 2, 3, 6 and 8.
  • Parse/help surfaces: criteria 4 and 7 are checked by running the command's --help and reading the flag line; criteria 5 and 8 by the failure path returning before any client call.

Links

  • Tech spec: none — this ticket is self-contained.
  • Design: none (CLI-only surface).
  • Dependencies: none.