# Update / Upgrade Flow

## Overview

Every folder under `src/` in this repository is built and published as its **own independent NuGet package** by its **own independent pipeline** (see [`Pipelines.md`](./Pipelines.md)). Components never reference each other via project references — they only ever consume each other's **published NuGet packages**. This means a change to a lower-level package is **not visible** to anything that depends on it until:

1. The lower-level package is built and published, **and**
2. Every dependent package explicitly bumps its NuGet package reference to the new version, is rebuilt, and republishes itself.

Because of the dependency shape described in [`Architecture.md`](./Architecture.md) — `ServiceContracts` (and `Common`) at the bottom, implementation libraries in the middle, `ServiceExtensions` at the top — **a change that starts at the bottom of the graph must be propagated upward through every affected layer, one package release at a time, in order.** You cannot skip a layer: `ServiceExtensions` cannot safely pick up a `ServiceContracts` change until the implementation package(s) it depends on have already picked up and republished with that change.

This document describes that propagation flow and the steps required at each layer. It is written to be followed exactly, in order, by a human contributor **or by an AI coding agent** performing the upgrade on a human's behalf.

## The three layers, and why order matters

| Layer | Example package(s) | Depends on |
|---|---|---|
| 1. Foundation | `SAIF.NetCore.ServiceContracts`, `SAIF.NetCore.Common` | Nothing (SAIF-internal) |
| 2. Implementation | `SAIF.NetCore.AuthService`, `SAIF.NetCore.Logging`, `SAIF.NetCore.HttpClients`, etc. | Foundation packages (`ServiceContracts`/`Common`) |
| 3. Top / app entry point | `SAIF.NetCore.ServiceExtensions` | Foundation packages **and** every implementation package it composes |

Each layer only knows about the **published package version** of the layer(s) below it — never the other repository's source. So if you edit `ServiceContracts` and want that change to eventually reach an application, you must walk **up** the graph, layer by layer, publishing a new package at each step and pulling that new package into the next layer up, until you reach `ServiceExtensions`.

## Step-by-step procedure

### Step 1 — Update the foundation package (if the change requires it)

Only needed if the change touches a shared interface, contract, or POCO in `SAIF.NetCore.ServiceContracts` (or a shared type in `SAIF.NetCore.Common`). Skip straight to Step 2 if the change is entirely internal to one implementation library.

1. Make the code change under `src/SAIF.NetCore.ServiceContracts/` (and/or `src/SAIF.NetCore.Common/`) on a feature branch.
2. Open a pull request into `main` targeting only that component's path (`src/SAIF.NetCore.ServiceContracts/**`).
3. Merge the PR. The component's pipeline (see [`Pipelines.md`](./Pipelines.md)) is CI-triggered automatically by the merge and publishes a new version of the `SAIF.NetCore.ServiceContracts` NuGet package.
4. **Wait for the pipeline to succeed** and note the new package version before proceeding — Step 2 needs it.

### Step 2 — Pull the updated foundation package into each affected implementation project

Repeat this step **once per implementation package** that needs the change (there may be several; check `Architecture.md`'s dependency table for which implementation packages consume the changed contract type).

1. On a feature branch under `src/{implementation-component}/`, bump the `SAIF.NetCore.ServiceContracts` (and/or `SAIF.NetCore.Common`) NuGet reference to the new version published in Step 1.
2. Make whatever implementation code changes are required to compile/work against the updated contract (e.g. implement a new interface member, adjust to a changed signature).
3. Open a pull request into `main`, scoped to that component's path (`src/{implementation-component}/**`).
4. Merge the PR. That component's pipeline publishes a new version of the implementation package.
5. **Wait for the pipeline to succeed** and note the new package version — Step 3 needs it.
6. Repeat for every other implementation package affected by the Step 1 change.

### Step 3 — Pull the updated implementation(s) and foundation package into `ServiceExtensions`

`ServiceExtensions` is the application entry point — nothing above it consumes these packages directly except applications, so this is always the **final, required step** for any change to reach consuming applications, even if the change started in an implementation package rather than a contract.

1. On a feature branch under `src/SAIF.NetCore.ServiceExtensions/`, bump the NuGet references for every implementation package updated in Step 2 (and the foundation package from Step 1, if `ServiceExtensions` references it directly).
2. Make any wiring changes needed (e.g. new DI registrations, updated startup extension methods).
3. Open a pull request into `main`, scoped to `src/SAIF.NetCore.ServiceExtensions/**`.
4. Merge the PR. The `ServiceExtensions` pipeline publishes a new version of the `SAIF.NetCore.ServiceExtensions` package.
5. Applications can now bump their `SAIF.NetCore.ServiceExtensions` package reference to pick up the full, end-to-end change.

## Rules of thumb

- **One package, one PR, one pipeline run, in order.** Never bundle a foundation change and its downstream implementation change into a single PR — they live in different components and are built by different pipelines; each layer's PR should only touch that layer's `src/{component}/` folder.
- **Always wait for a layer's pipeline to succeed and publish before starting the next layer's PR.** The next layer's PR needs to reference a real, published package version, not a guess.
- **Not every change touches all three layers.** A change contained entirely within one implementation package (no contract change) only needs Step 2 (that package) and Step 3 (`ServiceExtensions`, to pick it up) — skip Step 1. A change entirely internal to `ServiceExtensions` only needs Step 3.
- **Identify every downstream consumer before you start.** Before beginning Step 1, check `Architecture.md`'s component table and the package's own NuGet dependents to know which implementation packages (Step 2) and whether `ServiceExtensions` (Step 3, always) will need follow-up PRs.
- **This applies equally to AI agents.** If an AI agent is asked to "upgrade package X", it should walk this same three-step propagation — never modify a downstream package to depend on a not-yet-published upstream version, and never skip publishing/waiting between layers.

## Flow diagram

```mermaid
flowchart TD
    A["Step 1: Modify ServiceContracts / Common<br/>(only if shared contract changes)"] --> B["PR merged to main<br/>(src/ServiceContracts/**)"]
    B --> C["Pipeline builds & publishes<br/>new ServiceContracts package"]

    C --> D["Step 2: Pull new ServiceContracts package<br/>into each affected implementation project"]
    D --> E["Make implementation code changes"]
    E --> F["PR merged to main<br/>(src/{implementation}/**)"]
    F --> G["Pipeline builds & publishes<br/>new implementation package"]

    G --> H["Step 3: Pull updated implementation package(s)<br/>+ ServiceContracts into ServiceExtensions"]
    H --> I["Update DI/startup wiring as needed"]
    I --> J["PR merged to main<br/>(src/ServiceExtensions/**)"]
    J --> K["Pipeline builds & publishes<br/>new ServiceExtensions package"]

    K --> L["Applications bump their<br/>ServiceExtensions package reference"]

    classDef codeChange fill:#e8590c,stroke:#8a3a08,stroke-width:2px,color:#ffffff
    classDef pipeline fill:#2b8a3e,stroke:#1a5c28,stroke-width:2px,color:#ffffff
    classDef appConsume fill:#1864ab,stroke:#0b3d66,stroke-width:2px,color:#ffffff

    class A,D,H codeChange
    class C,G,K pipeline
    class L appConsume
```

- 🟧 Orange boxes: code changes made on a feature branch, resolved by a PR into `main`.
- 🟩 Green boxes: automatic pipeline output — a new NuGet package version.
- 🟦 Blue box: end state — applications can consume the fully-propagated change.
