Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/publish-packages-preview.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Hand-written. The "preview" channel — `main` (ADR-0004, amended 2026-05-30).
# Hand-written. The "preview" channel — `main` (ADR-0004 as deferred by ADR-0012).
#
# Shape:
# - Trigger: push to `main`. `main` is the integration trunk / `-preview`
# channel — the deliberate lane where work stabilises. Nerdbank.GitVersioning
# produces a prerelease of the form `2026.1.0-preview.<height>.g<commit>`
# produces a prerelease of the form `10.5.0-preview.<height>.g<commit>`
# (`main` is intentionally NOT a public-release ref in version.json), so these
# never look like a stable release.
# - Builds + packs, then pushes every *.nupkg to GitHub Packages ONLY.
Expand Down
18 changes: 9 additions & 9 deletions .github/workflows/publish-packages-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
#
# Shape:
# - Trigger: tag push `v*` on a production branch — CalVer production lines
# (release/2026 → tag v2026.1.3) or gitflow support lines (support/v10 → v10.x,
# support/2026 → v2026.x). main is NEVER tagged for release.
# (release/v10.5 → tag v10.5.3) or legacy support lines (support/v10 → v10.x).
# main is NEVER tagged for release.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 In GitFlow, the release/v10.5 branch is only used to stabilize the release and push rc packages. Releasing means merging to main and tagging main with v10.5.3.

# Tags keep the `v` prefix so the `v*` tag-protection ruleset applies; the
# package version core is the bare CalVer/semver (e.g. 2026.1.3). Tag
# package version core is the bare semver (e.g. 10.5.3). Tag
# protection restricts who can create those tags to repo admins.
# - Optional fallback: workflow_dispatch with an existing-tag input, for
# re-runs after a transient publish-API failure, OR to opt into a
Expand All @@ -26,7 +26,7 @@
# - nuget.org is OPT-IN. Tag pushes do NOT auto-publish there. To publish

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ What does this mean?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably the approval step in the gh environment representing nuget.org
Definitely need to look into this though, I have a feeling the CI there is slightly off too

The idea was:
build -> test -> pack -> publish to gh (because we can delete here if things go BOOM)
and then have approval gated the likes of nuget.org (because we cant just undo things here, so I would like to have a human approve aka HITL: Human-in-the-Loop)

