Fork Tiers
The fork management system operates at two tiers. Both run the same workflows, the same three-branch model, and the same cascade. They differ in what their upstream is and in how sync treats what it receives.
The Tier Model
graph TD
U[Upstream Project Repository<br/>shared code + all providers] -->|"sync, filter mode (ADR-038)<br/>classify, strip, inject"| S[Service Repository main<br/>the finished product]
T[Template Repository] -->|template sync<br/>workflows + engine| S
S -->|"sync, mirror mode (ADR-039)<br/>verbatim tip"| C[Customer Fork main<br/>the same product + local work]
C -->|contribution PRs<br/>fork network| S | First tier (service repository) | Customer tier (mirror fork) | |
|---|---|---|
| Created by | Template instantiation + initialization | GitHub fork + the Adopt Fork workflow |
| Upstream | The upstream project repository | The service repository's main |
| Sync behavior | Filter: classify, strip providers, inject Azure references | Mirror: verbatim upstream tip |
SYNC_MODE variable | Unset (or filter) | mirror |
| Fork-owned content | provider/<svc>-azure, testing/<svc>-test-azure, .github/, build/ | Local feature work only; everything else is upstream-owned |
| Cascade assertion and pom stamp | Active | Off (no ownership split to defend) |
| Template sync | Active | Off (updates arrive through the mirror) |
| Sync PR build profile | core only (fork_upstream is provider-less) | Full profile set (fork_upstream carries provider/) |
| Workflow updates arrive via | Template sync | The mirror of the parent's main |
Why the Customer Tier Is a True Fork
A pull request's head branch must live in the target repository's fork network. A customer who wants to contribute a change back to the service repository can only do so from a GitHub fork of it. That single constraint fixes the creation mechanism: customer repositories are forks, never template instantiations. The fork also inherits every deployed workflow from the parent's main at creation, which is what makes adoption lightweight (see Adoption).
Why Mirror Mode Exists
The filter (ADR-038) reduces an upstream project's tree to the product: it strips the providers the fork does not ship and halts on anything unclassified. The service repository's main already is the product. Running the filter against it would halt on .github/, build/, and the changelog, and would strip provider/, the code a downstream consumer needs most. Mirror mode takes the upstream tip verbatim through the same commit plumbing: merge-shaped commits, Upstream-Sha trailers, and the sentinel Filter-Rev: mirror, so provenance and no-op semantics are identical at both tiers while the engine itself never runs.
The tier is selected by the SYNC_MODE repository variable, set once during adoption. It cannot be inferred from the tree: the filter configuration file exists at both tiers, and at the customer tier it arrives through the mirror itself.
The Development Loop Across Tiers
- Upstream change: the upstream project merges a change; the first tier's filtered sync brings it in; the cascade integrates and releases it to the service repository's
main. - Mirror in: the customer fork's next sync mirrors the new
mainverbatim; their cascade merges it with their local work. - Local feature: the customer develops and proves a feature in their own fork with their own build and deployment.
- Contribute back: the customer opens a PR from their fork to the service repository. A maintainer approves the workflow run, sees real build signal (credential-bearing jobs stay off external heads by ADR-036), and merges.
- Round trip: the merged feature reaches the service repository's
mainand returns to the customer fork through their next mirror sync, where the cascade merge converges their local copy with the incoming change.
See ADR-039 for the full decision record and Adoption for the onboarding runbook.