Host setup checklist, real domains and ports, the database backup script the deploy workflow only ever invokes, and a rollback plan. Also drafted the FTPS switch procedure for whenever production moves off the Pi, with a note that shared hosting is likely IIS-based - that's a bigger change than swapping the transport.
14 KiB
14 KiB
AI-DLC State Tracking
Project Information
- Feature Name: Gitea Deployment Workflow
- Feature Slug: gitea-deployment-workflow
- Project Type: Brownfield
- Start Date: 2026-07-27T00:00:00Z
- Current Stage: CONSTRUCTION - Code Generation complete for Round 2 (U3 + U4)
- Branch: feature/gitea-deployment-workflow
Workspace State
- Existing Code: Yes
- Reverse Engineering Needed: Completed — full rerun on 2026-07-27 (user chose Q3 = B)
- Workspace Root: K:\Development\Projects\SlpModularCms
Reverse Engineering Status
- Reverse Engineering — Completed on 2026-07-27
- Artifacts Location: aidlc-docs/_shared/reverse-engineering/ (all 8 artifacts regenerated + timestamp)
- Verified by execution: Release build 0 errors / 50 warnings; 219 backend tests pass; 213 frontend tests pass;
pnpm run lintfails (5 errors, 1 warning); 2 high-severity transitive package advisories
Code Location Rules
- Application Code: Workspace root (NEVER in aidlc-docs/)
- Feature Documentation: aidlc-docs/features/gitea-deployment-workflow/ only
- Shared Artifacts: aidlc-docs/_shared/
- Structure patterns: See code-generation.md Critical Rules
Language Configuration
- Documentation Language: English
- Conversation Language: User Language (Dutch)
Extension Configuration
| Extension | Enabled | Decided At |
|---|---|---|
| Security Baseline | Yes (blocking) | Requirements Analysis |
| Property-Based Testing | No | Requirements Analysis |
Operations Configuration
- Include Operations Phase: Yes
- Decided At: Requirements Analysis
Deployment Setup
- Included: Yes
- Method: CI/CD (Gitea Actions — confirms what U5/U6 already built)
- Completed: 2026-07-28. Real domains:
test.slpsoftware.nl/slpsoftware.nl. Ports 5100/5101 chosen for the Pi's local Kestrel bindings. Database backup script drafted. Full FTPS future-switch procedure drafted (Q5 = B), with an explicit caveat that shared hosting is very likely IIS-based, so systemd-restart and atomic-symlink-switch do not carry over unchanged — treated as a starting brief for a future Infrastructure Design pass, not a ready-to-execute procedure - Artifacts:
operations/deployment/deployment-plan.md,deployment-instructions.md,rollback-plan.md
Scope Decisions (from feature-selection.md)
- Public website: documentation/instructions only — where the website build lands in
wwwroot/, how it coexists withwwwroot/admin/, and what a per-website workspace must deliver. The website's own build/deploy workflow stays out of scope (Q4 = A). - Environments: local, test, production only.
- Observability stack: UptimeRobot (uptime), Umami (analytics), console logging + Sentry (logging/errors).
- Deployment constraint: upload as a published .NET application; no server configuration may be required.
- Reference: existing working Gitea Actions setup at
K:\Development\SlpSoftware\Projects\SlpSoftware(React/Vite) is the starting point. - Health check endpoint: IN SCOPE (decided 2026-07-27). Liveness only —
AddHealthChecks()+MapHealthChecks("/health"), no package needed and no database check (Q17 = A / D-21, superseding the earlier note thatAddDbContextCheckmight be included)./healthmust be added toAvailabilityMiddleware._bypassPrefixesso the availability gate cannot return 503 for it. Health = infrastructure liveness; Availability/capabilities = CMS domain state — these stay strictly separate.
Note
: this section records the earliest scope decisions. The authoritative and complete decision set is
inception/requirements/requirements.md§ 3 (D-01…D-32) — in particular, Q4 = C changed the public website from living directly inwwwroot/towwwroot/web/.
Stage Progress
INCEPTION
- Workspace Detection — Complete
- Reverse Engineering — Complete, approved 2026-07-27 (full rerun of all 8
_shared/artifacts) - Requirements Analysis — Complete, approved 2026-07-27. 24 FRs (FR-24 added at Application Design), 10 NFRs, 32 decisions, 7 assumptions, 4 open items, 4 documented security deviations. Two question rounds:
requirement-verification-questions.md(25 Q) andrequirement-clarification-questions.md(5 Q). - User Stories — SKIP (infrastructure/operations work; no new end-user functionality or persona. Offered at Requirements Analysis approval, not requested.)
- Workflow Planning — Complete, approved 2026-07-27. Artifact:
inception/plans/execution-plan.md - Application Design — Complete, approved 2026-07-27. 14 code components (9 new, 5 modified) + 2 workflow components. Artifacts in
inception/application-design/. Two composition conflicts found and carried to Unit 2. Added FR-24, closed OPEN-02. - Units Generation — Complete (awaiting approval). 7 units in 4 execution rounds. Artifacts:
unit-of-work.md,unit-of-work-dependency.md,unit-of-work-story-map.md
CONSTRUCTION
Units finalised at Units Generation (see inception/application-design/unit-of-work.md):
U1 Hosting & Serving · U2 Data Durability · U3 Security Headers & CSP · U4 Observability · U5 CI Workflow & Gates · U6 Deploy Workflow · U7 Documentation
Execution rounds (Q4 = B): R1 = U1 + U2 · R2 = U3 + U4 · R3 = U5 + U6 · R4 = U7. One commit per unit; single PR at the end (Q6 = A).
- Functional Design — EXECUTE for U1, U2, U3, U4; SKIP for U5, U6, U7. U1 ✅ U2 ✅ approved 2026-07-27 · U3 ✅ U4 ✅ 2026-07-28
- NFR Requirements — SKIP (all units) — already comprehensively captured in
requirements.md§ 5 and § 6 - NFR Design — EXECUTE for U3, U4; SKIP for the rest. Deliberate deviation from the default NFR-Requirements/NFR-Design coupling — rationale in the execution plan. U3 ✅ U4 ✅ 2026-07-28 — 11 patterns for U3, 10 for U4. Closed OPEN-01; raised REF-U3-01
- Infrastructure Design — EXECUTE for U6, U7 per the original plan; SKIP for the rest. U6 ✅ 2026-07-28 — single Pi, directory-only environment split,
systemd --user(no sudo), releases/current/shared layout, 2-release retention, health check via public URL. Raised INFRA-U6-01 (linger requirement, carried to Operations). U7 did not need it in practice: by Units Generation, U7's scope had narrowed to documentation-only (Q8 = A — operational/infrastructure decisions moved to Operations), so it went straight to Code Generation with no infrastructure to design - Code Generation — EXECUTE (all 7 units, each built and tested before its completion message). U1 ✅ U2 ✅ 2026-07-27 (253 backend tests). U3 ✅ 2026-07-28 (315). U4 ✅ 2026-07-28 (366 backend + 237 frontend). U6 ✅ U5 ✅ 2026-07-28 — Round 3:
deploy-scp.yaml+continuous_integration.yaml, 372 backend + 237 frontend tests (unchanged from Round 2, confirming no regressions from FR-21/FR-22 fixes), 0 vulnerable packages, lint clean. Raised REF-U5-01 (Umami-origin gate needs a parallel Gitea variable, since the backend side is a host env var per D-16). U7 ✅ 2026-07-28 — Round 4:WEBSITE_WORKSPACE.md, README.md and.env.exampleupdated, all claims re-verified against actual U1–U6 source. All 7 units complete. - Build and Test — EXECUTE — Complete 2026-07-28. 609 unit tests (372 backend + 237 frontend), 0 vulnerable packages, 0 build errors. Plus live-host integration verification: static content/SPA-fallback/header-scoping, real master↔slave communication proving the persistent key ring survives a process restart, and W3C trace-id correlation in real log output. Artifacts in
construction/build-and-test/
OPERATIONS
- Deployment Setup — EXECUTE
- Monitoring Setup — EXECUTE
- Production Readiness Validation — EXECUTE (includes the
dotnet-appsettingscompliance gate)
Execution Plan Summary
- Risk Level: High — three destructive-and-silent failure modes (customer website loss, Data Protection key-ring loss, automatic migration against production)
- Stages to Execute: Functional Design (×4: U1–U4), NFR Design (×2: U3, U4), Infrastructure Design (×2: U6, U7), Code Generation (×7), Build and Test, Deployment Setup, Monitoring Setup, Production Readiness Validation
- Stages to Skip: User Stories (no end-user functionality), NFR Requirements (already captured), plus per-unit skips as listed above
Current Status
- Lifecycle Phase: OPERATIONS
- Current Stage: Build and Test complete 2026-07-28 (all 7 units, full-suite + live-host integration verification). CONSTRUCTION phase is now closed
- Next Stage: Deployment Setup (Operations Configuration = Yes, decided at Requirements Analysis)
- Status: Rounds 1–4 all done, nothing pushed. Entering Operations phase
Round 2 Design Record (2026-07-28)
- Functional Design U3 + U4 complete and committed (
357d395) - NFR Design U3 —
construction/u3-security-headers/nfr-design/— 11 patterns. All new types inCore/Hosting/Security/, soCore.Testscan reach them (avoids repeating U1's Step 11 deviation) - NFR Design U4 —
construction/u4-observability/nfr-design/— 10 patterns.Sentry.AspNetCore6.8.0 intoCore;@sentry/react^10.68.0 intofrontend - OPEN-01 CLOSED: correlation ID = W3C trace ID from the ambient
Activity,TraceIdentifieras fallback. Rationale: propagates master→slave viatraceparent, and equals thetraceIdASP.NET Core'sProblemDetailsalready returns - REF-U3-01 raised: BR-U3-22's Umami-origin startup warning is not implementable — the backend cannot read
VITE_UMAMI_WEBSITE_ID. Withdrawn from U3 and replaced by a blocking U5 CI gate comparing the frontend build variable against that environment'sSecurityHeaders:AllowedScriptOrigins - Additions beyond the functional design, each with rationale in the pattern docs:
Set-Cookieadded to the scrub list; asentry-tunnelrate limiter;OnRejectedon the existing rate limiter (today a429leaves no trace anywhere);SentrySdk.FlushAsyncbefore the migration-failure rethrow (otherwise the oneCriticalevent dies with the process) - Three defaults chosen rather than escalated (each one line to change, listed at the end of U4's pattern doc): JSON console outside Development,
TracesSampleRate0.1, tunnel cap 200 KB - U4 modifies two files from already-committed units —
DatabaseMigrationExtensions(U2) andAdminTokenValidator(U1). Both additive; to be named in the U4 commit message
Round 1 Verification Record (2026-07-27)
dotnet build SlpModularCms.sln -c Release— 0 errors- Backend tests — 253 passed, 0 failed (Core 83, Availability 82, Identity 37, Master 51); baseline was 219
- New EF migration
20260727203036_AddDataProtectionKeys— verified purely additive - Embedded placeholder resource name verified against the compiled assembly manifest
- Carried to phase-level Build and Test: composed-startup behaviour that needs a running host and a real database —
/admintrailing-slash redirect, 404-vs-HTML for missing assets, SPA fallback and placeholder resolution,/healthwhile availability-disabled,MigrateCoreDatabaseagainst SQL Server, and both hosts starting - Deviation: U1 plan Step 11 (
StaticContentTests) not implemented — the code lives inSlpModularCms.Api, which has no test project by convention; behaviour carried to Build and Test instead. Recorded in the unit'sgeneration-summary.md
Round 2 Verification Record (2026-07-28)
dotnet build SlpModularCms.sln -c Release— 0 errors (70 warnings, all pre-existing package advisories)- Backend tests — 372 passed, 0 failed at stage close (Core 196, Availability 82, Master 57, Identity 37); was 253 after Round 1, 366 after U4, +6 from the integrity-check fix
- Frontend tests — 237 passed, 0 failed; baseline 213
npx tsc -bclean; eslint on every changed frontend file reports 0 problems; fullpnpm run lintunchanged at the pre-existing 5 errors / 1 warning (FR-21, U5)Sentry.AspNetCore6.8.0 ships a nativenet10.0asset — the carried-forward compatibility question is closed@sentry/react10.68.0;pnpm-lock.yamldiff is additions only- Two findings:
z.string().url()acceptshtp://in Zod 4 (URL constructor accepts any scheme), so the pre-existing frontend validation never caught the typo BR-U4-24 names — nowz.url({ protocol: /^https?$/ }). Andappsettings.jsoncomments are verified byDeployedConfigurationTestsagainst the real provider rather than assumed, because the failure mode is both hosts refusing to start - One deviation:
IAdminTokenValidatorcollapsed to a singleValidate→AdminTokenResultinstead of adding an overload. Two methods with an invisible difference at the call site let a substitute silently invert the access decision while both compiled — see U4'sgeneration-summary.md - Two defects found in local testing after Round 2, both fixed in this branch rather than filed, per the standing rule that tech debt is for large or high-impact changes only:
- Ciphertext written before U2 cannot be decrypted by the database key ring (
980dc80). Recorded as ASM-08; local rows cleared and re-registered VerifyIntegrityAsynccould not tell an unreachable slave from one that does not recognise the master, so the only recoverable state was never repaired (6957ec7). Now four distinct outcomes; automatic registration on a rejected key is safe because the slave refuses any key that does not match an existing registration
- Ciphertext written before U2 cannot be decrypted by the database key ring (
- Note: fix 2 is master/slave domain behaviour, not deployment work. It sits in this branch by explicit decision, not because it belongs to the feature
- Carried to phase-level Build and Test: trace-ID propagation master → slave,
TraceIdpresent in rendered console output, tunnel status codes, thesecurity_eventtag on a real Sentry event, threshold behaviour end to end, and CSP/HSTS header presence on real static assets and error responses