From 81af6cd151f34cd946b6ca452a0418a1f1223299 Mon Sep 17 00:00:00 2001 From: Sluijsens Date: Sun, 2 Aug 2026 16:35:00 +0200 Subject: [PATCH] Completes Operations phase: Monitoring Setup and Production Readiness Validation Confirms existing tag-based Sentry alerting and domain-based UptimeRobot/ Umami monitoring already cover the Offerings module and the Api.SlpSoftware cutover with no new configuration. Closes the SECURITY-13 audit-trail open item and records the final production-readiness traceability, completing the slpsoftware-api feature's AI-DLC lifecycle. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01FWyStNL2ZsjrS7FLd7xvvN --- aidlc-docs/active-features.md | 2 +- .../features/slpsoftware-api/aidlc-state.md | 14 +-- aidlc-docs/features/slpsoftware-api/audit.md | 36 ++++++ .../inception/requirements/requirements.md | 2 +- .../monitoring/monitoring-instructions.md | 26 +++++ .../operations/plans/monitoring-setup-plan.md | 46 ++++++++ .../production-readiness-validation.md | 106 ++++++++++++++++++ 7 files changed, 223 insertions(+), 9 deletions(-) create mode 100644 aidlc-docs/features/slpsoftware-api/operations/monitoring/monitoring-instructions.md create mode 100644 aidlc-docs/features/slpsoftware-api/operations/plans/monitoring-setup-plan.md create mode 100644 aidlc-docs/features/slpsoftware-api/operations/production-readiness/production-readiness-validation.md diff --git a/aidlc-docs/active-features.md b/aidlc-docs/active-features.md index 41a357a..0dcfd6a 100644 --- a/aidlc-docs/active-features.md +++ b/aidlc-docs/active-features.md @@ -8,4 +8,4 @@ | Tech Debt Backlog (tech-debt-backlog) | ๐Ÿ”ต Inception | unknown | Modules.Master, Frontend | 2026-07-01 | | Local Dev Master/Slave Setup (local-dev-master-slave-setup) | โœ… Complete | unknown | Modules.Master, Api, Frontend | 2026-07-02 | | Gitea Deployment Workflow (gitea-deployment-workflow) | ๐ŸŸก Operations | feature/gitea-deployment-workflow | CI/CD, Api (hosting/config), Core, Modules, Frontend (build), Docs | 2026-07-27 | -| SlpSoftware Production API (slpsoftware-api) | ๐ŸŸข Construction | unknown | Clients (new Api.SlpSoftware), Modules (new Offerings module + Tests), Core (CmsHost), CI/CD (future retarget) | 2026-08-01 | +| SlpSoftware Production API (slpsoftware-api) | โœ… Complete | feature/slpsoftware-api | Clients (new Api.SlpSoftware), Modules (new Offerings module + Tests), Core (CmsHost), CI/CD (retargeted to Api.SlpSoftware) | 2026-08-02 | diff --git a/aidlc-docs/features/slpsoftware-api/aidlc-state.md b/aidlc-docs/features/slpsoftware-api/aidlc-state.md index d5c6882..4062140 100644 --- a/aidlc-docs/features/slpsoftware-api/aidlc-state.md +++ b/aidlc-docs/features/slpsoftware-api/aidlc-state.md @@ -5,7 +5,7 @@ - **Feature Slug**: slpsoftware-api - **Project Type**: Brownfield - **Start Date**: 2026-08-01T00:00:00Z -- **Current Stage**: CONSTRUCTION complete โ€” both units done, feature-wide Build and Test green; ready for OPERATIONS phase +- **Current Stage**: OPERATIONS phase complete โ€” Production Readiness Validation passed; feature complete pending the user's host-side systemd action - **Branch**: feature/slpsoftware-api (PR #9 open against master) ## Workspace State @@ -85,11 +85,11 @@ ### ๐ŸŸก OPERATIONS PHASE - [x] Deployment Setup โ€” Complete. `continuous_integration.yaml` retargeted to `Api.SlpSoftware` (D-15 cutover); `deploy-scp.yaml`/`rollback-plan.md` needed no changes; `deployment-instructions.md` ยง 1.6 updated with an explicit manual host-side action item for the user (edit the Pi's existing systemd units). No nginx/database/Gitea-variable changes (D-6). -- [ ] Monitoring Setup โ€” EXECUTE -- [ ] Production Readiness Validation โ€” EXECUTE (ALWAYS, once phase reached) +- [x] Monitoring Setup โ€” Complete. Investigated: Sentry alert rules (tag-based) and the global authorization-denial handler already cover the new `offerings-public` rate-limit policy and `OfferingsController`'s `AdminOnly` actions automatically; UptimeRobot/Umami/Gitea variables are domain-based, unaffected by the D-15 cutover. No new configuration needed โ€” documented as a confirmation, not new setup. +- [x] Production Readiness Validation โ€” Complete. All 9 FRs, 4 NFRs, 12 user stories, and 15 Security Compliance rules traced and confirmed; SECURITY-13's open item formally closed in `requirements.md`; verdict: ready for production contingent on the one pending host-side systemd action. ## Current Status -- **Lifecycle Phase**: OPERATIONS -- **Current Stage**: Deployment Setup โ€” Complete (repo-side changes done; user still needs to manually update the Pi's two systemd unit files) -- **Next Stage**: Monitoring Setup, then Production Readiness Validation -- **Status**: Awaiting user approval to proceed +- **Lifecycle Phase**: OPERATIONS โ€” COMPLETE +- **Current Stage**: Production Readiness Validation โ€” Complete (`operations/production-readiness/production-readiness-validation.md`) +- **Next Stage**: None โ€” feature complete, pending user's host-side systemd action and manual reference-content entry (ยง 6 of the validation doc) +- **Status**: Awaiting user's final approval to close out the feature diff --git a/aidlc-docs/features/slpsoftware-api/audit.md b/aidlc-docs/features/slpsoftware-api/audit.md index b4a07af..10b0294 100644 --- a/aidlc-docs/features/slpsoftware-api/audit.md +++ b/aidlc-docs/features/slpsoftware-api/audit.md @@ -899,3 +899,39 @@ **Context**: Operations, Deployment Setup stage complete โ€” all repo-side changes done and verified via diff/grep. The one remaining action (editing the Pi's two systemd unit files) is inherently the user's own manual step, documented clearly in both `deployment-instructions.md` locations. Presenting completion gate; awaiting user approval to proceed to Monitoring Setup. --- + +## Interruption โ€” Gitea Actions "1 running" Badge Investigated, No Action Taken + +**Timestamp**: 2026-08-02T17:30:00Z + +**User Input**: "Even tussendoor, ik zie in de UI van Gitea dat er nog 1 action bezig is... zou je kunnen kijken en dan die run annuleren?" โ€” followed by "gek. er staat wel een '1' achter Actions in het tabje. Maargoed. ga verder met de volgende stage" after being told nothing was found. + +**AI Response**: "Retrieved the Gitea API token via the user's `Get-StoredCredentialPassword.ps1` script (target `SlpSoftware-Gitea-API`). Checked all 433 Actions tasks across the repo (paginated, 9 pages) โ€” zero in a non-terminal (`running`/`queued`/`waiting`) state. Cross-checked via the more reliable `/commits/{sha}/status` endpoint for PR #9's head commit (`b6e9c07`) โ€” all 13 jobs `success` or `skipped`. Found nothing to cancel; reported this to the user rather than guessing or fabricating a cancellation. User accepted this and moved on, noting the Actions tab badge still showed '1' โ€” likely a stale UI count, consistent with an earlier session where the same user independently found a similarly stale 'lopende pipeline' display resolved itself. Not investigated further per the user's own 'maargoed, ga verder' instruction." + +**Context**: Ad-hoc mid-flow investigation, no repo changes. Did not block or delay the Operations phase. Returning to the Deployment Setup completion gate / Monitoring Setup transition. + +--- + +## Operations โ€” Monitoring Setup: Complete (No New Configuration) + +**Timestamp**: 2026-08-02T17:35:00Z + +**User Input**: "ga verder met de volgende stage" + +**AI Response**: "Read `gitea-deployment-workflow`'s `monitoring-instructions.md` in full, then checked whether the Offerings module or the D-15 cutover need any monitoring change. Investigated directly rather than assuming: read `AddCmsRateLimiting`'s shared `OnRejected` callback โ€” it fires `SecurityEvents.RateLimitTriggered` (the `rate_limit_triggered` Sentry tag the existing alert rule already filters on) for any rejected policy by name, so the new `offerings-public` policy is covered with zero new alert-rule configuration. Read `SecurityAuthorizationResultHandler.cs` โ€” a single global authorization-result handler fires `authorization_denied` for any policy failure on any endpoint, so `OfferingsController`'s `AdminOnly` actions are covered the same way. UptimeRobot's six monitors and the two Umami website entries check domains/paths, not a specific `.dll`, so the cutover is invisible to them. Confirmed `OfferingsService`'s own audit logging was deliberately kept out of `SecurityEvents`/Sentry at NFR Requirements time (routine content changes, not alertable anomalies) โ€” not a gap, a decision. Created `operations/plans/monitoring-setup-plan.md` (investigation + 'no questions, no new configuration' rationale) and `operations/monitoring/monitoring-instructions.md` (feature-local pointer + the conclusion table), so a future reader isn't left wondering whether this was simply forgotten. Updated `aidlc-state.md`: Monitoring Setup โ†’ Complete, Current Status โ†’ Production Readiness Validation next." + +**Context**: Operations, Monitoring Setup stage complete โ€” a documented confirmation rather than new setup, since nothing this feature built falls outside the existing tag-based/domain-based monitoring design. Presenting completion gate; awaiting user approval to proceed to Production Readiness Validation. + +--- + +## Operations โ€” Production Readiness Validation: Complete + +**Timestamp**: 2026-08-02T17:45:00Z + +**User Input**: "Continue to Next Stage" + +**AI Response**: "Traced the feature against its own Requirements Analysis artifacts rather than re-deriving new criteria: all 9 FRs (FR-1โ€“FR-9), all 4 NFRs, all 12 user stories (US-01โ€“US-12), and all 15 Security Compliance rules confirmed. Formally closed the one open item from Requirements Analysis โ€” SECURITY-13's audit-trail coverage โ€” in `requirements.md` itself (struck through, with a 'Resolved 2026-08-02' note pointing at NFR-OFF-03's `LastModifiedByUserId`/`CreatedAt`/`UpdatedAt` resolution), rather than leaving it dangling as still-open now that the feature is otherwise done. Verified migration safety against the rollback plan's D-26 requirement (forward-compatible, non-destructive): confirmed the `InitialCreate` migration is a single `CREATE TABLE` touching no existing table, trivially satisfying it. Documented two required actions that fall outside this repository: the pending host-side systemd `ExecStart` edit (blocking for production traffic, already flagged at Deployment Setup) and manually entering the three FR-8 reference offerings through the new admin UI once live (not blocking, user's own task per D-5). Created `operations/production-readiness/production-readiness-validation.md` with the full traceability tables and a final verdict: ready for production, contingent on the one pending host action. Updated `aidlc-state.md`: OPERATIONS phase marked complete." + +**Context**: Operations, Production Readiness Validation stage complete โ€” the final stage of this feature's AI-DLC lifecycle. Presenting the feature-completion gate; awaiting the user's final approval to close out `slpsoftware-api`. + +--- diff --git a/aidlc-docs/features/slpsoftware-api/inception/requirements/requirements.md b/aidlc-docs/features/slpsoftware-api/inception/requirements/requirements.md index 8a2ee25..29bcf96 100644 --- a/aidlc-docs/features/slpsoftware-api/inception/requirements/requirements.md +++ b/aidlc-docs/features/slpsoftware-api/inception/requirements/requirements.md @@ -136,7 +136,7 @@ No PBT tooling or rules are introduced for this feature's tests (D-12). | SECURITY-14 Alerting and monitoring | **Pre-existing, unchanged** | Reuses the shared Sentry-based alerting from `gitea-deployment-workflow`; no new alert categories introduced by this feature. | | SECURITY-15 Exception handling and fail-safe defaults | **Pre-existing, unchanged** | Reuses the shared `GlobalExceptionHandler`; new endpoints follow the same fail-closed pattern (auth failure โ†’ 401/403, not silent fallback). | -**Open item**: SECURITY-13 audit-trail coverage for `Offering` create/edit/delete/reorder is not yet confirmed against an existing system-wide audit mechanism (or its absence) โ€” to be resolved at Functional Design / NFR Design for the Offerings unit, not blocking Requirements sign-off. +~~**Open item**: SECURITY-13 audit-trail coverage for `Offering` create/edit/delete/reorder is not yet confirmed against an existing system-wide audit mechanism (or its absence) โ€” to be resolved at Functional Design / NFR Design for the Offerings unit, not blocking Requirements sign-off.~~ **Resolved 2026-08-02** (NFR Requirements, Offerings unit): `Offering.LastModifiedByUserId` + `CreatedAt`/`UpdatedAt` provide a minimal "who + when" audit trail on every create/update/delete โ€” see `nfr-requirements.md`'s NFR-OFF-03. Deliberately not a full before/after audit-log table; formally closed at Production Readiness Validation (`operations/production-readiness/production-readiness-validation.md` ยง 4). --- diff --git a/aidlc-docs/features/slpsoftware-api/operations/monitoring/monitoring-instructions.md b/aidlc-docs/features/slpsoftware-api/operations/monitoring/monitoring-instructions.md new file mode 100644 index 0000000..2a2fd23 --- /dev/null +++ b/aidlc-docs/features/slpsoftware-api/operations/monitoring/monitoring-instructions.md @@ -0,0 +1,26 @@ +# Monitoring Setup โ€” slpsoftware-api + +This feature does not own separate monitoring infrastructure. Per the same "extend, don't duplicate" +relationship as Deployment Setup, all monitoring for `slpsoftware.nl`/`test.slpsoftware.nl` is +already built and configured by `gitea-deployment-workflow`: + +- `aidlc-docs/features/gitea-deployment-workflow/operations/monitoring/monitoring-instructions.md` + +## Conclusion: No New Configuration Needed + +Investigated (see `operations/plans/monitoring-setup-plan.md` for the full trace) whether the +Offerings module or the D-15 CI/CD cutover require any change to Sentry alert rules, UptimeRobot +monitors, or Umami โ€” they do not: + +| Area | Why it's already covered | +|---|---| +| Sentry alert rules (`rate_limit_triggered` tag) | `AddCmsRateLimiting`'s shared rejection handler fires this tag for **any** policy by name, including the new `offerings-public` policy โ€” no per-policy wiring exists to update | +| Sentry alert rules (`authorization_denied` tag) | A single global `IAuthorizationMiddlewareResultHandler` in Core fires this tag for **any** policy failure on **any** endpoint, including `OfferingsController`'s `AdminOnly` actions | +| UptimeRobot (`/health`, `/`, `/admin` ร— test/production) | Checks domains/paths, not a specific `.dll` โ€” the cutover changes which process answers, invisible to an HTTP-status monitor | +| Umami (admin SPA website entries) | Same URLs before and after the cutover | +| Gitea variables (`VITE_SENTRY_DSN`, `VITE_UMAMI_*`, `SECURITY_ALLOWED_SCRIPT_ORIGINS_*`) | Already set, tied to the Sentry project/Umami instance/CSP config, not to which project publishes the artifact | + +`OfferingsService`'s own audit logging (`LastModifiedByUserId`, structured `LogInformation` on +create/update/delete โ€” NFR-OFF-03) was deliberately **not** routed through `SecurityEvents`/Sentry: +it logs routine content changes, not alertable anomalies. This was a considered decision at NFR +Requirements, not a monitoring gap. diff --git a/aidlc-docs/features/slpsoftware-api/operations/plans/monitoring-setup-plan.md b/aidlc-docs/features/slpsoftware-api/operations/plans/monitoring-setup-plan.md new file mode 100644 index 0000000..80273ee --- /dev/null +++ b/aidlc-docs/features/slpsoftware-api/operations/plans/monitoring-setup-plan.md @@ -0,0 +1,46 @@ +# Monitoring Setup Plan โ€” slpsoftware-api + +## Investigation (Before Drafting Anything) + +Read `gitea-deployment-workflow/operations/monitoring/monitoring-instructions.md` in full, then +checked whether anything this feature added (the Offerings module, the D-15 cutover) needs new or +changed monitoring configuration. + +- **Sentry alert rules (FR-19, tag-based)**: the six existing rules filter on the `security_event` + Sentry tag, not on message text or endpoint. Read `SlpModularCms.Core.Hosting.ServiceCollectionExtensions.AddCmsRateLimiting`'s + shared `OnRejected` callback โ€” it calls `SecurityEvents.RateLimitTriggered` (tag + `rate_limit_triggered`) for **any** rejected policy, by name, not per-policy-hardcoded. The new + `offerings-public` policy added in Code Generation is therefore already covered by the existing + `rate_limit_triggered` alert rule with zero new configuration. +- **Authorization denials**: read `SecurityAuthorizationResultHandler.cs` โ€” it's a global + `IAuthorizationMiddlewareResultHandler`, wired once in `Core`, firing `authorization_denied` + (tag) for **any** policy failure on **any** endpoint. `OfferingsController`'s `AdminOnly`-protected + actions are covered automatically; nothing Offerings-specific needed wiring. +- **No new alertable event type**: NFR-OFF-03's audit logging (`OfferingsService`'s + `LogInformation` calls) was deliberately kept **out of** `SecurityEvents`/Sentry (see NFR + Requirements' investigation) โ€” routine content mutations are not alertable anomalies. Nothing to + add here by design. +- **UptimeRobot (D-23)**: the six monitors (`/health`, `/`, `/admin` ร— test/production) check + domains and paths, not a specific `.dll`. They already point at `test.slpsoftware.nl`/ + `slpsoftware.nl` โ€” the D-15 cutover changes which process answers behind those URLs, invisible to + a monitor that only checks the HTTP response. No new monitor needed; the public + `GET /api/v1/offerings` endpoint was never in scope for dedicated uptime monitoring (D-23 covers + liveness/static-hosting/admin-SPA checks only, not every individual API route). +- **Umami**: two website entries for the admin SPA, same URLs before and after the cutover. No + change. +- **Gitea variables**: `VITE_SENTRY_DSN`, `VITE_UMAMI_*`, `SECURITY_ALLOWED_SCRIPT_ORIGINS_*` all + already exist and are unaffected โ€” same Sentry project (D-19), same Umami instance, same CSP + origins, none of which are tied to which project publishes the artifact. + +## Why No Questions, No New Configuration + +Every piece of monitoring infrastructure this feature could plausibly need is already covered by +`gitea-deployment-workflow`'s existing, tag-based/domain-based design โ€” none of it hardcodes a +project name or is scoped to a specific endpoint in a way the cutover or the new module would break. +This stage's output is a documented confirmation, not new setup. + +## Checklist + +- [x] Create `operations/monitoring/monitoring-instructions.md` as a feature-local pointer document + recording this investigation and its "no changes needed" conclusion, so a future reader isn't + left wondering whether Offerings' monitoring was simply forgotten diff --git a/aidlc-docs/features/slpsoftware-api/operations/production-readiness/production-readiness-validation.md b/aidlc-docs/features/slpsoftware-api/operations/production-readiness/production-readiness-validation.md new file mode 100644 index 0000000..da8c553 --- /dev/null +++ b/aidlc-docs/features/slpsoftware-api/operations/production-readiness/production-readiness-validation.md @@ -0,0 +1,106 @@ +# Production Readiness Validation โ€” slpsoftware-api + +Final Operations-phase gate. Validates the feature against its own Requirements Analysis artifacts +(traceability, not re-litigation) plus what actually running in production requires beyond passing +tests. + +## 1. Functional Requirements Traceability + +| FR | Status | Evidence | +|---|---|---| +| FR-1 New Client Project `Api.SlpSoftware` | โœ… Done | Unit 1, merged into `feature/slpsoftware-api` | +| FR-2 Module Composition (Identity/Availability/Master/Offerings) | โœ… Done | `Api.SlpSoftware.csproj` project references | +| FR-3 Shared Hosting Pipeline Extraction (`CmsHost`) | โœ… Done | `SlpModularCms.Core.Hosting.CmsHost`, used by both `Api` and `Api.SlpSoftware` | +| FR-4/FR-5 `Offerings` module + admin CRUD | โœ… Done | Unit 2, `SlpModularCms.Modules.Offerings` | +| FR-6 Public `GET /api/v1/offerings` | โœ… Done | `OfferingsController.GetOfferings`, `[AllowAnonymous]` | +| FR-7 Admin CRUD + reorder, `AdminOnly` | โœ… Done | `OfferingsController`'s remaining actions | +| FR-8 Reference content documented | โœ… Done | `requirements.md`'s reference-content table (3 packages); entering it into the running system is an explicit user-owned manual step (Scope Boundaries) | +| FR-9 CI/CD retarget (cutover) | โœ… Done (repo side) | `continuous_integration.yaml` retargeted; **host-side systemd edit still pending โ€” see ยง 4** | + +## 2. Non-Functional Requirements + +| NFR | Status | Evidence | +|---|---|---| +| NFR-1 No nginx changes | โœ… Confirmed | D-6; verified at Deployment Setup โ€” proxy forwards to a port, not an executable | +| NFR-2 Module test coverage | โœ… Met | 38 tests in `Modules.Offerings.Tests` covering entity/context, repository, service (all 4 business rules), controller (routes/auth) | +| NFR-3 Long-term reusability (no SlpSoftware-specific baking-in) | โœ… Confirmed | Module named `Offerings` (D-9), no hardcoded tenant/site assumptions in entity/service/controller | +| NFR-4 Property-based testing not required | โœ… N/A by design | D-12, example-based tests only throughout | + +## 3. User Story Coverage + +All 12 user stories (US-01โ€“US-12) implemented and covered by tests: US-01/02/03 (public list, +featured highlight, empty-state resilience), US-04โ€“US-07 (create/edit/delete, including +last-remaining-offering deletion), US-08/US-09 (drag-and-drop and button-based reorder), US-10 +(featured exclusivity), US-11 (validation feedback), US-12 (`AdminOnly` role gate). + +## 4. Security Compliance โ€” Final Status + +All 15 rules from `requirements.md`'s Security Compliance table are **Addressed**, **Pre-existing/ +unchanged**, or **N/A**, with one item to formally close: + +**SECURITY-13 (Software and data integrity) โ€” RESOLVED.** Requirements Analysis logged this as an +open item pending Functional/NFR Design. Resolution: `Offering.LastModifiedByUserId` + +`CreatedAt`/`UpdatedAt` (NFR-OFF-03) provide a minimal "who + when" audit trail on every +create/update/delete. Explicitly **not** a full before/after audit-log table โ€” that remains a known, +accepted scope boundary (documented in `nfr-requirements.md`), not a gap discovered late. + +No other Security Compliance item changed status during Construction or Operations. + +## 5. Build, Test, and Code Quality + +- Backend: full solution build succeeded; **414/414 tests passing** across 6 test projects, no + regressions (verified at the feature-wide Build and Test stage, re-confirmed after two live-testing + bug fixes during Unit 2's post-completion review โ€” record-validation targeting and the drag overlay + fix โ€” neither of which touched test-covered logic paths differently than already tested). +- Frontend: `pnpm build` succeeded, `pnpm lint` clean, **254/254 tests passing** across 41 files. +- Migration safety (rollback plan's D-26 requirement โ€” forward-compatible, non-destructive): the + `InitialCreate` migration for `OfferingsDbContext` is a single `CREATE TABLE` with no `ALTER` on + any existing table โ€” trivially satisfies this by construction. A rollback to a pre-Offerings + release simply leaves the `OfferingsOfferings` table unused, never touched destructively. + +## 6. Deployment Readiness + +Repo-side: complete (Deployment Setup stage). **Two items require action outside this repository +before the cutover is live**: + +1. **Host action (blocking for production traffic, not for CI)**: edit + `~/.config/systemd/user/slpsoftware-test.service` and `slpsoftware-production.service` on + pi-main, changing `ExecStart`'s target from `SlpModularCms.Api.dll` to + `SlpModularCms.Api.SlpSoftware.dll` (exact steps: `deployment-instructions.md` ยง 1.6, restated in + `operations/deployment/deployment-instructions.md`). Until this is done, a successful CI deploy + uploads the new build correctly, but the running service keeps executing the old `Api.dll` from + that release directory. +2. **Content entry (not blocking, user's own task)**: the three reference offerings (FR-8's table) + need to be entered through the new admin UI once live โ€” no auto-seed exists by design (D-5). + +## 7. Monitoring Readiness + +Confirmed at the Monitoring Setup stage: no new Sentry alert rules, UptimeRobot monitors, or Umami +entries needed. Existing tag-based/domain-based monitoring already covers the new rate-limit policy, +the new `AdminOnly` endpoints, and the cutover itself (see `operations/monitoring/monitoring-instructions.md`). + +## 8. Rollback Readiness + +`gitea-deployment-workflow`'s existing `rollback-plan.md` applies unchanged: fast rollback via the +`current` symlink (no rebuild), rebuild-and-redeploy for anything older, database restore from the +pre-deploy backup. None of its mechanisms are `.dll`-name-specific, so the D-15 cutover doesn't +require a rollback-plan update, and a rollback of this feature's first production deploy would land +back on the old `Api.dll` release exactly as any other rollback would. + +## 9. Known, Accepted Limitations (Not Defects) + +- No HTTP caching on the public endpoint (NFR-OFF-02, Q2=B) โ€” deliberate, revisit if traffic ever + makes it worth it. +- No full audit-log table for Offerings mutations (SECURITY-13's resolution scope, ยง 4 above). +- Sequential (non-transactional) single-row saves remain outside the three multi-row operations' + transaction boundary โ€” intentional per NFR Design Pattern 1's scope. +- `SlpModularCms.Api`'s own pipeline regression tests (`Api.Tests`, NFR-CS-01) don't separately cover + `Api.SlpSoftware` โ€” accepted trade-off from Unit 1, unchanged by this feature. + +## 10. Verdict + +**Ready for production, contingent on completing the one host-side action in ยง 6.1.** All +functional and non-functional requirements are implemented and traced; all tests pass; security +compliance is fully addressed with its one open item formally resolved; monitoring and rollback +require no changes. This is a documentation/traceability gate โ€” it does not re-run any build or test +already verified at the Build and Test stage.