Files
slp-modular-cms/aidlc-docs/features/slpsoftware-api/construction/plans/slpsoftware-client-setup-code-generation-plan.md
SluijsensandClaude Sonnet 5 fa389e42ee
Continuous Integration / config (pull_request) Successful in 11s
Continuous Integration / changes (pull_request) Successful in 21s
Continuous Integration / backend-build (pull_request) Successful in 6m10s
Continuous Integration / vulnerability-scan (pull_request) Successful in 4m59s
Continuous Integration / frontend-prepare (pull_request) Successful in 1m27s
Continuous Integration / backend-test (pull_request) Failing after 7m48s
Continuous Integration / frontend-build (pull_request) Successful in 2m5s
Continuous Integration / frontend-test (pull_request) Successful in 4m24s
Continuous Integration / frontend-lint (pull_request) Successful in 2m0s
Continuous Integration / publish-test (pull_request) Skipped
Continuous Integration / publish-production (pull_request) Skipped
Continuous Integration / deploy-test (pull_request) Skipped
Continuous Integration / deploy-production (pull_request) Skipped
Adds SlpModularCms.Api.SlpSoftware and extracts shared CmsHost composition
Unit 1 of the slpsoftware-api feature (FR-1/FR-2/FR-3): a new Client project
in the Clients solution folder, intended to eventually become the deployed
API for test.slpsoftware.nl/slpsoftware.nl, hosting the same four modules as
SlpModularCms.Api plus a future Offerings module.

- Extracts SlpModularCms.Api/Program.cs's hosting-pipeline composition into
  SlpModularCms.Core.Hosting.CmsHost (ConfigureServices/ConfigurePipeline),
  shared by both Client projects so they cannot drift apart
- Moves StaticContentExtensions.cs + WebsitePlaceholder.html from Api into
  Core, since CmsHost cannot live in Api but Core cannot depend on Api
- Adds SlpModularCms.Api.SlpSoftware with its own isolated local dev database
  and dev ports (5286/7223, distinct from Api's and Api.Slave's)
- Adds SlpModularCms.Api.Tests with WebApplicationFactory-based pipeline
  regression tests (security headers, health check, SPA fallback, rate
  limiting), scoped to Api per NFR Design
- Adds a frontend dev:slpsoftware pnpm script mirroring dev:slave
- Fixes GlobalExceptionHandler logging routine 401s (e.g. an expired/missing
  refresh token) as unhandled errors -- pre-existing, unrelated to this
  feature's own scope, found while testing the new instance

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWyStNL2ZsjrS7FLd7xvvN
2026-08-02 01:28:39 +02:00

10 KiB
Raw Permalink Blame History

Code Generation Plan — Unit: SlpSoftware Client Setup

Unit Context

Stories implemented: None directly (unit-of-work-story-map.md — this unit is a purely technical enabling unit). Functional requirements implemented: FR-1, FR-2, FR-3. Dependencies: None on other units (this unit is the dependency Unit 2 "Offerings" needs). Expected interfaces produced: SlpModularCms.Core.Hosting.CmsHost (ConfigureServices/ConfigurePipeline), consumed by both Client projects' Program.cs. Database entities owned: None. Workspace root: K:\Development\Projects\SlpModularCms (brownfield — modify/move existing files where noted, never duplicate).

Investigation Finding That Shapes This Plan

Reading SlpModularCms.Api/Program.cs and SlpModularCms.Api/Extensions/StaticContentExtensions.cs directly (not assumed) revealed that UseCmsStaticContent()/MapCmsSpaFallbacks() — needed by CmsHost.ConfigurePipeline — currently live in the Api project itself, not in Core. Since Core cannot depend on Api (wrong dependency direction — Api depends on Core, never the reverse), this file (and its embedded WebsitePlaceholder.html resource) must move into Core before CmsHost can call it. This is Step 1 below, not an afterthought.


Steps

Step 1 — Move Static Content Hosting into Core (prerequisite for CmsHost)

  • Move src/SlpModularCms.Api/Extensions/StaticContentExtensions.cssrc/SlpModularCms.Core/Hosting/StaticContentExtensions.cs; change namespace SlpModularCms.Api.ExtensionsSlpModularCms.Core.Hosting; update PlaceholderResourceName from "SlpModularCms.Api.Extensions.WebsitePlaceholder.html" to "SlpModularCms.Core.Hosting.WebsitePlaceholder.html"
  • Move src/SlpModularCms.Api/Extensions/WebsitePlaceholder.htmlsrc/SlpModularCms.Core/Hosting/WebsitePlaceholder.html
  • SlpModularCms.Core.csproj: add <EmbeddedResource Include="Hosting\WebsitePlaceholder.html" />
  • SlpModularCms.Api.csproj: remove the now-obsolete <EmbeddedResource Include="Extensions\WebsitePlaceholder.html" /> item group and its explanatory comment (the file no longer lives there)
  • Delete the now-empty src/SlpModularCms.Api/Extensions/ directory if nothing else remains in it

