webadmin turned out to be FTP-only and belongs to whoever uploads customer websites, not to this pipeline. Switched to a separate deploy account with its own home-directory paths, well away from where the other sites live, and made the persistent website symlink cross into webadmin's territory instead of the other way around.
15 KiB
15 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 - Revised after user feedback (real host facts):
webadmin(the Pi's FileZilla/SFTP account, root/mnt/storage1/www/) cannot SSH in and is not the deploy account — it's theWEBSITE_WORKSPACE.mdwebsite-author role. A separate, dedicated SSH-capable account now runs the deploy pipeline (PI_MAIN_USERNAME), with deploy paths under that account's own home directory rather than under/mnt/storage1/www/html/, so the CMS's release structure never interferes with the other hosted websites.shared/wwwroot-webis now a cross-account symlink to whereverwebadminuploads this customer's site, requiring a one-time shared-group permission setup — documented, not automated - 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