# Unit of Work Dependencies --- ## Dependency Diagram ```mermaid graph TD u1["U1 Hosting and Serving"] u2["U2 Data Durability"] u3["U3 Security Headers and CSP"] u4["U4 Observability"] u5["U5 CI Workflow and Gates"] u6["U6 Deploy Workflow"] u7["U7 Documentation"] ops["Operations Phase"] u1 -->|"path layout for CSP scoping"| u3 u4 -->|"Umami and Sentry origins"| u3 u4 -->|"env-specific Vite variables"| u5 u1 --> u6 u2 -->|"durability must precede first deploy"| u6 u3 --> u5 u5 -->|"invokes"| u6 u6 -->|"settled host layout"| u7 u6 --> ops u7 --> ops classDef r1 fill:#9ae6b4,stroke:#2f855a,stroke-width:1px,color:#000; classDef r2 fill:#90cdf4,stroke:#2b6cb0,stroke-width:1px,color:#000; classDef r3 fill:#fbd38d,stroke:#c05621,stroke-width:1px,color:#000; classDef r4 fill:#d6bcfa,stroke:#6b46c1,stroke-width:1px,color:#000; classDef opsphase fill:#f6e05e,stroke:#c05621,stroke-width:1px,color:#000; class u1,u2 r1; class u3,u4 r2; class u5,u6 r3; class u7 r4; class ops opsphase; ``` Text alternative: U1 and U2 are independent and run first; U3 needs U1's path layout and U4's origins; U5 and U6 need the application work complete; U7 needs U6's settled host layout; the Operations phase follows. --- ## Dependency Matrix | Unit | Depends on | Depended on by | Nature of dependency | |---|---|---|---| | **U1** Hosting & Serving | — | U3, U6 | Establishes the path layout that U3's CSP scopes against and U6 deploys into | | **U2** Data Durability | — | U6 | Must land before any automated deploy, or the first atomic switch destroys the key ring | | **U3** Security Headers | U1, U4 | U5 | Needs U1's final paths and U4's external origins | | **U4** Observability | — | U3, U5 | Introduces the origins U3 must permit and the build variables U5 must supply | | **U5** CI Workflow | U3, U4 | U6 | Gates must pass against the finished application; the two builds need U4's variables | | **U6** Deploy Workflow | U1, U2, U5 | U7, Operations | Deploys what the application units produced, invoked by U5 | | **U7** Documentation | U6 | Operations | Documents the layout U6 settles | --- ## Ordering Constraints That Are Load-Bearing These are not preferences. Reordering any of them produces a broken or dangerous result. ### 1. U2 before U6 — otherwise the first deploy is the dangerous one The atomic release switch (U6) changes the content root path on every deploy. Until U2 configures a database-backed key ring **with an explicit application discriminator**, that switch discards the Data Protection keys and makes every stored slave API key undecryptable. The symptom presents as a network fault between master and slave, so it would be misdiagnosed. Deploying first and hardening afterwards means the very first production deploy carries the failure. ### 2. U4 before or with U3 — otherwise the CSP is written blind U3's `Strict` policy must permit the Umami script origin and the Sentry ingest origin. Those origins are introduced by U4. Writing U3 first means either guessing them or shipping a CSP that blocks the observability U4 then adds — a failure that appears only in a real browser. This is why R2 groups them rather than running them in sequence. ### 3. U1 before U3 — otherwise path scoping is provisional U3 assigns policies by path prefix. Until U1 settles which paths exist and where they are served from, that assignment is written against a layout still in flux. ### 4. U3 and U4 before U5 — otherwise the gates fail on incomplete work U5's six gates run against the whole application. Switching them on before the application units are complete produces failures that reflect unfinished work rather than defects. ### 5. U5 with U6 — one interface, two files U6 is a reusable workflow invoked by U5 with a fixed input set. Designing them apart risks an interface mismatch that only surfaces on the first real run. ### 6. U6 before U7 — documentation cannot precede the layout it documents U7's website contract states target paths, reserved paths and the persistent-directory arrangement. U6 settles those in its Infrastructure Design. --- ## What Is *Not* Dependent Worth stating explicitly, because it justifies the grouping: - **U1 and U2 do not touch each other.** U1 changes serving and middleware; U2 changes persistence and startup. They share `Program.cs` as a file, but not as logic — U1 adds pipeline and endpoint registrations, U2 adds service registration and a startup call. A merge conflict is possible; a behavioural conflict is not. - **U4 does not depend on U1, U2 or U3.** Observability can be added to the application as it stands today. - **U7 does not depend on U3, U4 or U5** beyond describing their results. --- ## Shared Resources and Coordination Points | Resource | Touched by | Coordination needed | |---|---|---| | `SlpModularCms.Api/Program.cs` | U1, U2, U3, U4 | Four units modify the same file in different places. Registration and pipeline order is specified in `services.md` § S-01, so each unit inserts at a defined position rather than appending | | `SlpModularCms.Api.Slave/Program.cs` | U1 (health only), U2, U3, U4 | Same, minus the static mounts. The Slave is a reference instance (Q2 of Application Design = A) and must keep working; it has no test project, so it is verified by starting it | | `SlpModularCms.Core` | U1, U2, U3, U4 | Each unit adds its own subfolder under `Hosting/` — `Health/`, `Security/`, `Observability/` — so files do not collide | | `AvailabilityModule.cs` / `MasterModule.cs` | U2 | Removing `AddDataProtection()` from both. No other unit touches them | | `appsettings.json` | U2, U3, U4 | Three new sections. Additive, no overlap | | `frontend/` | U4 (features), U5 (lint fixes) | U5's lint fixes touch `AddCmsInstanceDialog.tsx`, `InviteUserDialog.tsx`, `SettingsPage.tsx` and `SetStatusDialog.tsx`; U4 touches `main.tsx`, `config.ts` and adds a Umami component. **No overlapping files** | | `.gitea/workflows/` | U5, U6 | Separate files sharing one input contract | --- ## Consequence of Merging the Quality Gates into U5 Q2 = B moved the lint fixes and package pins from a standalone first unit into U5. This is coherent — gates and their prerequisites land in one commit, so the pipeline is never red on arrival — but it has one side effect worth managing: **`pnpm run lint` stays failing through U3 and U4**, and U4 changes frontend files. New violations introduced during U4 would therefore hide among the five pre-existing ones. **Mitigation**: run lint on the **changed files** during U4 rather than the whole tree. The blocking gate still arrives with U5, but nothing new accumulates in the meantime. The overlap is limited: U5's fixes and U4's changes touch disjoint files, so there is no merge risk — only a detection gap. --- ## Rollback Between Units Every unit is a self-contained commit on `feature/gitea-deployment-workflow`, with a single pull request at the end (Q6 = A). | Unit | Revertible independently? | Notes | |---|---|---| | U1 | Yes | Pipeline and endpoint registrations | | U2 | Yes, with care | The Core migration adds a table; reverting the code leaves the table in place, which is harmless | | U3 | Yes | Additive middleware plus one config section | | U4 | Yes | Additive | | U5 | Yes | New file plus lint and package changes | | U6 | Yes | New file only | | U7 | Yes | Documentation only | **Nothing is deployed to any environment until U6 is complete and explicitly triggered**, so a mid-sequence failure cannot affect a running environment.