# Fallout.* to nuget.org you invoke workflow_dispatch with
# publish-to-nugetorg=true — a conscious "this release is stabilised enough"
# switch, used when a release/YYYY production line is ready for the broad
# switch, used when a production line is ready for the broad
# consumer audience, or for a support/v10 legacy security patch.
# - GitHub Packages gets every Fallout.* and Nuke.* build via tag push.
# - GitHub Releases bundles nupkgs on the tag's release page regardless.
Expand Down Expand Up @@ -78,13 +78,13 @@ jobs:
run: |
set -euo pipefail
TAG_SHA="${{ github.sha }}"
# Match CalVer production lines (release/2026, …), legacy-style minor
# release lines (release/v10.4, …), and gitflow support lines
# (support/v10 legacy semver, support/2026 retired years, …).
# Match production lines (release/v10.4, …) and legacy support lines
# (support/v10, …). The \d{4} alternatives are retained for the CalVer
# North Star (ADR-0012) and match nothing today.
REACHABLE=$(git branch -r --contains "$TAG_SHA" | grep -E 'origin/(release/(v[0-9]+\.[0-9]+|[0-9]{4})|support/(v[0-9]+|[0-9]{4}))' || true)
if [ -z "$REACHABLE" ]; then
echo "::error::Tag ${GITHUB_REF_NAME} at $TAG_SHA is not reachable from any production branch."
echo "Tag-triggered releases only fire from release/YYYY (production) or support/* (legacy/retired) branches."
echo "Tag-triggered releases only fire from release/* (production) or support/* (legacy) branches."
exit 1
fi
echo "Tag ${GITHUB_REF_NAME} validated. Reachable from:"
Expand Down Expand Up @@ -139,7 +139,7 @@ jobs:
# in via the input flag, you still approve the deployment.
#
# The opt-in defaults to false because the default channel is GitHub Packages
# (see header comment). When a release/YYYY production line is ready for the
# (see header comment). When a production line is ready for the
# broad audience — or for cutting a support/v10 maintenance patch — invoke
# workflow_dispatch with the flag set.
#
Expand Down
22 changes: 13 additions & 9 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@ Originally NUKE by [matkoch](https://github.com/matkoch); under new maintenance

**Rebrand status:** the structural rename has landed — namespaces (`Fallout.*`), package IDs, project filenames, and the global tool name (`dotnet fallout`) are all in place. Legacy `Nuke.*` lives on only as the consumer transition shims under `src/Shims/`. The mapping is a strict 1:1 prefix swap — every `Nuke.X.Y.Z` namespace and assembly is `Fallout.X.Y.Z`, with no consolidation — which `[TypeForwardedTo]` in the shims locks in place. Consumer-facing migration is [docs/migration/from-nuke.md](docs/migration/from-nuke.md).

**Versioning & channels (calendar versioning, two-tier ladder — [ADR-0004](docs/adr/0004-calendar-versioning-and-dual-pace-channels.md), channel ladder superseded by [ADR-0008](docs/adr/0008-collapse-experimental-into-main.md)).** The project ships on **calendar versions `YYYY.MINOR.PATCH`** (mechanically valid semver; major = year). A maturity ladder feeds the production line — **GitHub Packages = test/preview; nuget.org = production**:
- **`main` = the integration trunk + sole `-preview` channel** — default branch; both deliberate improvements/bug fixes *and* faster AI-assisted work land here. Per-commit `-preview` prereleases (`2026.1.0-preview.<height>.g<commit>`) to **GitHub Packages only — never nuget.org**. Ordinary review. (The dedicated `experimental` `-alpha` lane was removed by ADR-0008 — it ran behind `main` and carried no unique work.)
- **`release/YYYY` = the production line** — **cut from `main` on demand at the first release of the year, not preemptively** ([ADR-0007](docs/adr/0007-cut-release-branch-on-demand.md)); until then `main` (`-preview`) is the most-stable line. Hardened deliberately, `-rc.N` → GA, non-breaking minors/patches only after the cut, rigorous review. Tags publish to nuget.org (opt-in) + GitHub Packages + GitHub Releases.
- **Breaking changes are batched to the yearly major cut** — they accumulate on `main` gated behind `[Experimental("FALLOUT0xx")]` (or a short-lived topic branch off `main` when they can't be gated) and ship as next year's `YYYY+1.0.0`. Mid-year `main`/production is strictly non-breaking; the production-cut review is the backstop. Version ladder: `-preview` < `-rc` < GA.
- **Legacy `support/v10`** (renamed from `release/v10`; + `hotfix/v10.x`) stays on semver `10.x`, security/critical fixes only; retired year lines become **`support/YYYY`**. **`release/v11` is retired and its branch removed** (nothing clean shipped; its work re-homed onto the 2026 line) — dead branches with no unique history are now deletable, tags are the durable release markers ([ADR-0007](docs/adr/0007-cut-release-branch-on-demand.md) §6).
- Opt-in unstable public APIs are marked `[Experimental("FALLOUT0xx")]` and can ride any channel; promoting to stable = removing the attribute.
**Versioning & channels — CURRENT STATE ([ADR-0012](docs/adr/0012-current-state-semver-10x-north-star-calver-gitflow.md)).** The project ships **semver on the `10.x` line**, trunk-based. A maturity ladder feeds the production line — **GitHub Packages = test/preview; nuget.org = production**:
- **`main` = the integration trunk + sole `-preview` channel** — default branch; both deliberate improvements/bug fixes *and* faster AI-assisted work land here. Per-commit `-preview` prereleases (`10.5.0-preview.<height>.g<commit>`) to **GitHub Packages only — never nuget.org**. Ordinary review. (The dedicated `experimental` `-alpha` lane was removed by [ADR-0008](docs/adr/0008-collapse-experimental-into-main.md) — it ran behind `main` and carried no unique work.)
- **`release/v<major>.<minor>` = the production line** (currently `release/v10.4`; next `release/v10.5`) — **cut from `main` on demand at the first release of the line, not preemptively** ([ADR-0007](docs/adr/0007-cut-release-branch-on-demand.md)); until then `main` (`-preview`) is the most-stable line. Hardened deliberately, `-rc.N` → GA, non-breaking minors/patches only after the cut, rigorous review. Tags publish to nuget.org (opt-in) + GitHub Packages + GitHub Releases.
- **A release branch pins the full version literally** — `"10.4.0-rc.4"`, then `"10.4.0"` at GA. Bump it by hand for each candidate; `{height}` is not used there. **`main`'s preview core rolls forward in the same sitting as the cut** — skipping that strands the preview lane below the shipped release. Both rules and the detached-HEAD `PublicRelease` trap are in [docs/versioning.md](docs/versioning.md).
- **Breaking changes are batched to the next major**, on no fixed calendar — they accumulate on `main` gated behind `[Experimental("FALLOUT0xx")]` (or a short-lived topic branch off `main` when they can't be gated). Mid-year `main`/production is strictly non-breaking; the production-cut review is the backstop. Version ladder: `-preview` < `-rc` < GA.
- **Legacy `support/v10`** (renamed from `release/v10`; + `hotfix/v10.x`) stays on `10.3.x`, security/critical fixes only, and does **not** retire when a newer line is cut. **`release/v11` is retired and its branch removed** (nothing clean shipped) — dead branches with no unique history are deletable, tags are the durable release markers ([ADR-0007](docs/adr/0007-cut-release-branch-on-demand.md) §6). `11.0.1`–`11.0.18` are burned on nuget.org and can never be reused.
- Opt-in unstable public APIs are marked `[Experimental("FALLOUT0xx")]` and can ride any channel; promoting to stable = removing the attribute. The mechanism is wired up; routine use is aspirational (one usage today, `FALLOUT001`).

**Active work** — rebrand completion + plugin-architecture internal foundation ([milestone #6](https://github.com/Fallout-build/Fallout/milestone/6)), now shipping on the `2026` line. **No public plugin SDK yet** — that's a later major ([milestone #7](https://github.com/Fallout-build/Fallout/milestone/7)). Internal middleware/listener interfaces stay `internal`; do not expose via `InternalsVisibleTo` to non-test assemblies. See [docs/roadmap.md](docs/roadmap.md) and the five open RFCs ([#97](https://github.com/Fallout-build/Fallout/issues/97)–[#101](https://github.com/Fallout-build/Fallout/issues/101)).
**NORTH STAR — not implemented.** **Calendar versioning** (`YYYY.MINOR.PATCH`, `release/YYYY`, breaking changes batched to the yearly cut — [ADR-0004](docs/adr/0004-calendar-versioning-and-dual-pace-channels.md)) and **full GitFlow** (a long-lived `develop` as integration trunk and preview lane; `main` holds released code only). The two must land together, and GitFlow requires superseding ADR-0008. Do **not** write code, docs, or PR descriptions as though either is in force — see [docs/branching-and-release.md](docs/branching-and-release.md#north-star).

**Active work** — rebrand completion + plugin-architecture internal foundation ([milestone #6](https://github.com/Fallout-build/Fallout/milestone/6)), shipping on the `10.x` line. **No public plugin SDK yet** — that's a later major ([milestone #7](https://github.com/Fallout-build/Fallout/milestone/7)). Internal middleware/listener interfaces stay `internal`; do not expose via `InternalsVisibleTo` to non-test assemblies. See [docs/roadmap.md](docs/roadmap.md) and the five open RFCs ([#97](https://github.com/Fallout-build/Fallout/issues/97)–[#101](https://github.com/Fallout-build/Fallout/issues/101)).

## Stack

Expand Down Expand Up @@ -49,8 +52,8 @@ To restructure an existing PR's commit history into focused commits, use the `/r

## Critical rules (read this every session)

1. **At PR-creation time, follow the [PR-creation flow](docs/agents/release-and-versioning.md#pr-creation-flow) in `docs/agents/release-and-versioning.md`.** That flow covers working from a fork (branch off `upstream/main`, push to `origin`, PR against `upstream`), creating the PR as a draft by default, and labelling. Every PR gets a `target/vCurrent` label (or `target/vNext` for work held to next year's major) and a changelog-category label — [`.github/release.yml`](.github/release.yml) is the source of truth for the label taxonomy and AI applies the matching one whenever it raises a PR. Breaking changes additionally get a `breaking-change` label and a `⚠️ Breaking change` callout in the PR description naming the migration path — the label is what carries them into the generated release notes, and they are held for the **next yearly major** (breaking changes are batched to the year cut, not shipped mid-year). A breaking-change PR targets **`main`** with the breaking surface gated behind `[Experimental("FALLOUT0xx")]` (or, when it can't be gated, on a short-lived topic branch off `main` held for the year cut) — **never** a `release/YYYY` production train. This is non-negotiable — review will block. (Before [ADR-0008](docs/adr/0008-collapse-experimental-into-main.md) breaking work targeted the `experimental` branch; that branch is gone.)
2. **Default to backwards compatibility.** Prefer additive over breaking changes. Before changing a public signature, removing an API, renaming a package, or altering an on-disk format, ask: can this be additive instead? `[Obsolete]` markers, transition shims (see `src/Shims/` + `Fallout.SourceGenerators.TransitionShimGenerator`), the `[Experimental("FALLOUT0xx")]` opt-in escape hatch for not-yet-stable surface, feature flags, and overload-based extension are all preferred to a hard break. When a breaking change is genuinely unavoidable, it lands on `main` (gated behind `[Experimental("FALLOUT0xx")]`, or held on a short-lived topic branch for the next yearly major), and follows rule #1's flow — the break must be deliberate, named, and migration-pathed in the PR description. See [#262](https://github.com/Fallout-build/Fallout/issues/262) for the broader discussion. The `[Experimental]` convention (diagnostic-ID scheme + registry) is documented in [docs/agents/conventions.md](docs/agents/conventions.md#experimental-for-opt-in-unstable-apis) and [docs/experimental-apis.md](docs/experimental-apis.md). Deprecations use `[Obsolete]` with a `FALLOUTOBS0xx` `DiagnosticId` so `TreatWarningsAsErrors` consumers can suppress a single deprecation — see [docs/agents/conventions.md](docs/agents/conventions.md#obsolete-for-deprecating-public-apis) and the [docs/obsolete_apis.md](docs/obsolete_apis.md) registry.
1. **At PR-creation time, follow the [PR-creation flow](docs/agents/release-and-versioning.md#pr-creation-flow) in `docs/agents/release-and-versioning.md`.** That flow covers working from a fork (branch off `upstream/main`, push to `origin`, PR against `upstream`), creating the PR as a draft by default, and labelling. Every PR gets a `target/vCurrent` label (or `target/vNext` for work held to the next major) and a changelog-category label — [`.github/release.yml`](.github/release.yml) is the source of truth for the label taxonomy and AI applies the matching one whenever it raises a PR. Breaking changes additionally get a `breaking-change` label and a `⚠️ Breaking change` callout in the PR description naming the migration path — the label is what carries them into the generated release notes, and they are held for the **next major** (breaking changes are batched to a major cut, not shipped mid-line). A breaking-change PR targets **`main`** with the breaking surface gated behind `[Experimental("FALLOUT0xx")]` (or, when it can't be gated, on a short-lived topic branch off `main` held for the cut) — **never** a `release/v<major>.<minor>` production train. This is non-negotiable — review will block. (Before [ADR-0008](docs/adr/0008-collapse-experimental-into-main.md) breaking work targeted the `experimental` branch; that branch is gone.)
2. **Default to backwards compatibility.** Prefer additive over breaking changes. Before changing a public signature, removing an API, renaming a package, or altering an on-disk format, ask: can this be additive instead? `[Obsolete]` markers, transition shims (see `src/Shims/` + `Fallout.SourceGenerators.TransitionShimGenerator`), the `[Experimental("FALLOUT0xx")]` opt-in escape hatch for not-yet-stable surface, feature flags, and overload-based extension are all preferred to a hard break. When a breaking change is genuinely unavoidable, it lands on `main` (gated behind `[Experimental("FALLOUT0xx")]`, or held on a short-lived topic branch for the next major), and follows rule #1's flow — the break must be deliberate, named, and migration-pathed in the PR description. See [#262](https://github.com/Fallout-build/Fallout/issues/262) for the broader discussion. The `[Experimental]` convention (diagnostic-ID scheme + registry) is documented in [docs/agents/conventions.md](docs/agents/conventions.md#experimental-for-opt-in-unstable-apis) and [docs/experimental-apis.md](docs/experimental-apis.md). Deprecations use `[Obsolete]` with a `FALLOUTOBS0xx` `DiagnosticId` so `TreatWarningsAsErrors` consumers can suppress a single deprecation — see [docs/agents/conventions.md](docs/agents/conventions.md#obsolete-for-deprecating-public-apis) and the [docs/obsolete_apis.md](docs/obsolete_apis.md) registry.
3. **Central package versions only** — add to `Directory.Packages.props`, never `Version=` inline.
4. **Tests next to code** — every `src/Foo` has a `tests/Foo.Tests` sibling. Mirror namespaces.
5. **Stay on xUnit + FluentAssertions + Verify.** Don't introduce new test frameworks.
Expand All @@ -65,6 +68,7 @@ Full conventions + what-not-to-do list: [docs/agents/conventions.md](docs/agents

- **[docs/agents/repository-layout.md](docs/agents/repository-layout.md)** — full directory structure, project groupings, transition-shim strategy
- **[docs/agents/release-and-versioning.md](docs/agents/release-and-versioning.md)** — branching, semver policy, PR-creation flow, release pipeline, NuGet gotchas
- **[docs/versioning.md](docs/versioning.md)** — where version numbers come from (nbgv, git height, `-rc.N` pinning) and the two traps that have shipped bugs. Read before touching `version.json` or cutting a release.
- **[docs/branching-and-release.md](docs/branching-and-release.md)** — maintainer runbook for cutting releases, hotfixing older majors, cutting new `release/vN` branches
- **[docs/adr/](docs/adr/)** — Architecture Decision Records (read `0004-calendar-versioning-and-dual-pace-channels.md` and `0001-release-branch-model.md` for the release model)
- **[docs/agents/conventions.md](docs/agents/conventions.md)** — conventions, what-not-to-do list, tool-wrapper recipe
Expand Down
Loading
Loading