Step 2 — CmsHostOptions (Business Logic Generation — Core)

  • Create src/SlpModularCms.Core/Hosting/CmsHostOptions.cs: an intentionally empty class (NFR Design Pattern 2 / Q2 = B) — a pure extension point, no properties yet

Step 3 — CmsHost (Business Logic Generation — Core)

  • Create src/SlpModularCms.Core/Hosting/CmsHost.cs with:
    • public static ModuleOrchestrator ConfigureServices(WebApplicationBuilder builder, CmsHostOptions options) — reproduces Api/Program.cs lines for: appsettings.local.json loading stays in each project's own Program.cs (NOT moved here — application-design.md component-methods.md is explicit that bootstrap lines stay per-project); logging (AddCmsLogging), Sentry (UseCmsSentry), ModuleOrchestrator construction + DiscoverModules(), AddCoreInfrastructure, AddCmsCors, AddCmsRateLimiting, AddCmsHealthChecks, AddCmsSecurityHeaders, AddCmsObservability, AddCmsDataProtection (before module services — order preserved exactly), RegisterModuleServices, AddSingleton(orchestrator), AddControllers with ApiPrefixConvention("api/v1") + JsonStringEnumConverter. Returns the orchestrator.
    • public static void ConfigurePipeline(WebApplication app, ModuleOrchestrator orchestrator, CmsHostOptions options) — reproduces: MigrateCoreDatabase, UseExceptionHandler, UseCmsSecurityHeaders, UseRateLimiter, Development-only MapOpenApi/MapScalarApiReference, UseHttpsRedirection, UseCmsStaticContent, UseCors, orchestrator.UseModules(app), UseAuthentication/UseAuthorization, MapControllers, MapCmsHealthChecks, MapSentryTunnel, MapCmsSpaFallbacks — same order as today's Program.cs, since that order encodes real constraints documented in its comments
  • Both methods accept CmsHostOptions per NFR-CS-02, even though it currently has no properties to read

Step 4 — Repoint SlpModularCms.Api/Program.cs (Modify In-Place)

  • Rewrite src/SlpModularCms.Api/Program.cs to the thin form: create builder, load appsettings.local.json, var orchestrator = CmsHost.ConfigureServices(builder, new CmsHostOptions());, var app = builder.Build();, CmsHost.ConfigurePipeline(app, orchestrator, new CmsHostOptions());, app.Run();
  • No behavior change — verified by Step 7's regression tests

Step 5 — New Client Project: SlpModularCms.Api.SlpSoftware (Project Structure Setup)

  • Create src/SlpModularCms.Api.SlpSoftware/SlpModularCms.Api.SlpSoftware.csproj — mirrors SlpModularCms.Api.csproj (SDK, TargetFramework, Nullable, ImplicitUsings, same package references: Asp.Versioning.Mvc, Microsoft.AspNetCore.Authentication.JwtBearer, Microsoft.AspNetCore.OpenApi, Microsoft.EntityFrameworkCore.Design, Scalar.AspNetCore), without the WebsitePlaceholder.html embedded resource (that now lives in Core, shared) and without a Modules.Offerings reference (added later by Unit 2, per unit-of-work.md)
    • ProjectReference: SlpModularCms.Core, SlpModularCms.Modules.Availability, SlpModularCms.Modules.Identity, SlpModularCms.Modules.Master (FR-2)
  • Create src/SlpModularCms.Api.SlpSoftware/Program.cs — same thin shape as Step 4's rewritten Api/Program.cs
  • Create src/SlpModularCms.Api.SlpSoftware/appsettings.json — mirrors Api's structure/keys (connection string placeholder, JWT settings, Availability, MasterModule, MasterPolling, Cors, RateLimiting, SecurityHeaders, Observability sections)
  • Create src/SlpModularCms.Api.SlpSoftware/appsettings.Development.json — mirrors Api's Development file, but with its own isolated local dev database name (Infrastructure Design Q1 = B), e.g. Database=SlpModularCmsSlpSoftwareDev
  • Not creating appsettings.local.json — it's git-ignored (verified: listed in .gitignore, not tracked in git) and personal per-developer; the developer creates their own copy locally if needed, same as for Api
  • Not creating Program.Coverage.cs for this project in this unit — Infrastructure/NFR Design (Q1 = C) scoped the new pipeline regression tests to Api only, so there is no test target requiring Program to be a public partial class here yet; add it in a future unit/feature if Api.SlpSoftware-specific pipeline tests are ever introduced

Step 6 — Solution File Updates (SlpModularCms.sln)

  • Add SlpModularCms.Api.SlpSoftware project entry, nested under the existing (currently empty) Clients solution folder ({D72703E6-B021-4360-B1EE-0E99999B5899})
  • Add SlpModularCms.Api.Tests project entry (Step 7), nested directly under Tests ({2F43D186-C7D5-4AB1-B821-4D595CA2ECB3}) — mirroring how SlpModularCms.Core.Tests is nested directly under Tests, not under Tests/Modules
  • Add both new projects' GUIDs to ProjectConfigurationPlatforms (Debug/Release × Any CPU/x64/x86, matching the existing pattern for every other project)
  • Add both new projects' GUIDs to NestedProjects

Step 7 — Pipeline Regression Tests (Business Logic Unit Testing, NFR-CS-01)

  • Create src/SlpModularCms.Api.Tests/SlpModularCms.Api.Tests.csproj — same SDK-style shape as SlpModularCms.Core.Tests.csproj (xunit, FluentAssertions, Microsoft.NET.Test.Sdk, coverlet.collector), plus Microsoft.AspNetCore.Mvc.Testing (provides WebApplicationFactory<TEntryPoint>); ProjectReference to SlpModularCms.Api.csproj (its Program.Coverage.cs already makes Program a public partial class, so no further change needed there)
  • Create src/SlpModularCms.Api.Tests/PipelineTests.cs using WebApplicationFactory<Program>, asserting (NFR-CS-01 / NFR Design Pattern 1):
    • Required security headers (CSP, HSTS, X-Content-Type-Options, X-Frame-Options, Referrer-Policy) present on a representative response
    • /health returns a successful response
    • A non-file /admin/{path} route resolves to the admin SPA's index.html fallback (or, absent a built SPA in the test environment, at minimum does not 404 as a missing-file/static-asset request would)
    • A burst of requests against a rate-limited route eventually receives 429 Too Many Requests

Step 8 — Business Logic Summary

  • Create aidlc-docs/features/slpsoftware-api/construction/slpsoftware-client-setup/code/summary.md (markdown only) summarizing: files moved (Step 1), CmsHost/CmsHostOptions added, Api/Program.cs rewritten, Api.SlpSoftware created, solution file changes, tests added

Step 9 — API Layer / Repository Layer Generation

  • N/A — this unit introduces no new HTTP endpoints or persisted entities (FR-1/FR-2/FR-3 are pure composition/project-scaffolding); these categories apply to Unit 2 "Offerings" instead

Step 10 — Database Migration Scripts

  • N/A — no new data model in this unit

Step 11 — Documentation Generation

  • Update root README.md: add SlpModularCms.Api.SlpSoftware to the project-structure description alongside SlpModularCms.Api/SlpModularCms.Api.Slave, and add a short note under the existing architecture/hosting section explaining CmsHost as the shared composition point both Client projects call, plus a one-line note that Api.SlpSoftware uses its own local dev database (Infrastructure Design Q1 = B)

Step 12 — Deployment Artifacts Generation

  • N/A for this Construction stage — per Infrastructure Design, no .gitea/workflows/*.yaml or Gitea Actions variable changes happen here; the CI/CD retarget is explicitly Operations-phase work (D-7/D-15)

Step 13 — Build and Test Verification (automatic, Step 13.5 of the workflow)

  • Build the full solution (or at minimum Api, Api.SlpSoftware, Core, Api.Tests) and confirm it compiles
  • Run SlpModularCms.Api.Tests (new) and confirm all pipeline regression tests pass against Api
  • Run SlpModularCms.Core.Tests (existing) and confirm nothing regressed from the Step 1 file move
  • Fix and retry on any failure; only surface to the user if a fix requires a decision only they can make

Scope reminder: this plan implements Unit 1 only. Unit 2 "Offerings" (all 12 user stories) is a separate Code Generation pass, after this unit is approved and its own Build and Test step is green.