Makes the application say what it is doing and when it fails
U4. Console logging plus Sentry, a same-origin tunnel so ad blockers cannot silence browser errors, Umami on the admin SPA, and six security events that alert rules can actually be built on. The correlation id is the W3C trace id from the ambient Activity, enabled by one line of ActivityTrackingOptions so every entry from every category carries it without touching a call site. It propagates across the master/slave boundary via traceparent, which TraceIdentifier cannot do at all, and it is the same value ProblemDetails already returns to the browser. The security events use source-generated LoggerMessage with constant templates. Sentry groups log events by message, so interpolating an email address would give every address its own issue and "more than 20 failed logins in five minutes" could never fire — the events would arrive, be visible, be tagged, and the alerting would silently be impossible. A test asserts the rendered message is identical across argument values. Scrubbing happens in-process, before transmission, and covers Set-Cookie as well as Cookie: the login response issues the refreshToken there, so scrubbing only the request side would protect nothing. Transactions are scrubbed too, because they carry request data and are the channel nobody thinks of. The tunnel derives its destination from the DSN once at startup and reads nothing from the request, which is what separates a tunnel from a server-side request forgery primitive. Size is capped by a bounded read rather than by trusting Content-Length, and the endpoint is rate limited. Two things found along the way. Zod 4's url() hands the value to the URL constructor, which accepts any scheme — so the existing frontend validation would have accepted the exact "htp://" typo BR-U4-24 names, and the SPA would have called a nonexistent origin. Now constrained to http(s). And the new appsettings comments are verified against the real configuration provider, because the failure mode if it rejected them is both hosts refusing to start after a release switch. One deviation. IAdminTokenValidator was meant to gain a reason-reporting overload; implemented that way, a substitute returning false by default silently inverted the access decision while both methods compiled. Two methods whose difference is invisible at the call site is the defect, so it is now a single Validate returning AdminTokenResult. Touches two files from already-committed units: DatabaseMigrationExtensions (U2) gains a flush before the rethrow, or the one Critical event in the system dies with the process; AdminTokenValidator (U1) classifies why a bypass was refused. Build 0 errors; 366 backend tests pass, up from 315, and 237 frontend tests, up from 213. tsc clean, eslint clean on every changed file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HHoJpxYXzHACSQguHrC5fw
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
- **Feature Slug**: gitea-deployment-workflow
|
||||
- **Project Type**: Brownfield
|
||||
- **Start Date**: 2026-07-27T00:00:00Z
|
||||
- **Current Stage**: CONSTRUCTION - Round 2, NFR Design complete for U3 + U4 (awaiting approval)
|
||||
- **Current Stage**: CONSTRUCTION - Code Generation complete for Round 2 (U3 + U4)
|
||||
- **Branch**: feature/gitea-deployment-workflow
|
||||
|
||||
## Workspace State
|
||||
@@ -69,7 +69,7 @@ Execution rounds (Q4 = B): **R1** = U1 + U2 · **R2** = U3 + U4 · **R3** = U5 +
|
||||
- [ ] NFR Requirements — **SKIP (all units)** — already comprehensively captured in `requirements.md` § 5 and § 6
|
||||
- [x] 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**; SKIP for the rest
|
||||
- [~] Code Generation — **EXECUTE** (all 7 units, each built and tested before its completion message). **U1 ✅ U2 ✅** generated and verified 2026-07-27 — build 0 errors, 253 backend tests pass (was 219)
|
||||
- [~] 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)
|
||||
- [ ] Build and Test — **EXECUTE**
|
||||
|
||||
### OPERATIONS
|
||||
@@ -84,9 +84,9 @@ Execution rounds (Q4 = B): **R1** = U1 + U2 · **R2** = U3 + U4 · **R3** = U5 +
|
||||
|
||||
## Current Status
|
||||
- **Lifecycle Phase**: CONSTRUCTION
|
||||
- **Current Stage**: Round 2 — NFR Design complete for U3 Security Headers & CSP and U4 Observability
|
||||
- **Next Stage**: Code Generation for U3 + U4
|
||||
- **Status**: Awaiting NFR Design approval. Round 1 (U1 + U2) code approved and committed 2026-07-28
|
||||
- **Current Stage**: Round 2 complete — U3 Security Headers & CSP and U4 Observability generated, built and tested
|
||||
- **Next Stage**: Round 3 — U5 CI Workflow & Gates + U6 Deploy Workflow (U6 needs Infrastructure Design first)
|
||||
- **Status**: Awaiting Round 2 code approval. Rounds 1 and 2 committed, nothing pushed
|
||||
|
||||
## Round 2 Design Record (2026-07-28)
|
||||
- Functional Design U3 + U4 complete and committed (`357d395`)
|
||||
@@ -105,3 +105,14 @@ Execution rounds (Q4 = B): **R1** = U1 + U2 · **R2** = U3 + U4 · **R3** = U5 +
|
||||
- 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 — `/admin` trailing-slash redirect, 404-vs-HTML for missing assets, SPA fallback and placeholder resolution, `/health` while availability-disabled, `MigrateCoreDatabase` against SQL Server, and both hosts starting
|
||||
- **Deviation**: U1 plan Step 11 (`StaticContentTests`) not implemented — the code lives in `SlpModularCms.Api`, which has no test project by convention; behaviour carried to Build and Test instead. Recorded in the unit's `generation-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 — **366 passed, 0 failed** (Core 196, Availability 82, Master 51, Identity 37); was 253 after Round 1
|
||||
- Frontend tests — **237 passed, 0 failed**; baseline 213
|
||||
- `npx tsc -b` clean; eslint on every changed frontend file reports 0 problems; full `pnpm run lint` unchanged at the pre-existing 5 errors / 1 warning (FR-21, U5)
|
||||
- `Sentry.AspNetCore` 6.8.0 ships a native **`net10.0`** asset — the carried-forward compatibility question is closed
|
||||
- `@sentry/react` 10.68.0; `pnpm-lock.yaml` diff is additions only
|
||||
- **Two findings**: `z.string().url()` accepts `htp://` in Zod 4 (URL constructor accepts any scheme), so the pre-existing frontend validation never caught the typo BR-U4-24 names — now `z.url({ protocol: /^https?$/ })`. And `appsettings.json` comments are verified by `DeployedConfigurationTests` against the real provider rather than assumed, because the failure mode is both hosts refusing to start
|
||||
- **One deviation**: `IAdminTokenValidator` collapsed to a single `Validate` → `AdminTokenResult` instead 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's `generation-summary.md`
|
||||
- **Carried to phase-level Build and Test**: trace-ID propagation master → slave, `TraceId` present in rendered console output, tunnel status codes, the `security_event` tag on a real Sentry event, threshold behaviour end to end, and CSP/HSTS header presence on real static assets and error responses
|
||||
|
||||
@@ -877,3 +877,73 @@ Each is one line; all three are listed at the end of U4's pattern document for r
|
||||
**No blocking security findings. No new deviation.**
|
||||
|
||||
---
|
||||
|
||||
## 2026-07-28 — CONSTRUCTION: Code Generation (Round 2, U3 + U4)
|
||||
|
||||
**Stage**: Code Generation for U3 HTTP Security Headers & CSP and U4 Observability Integration. Each unit was built and tested before moving on, per the standing instruction not to defer verification to the final stage.
|
||||
|
||||
### Verification
|
||||
|
||||
| | U3 | U4 |
|
||||
|---|---|---|
|
||||
| Release build | 0 errors | 0 errors |
|
||||
| Backend tests | **315** passed (from 253) | **366** passed |
|
||||
| Frontend tests | unchanged (213) | **237** passed |
|
||||
| `tsc -b` | n/a | clean |
|
||||
| eslint on changed files | n/a | 0 problems |
|
||||
|
||||
Full `pnpm run lint` remains at the pre-existing 5 errors / 1 warning — none in files this round touched. FR-21 fixes those in U5.
|
||||
|
||||
`Sentry.AspNetCore` 6.8.0 ships a native `net10.0` asset, closing the compatibility question NFR Design carried forward. `@sentry/react` 10.68.0; the lockfile diff is additions only.
|
||||
|
||||
### Two findings
|
||||
|
||||
**1. `z.string().url()` never caught the typo BR-U4-24 cites.** The rule says a malformed API base URL must not be silently accepted, and names `htp://localhost:7221`. Zod 4's `url()` validates by handing the value to the `URL` constructor, which accepts **any** scheme — verified directly: `htp://localhost:7221` and `ftp://x.nl` both pass a bare `.url()`. So the *pre-existing* frontend validation, before this unit touched it, would have accepted exactly the typo it was supposed to catch, and the SPA would have issued requests to a nonexistent origin — a failure that looks like the API being down. Now `z.url({ protocol: /^https?$/ })`. Not a regression introduced here; found because BR-U4-24 asked for a test the old schema could not have passed.
|
||||
|
||||
**2. Comments in `appsettings.json` are verified rather than assumed.** Several non-obvious values gained `//` comments. The JSON configuration provider does tolerate them, but the failure mode if it did not is *both hosts refusing to start after a release switch*, so `DeployedConfigurationTests` now loads both real, committed files through the real provider — and runs `ValidateOnStart` against the committed `SecurityHeaders` section, so a policy-name typo fails in CI rather than in a deployment.
|
||||
|
||||
### One deviation, and what it revealed
|
||||
|
||||
**`IAdminTokenValidator` collapsed to a single method.** NFR Design specified adding a reason-reporting overload alongside the existing `IsVerifiedAdmin(string?)`. Implemented that way, two existing tests failed in a revealing manner: the middleware called the new overload while the tests stubbed the old one, and an `NSubstitute` substitute returns `false` by default — so **the access decision silently inverted** while both methods existed and compiled.
|
||||
|
||||
That is the shape of the defect, not merely of the test failure. Two methods whose difference is invisible at a call site means a caller using the boolean form gets the correct access decision and silently emits no security event — precisely the class of bug this unit exists to make impossible. Replaced with one `Validate(string?) → AdminTokenResult`. Five call sites and two test files updated; behaviour otherwise identical.
|
||||
|
||||
Also noted: `MigrationFailure` uses synchronous `SentrySdk.Flush`, because `MigrateCoreDatabase` is synchronous and making it async would change a U2 signature and both hosts' startup for no benefit.
|
||||
|
||||
### Traps that were closed rather than encountered
|
||||
|
||||
- **`DefaultHttpContext.Response.OnStarting` is a no-op.** All of U3's decision logic went into a static `SecurityHeaderWriter` tested against a bare `HeaderDictionary`, so the middleware is glue whose only risk is registration order — which is carried to Build and Test rather than pretended to be unit-testable.
|
||||
- **Sentry groups log events by message template.** Six source-generated `LoggerMessage` methods with constant templates and `EventId` 5001–5006, plus a test asserting the rendered message is *identical* across different argument values. Without that, FR-19's rate-based alert rules could never fire while everything appeared to work.
|
||||
- **`StartsWithSegments`, not `string.StartsWith`.** `"/administrator".StartsWith("/admin")` is true; a public page would have inherited the strict policy and lost its inline scripts with no server-side trace at all. Asserted for `/administrator`, `/admin-tools`, `/administration/contact`, `/healthcheck` and `/api/v10/Users`.
|
||||
|
||||
### Additions beyond the functional design, each implemented
|
||||
|
||||
- `Set-Cookie` in the scrub list — the login response issues the `refreshToken` there, so scrubbing only the request cookie would protect nothing
|
||||
- `SetBeforeSendTransaction` alongside `SetBeforeSend`
|
||||
- `OnRejected` on the rate limiter — a `429` previously left no trace anywhere, making the only rate limiter in the application unobservable
|
||||
- A `sentry-tunnel` fixed-window limiter — the other controls bound what each call can do, not how many calls there can be
|
||||
- `SentrySdk.Flush` before the migration-failure rethrow
|
||||
- Origin-format validation on the CSP origin lists — a CSP source list silently ignores a malformed source, so a URL with a path would look configured and block the script anyway
|
||||
- `SecurityAuthorizationResultHandler` at `IAuthorizationMiddlewareResultHandler` rather than inside an `IAuthorizationHandler`, because a handler sees one requirement at a time and would report denials for requests that were ultimately allowed
|
||||
|
||||
### Divergences from the reference project, all deliberate
|
||||
|
||||
- The dev tunnel proxy targets the local API rather than Sentry's ingest host, so no project id is committed and nothing has to be kept in sync by hand
|
||||
- `UmamiAnalytics` has no script-removing cleanup: under StrictMode the double-invocation becomes inject → remove → inject, and removing the element does not unregister Umami's listeners, so the first page view can be counted twice. Guarded by a test asserting one injection across re-renders
|
||||
- `tanstackRouterBrowserTracingIntegration` not adopted — it needs the router instance, and importing it from `sentry.ts` inverts the startup order
|
||||
|
||||
### Artifacts generated
|
||||
- `construction/u3-security-headers/code/generation-summary.md`
|
||||
- `construction/u4-observability/code/generation-summary.md`
|
||||
- 7 production files + 4 test files for U3; 10 backend and 3 frontend production files + 5 test files for U4
|
||||
|
||||
### Security Compliance (Security Baseline extension — enabled, blocking)
|
||||
- **SECURITY-03 — compliant.** Correlation ID arrives by configuration rather than discipline; EF's `Database.Command` pinned at `Warning`, asserted against the committed files.
|
||||
- **SECURITY-04 — compliant.** All five headers, one-year HSTS with `includeSubDomains`, a CSP on every HTML-serving path. `script-src 'self'` under Strict is asserted by a test, so loosening it requires deleting a test that explains why.
|
||||
- **SECURITY-11 — compliant.** The tunnel destination is parsed once at startup and no part of it can come from a request.
|
||||
- **SECURITY-14 — addressed; DEV-01 unchanged.** Six tagged event types, all `Warning` or above by construction.
|
||||
- **SECURITY-15 — compliant.** Both units fail closed at startup and neither throws into the response path.
|
||||
|
||||
**No blocking security findings. No new deviation.**
|
||||
|
||||
---
|
||||
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
# Code Generation Summary — U4 Observability Integration
|
||||
|
||||
**Generated**: 2026-07-28
|
||||
**Verified**:
|
||||
- `dotnet build SlpModularCms.sln -c Release` → **0 errors**
|
||||
- `dotnet test` → **366 passed, 0 failed** (315 after U3, so **+51**)
|
||||
- `pnpm test` → **237 passed, 0 failed** (baseline 213, so **+24**)
|
||||
- `npx tsc -b` → clean
|
||||
- `npx eslint` on all changed frontend files → **0 problems**; full `pnpm run lint` unchanged at the pre-existing 5 errors / 1 warning (FR-21, U5)
|
||||
|
||||
---
|
||||
|
||||
## Backend Files Created
|
||||
|
||||
| File | Purpose |
|
||||
|---|---|
|
||||
| `Core/Observability/BypassRejectionReason.cs` | Why an admin bypass was refused — never the token |
|
||||
| `Core/Observability/SecurityEvents.cs` | `SecurityEventNames` + six source-generated `LoggerMessage` methods, `EventId` 5001–5006 |
|
||||
| `Core/Hosting/Observability/ObservabilityOptions.cs` | `Observability` configuration section |
|
||||
| `Core/Hosting/Observability/LoggingExtensions.cs` | `AddCmsLogging` — activity tracking, `IncludeScopes`, per-environment console |
|
||||
| `Core/Hosting/Observability/SentryEventScrubber.cs` | `ISentryEventScrubber` — removes four credential headers and the body |
|
||||
| `Core/Hosting/Observability/SecurityEventProcessor.cs` | Promotes the event name to a `security_event` Sentry tag |
|
||||
| `Core/Hosting/Observability/SentryTunnelTarget.cs` | Envelope endpoint parsed from the DSN, once, at startup |
|
||||
| `Core/Hosting/Observability/SentryExtensions.cs` | `AddCmsObservability` / `UseCmsSentry` |
|
||||
| `Core/Hosting/Observability/SentryTunnelExtensions.cs` | The `/sentry-tunnel` endpoint |
|
||||
| `Core/Hosting/Observability/SecurityAuthorizationResultHandler.cs` | Reports authorization denials, then defers to the framework |
|
||||
|
||||
## Frontend Files Created
|
||||
|
||||
| File | Purpose |
|
||||
|---|---|
|
||||
| `frontend/src/lib/sentry.ts` | `initSentry()` — skips without a DSN, tunnels same-origin |
|
||||
| `frontend/src/components/SentryErrorBoundary.tsx` | Recoverable fallback with no exception text |
|
||||
| `frontend/src/components/UmamiAnalytics.tsx` | Script injection, once, never in development |
|
||||
|
||||
## Files Modified
|
||||
|
||||
| File | Change |
|
||||
|---|---|
|
||||
| `Core/Hosting/ServiceCollectionExtensions.cs` | `OnRejected` on the rate limiter; `sentry-tunnel` limiter; registers the authorization result handler |
|
||||
| `Core/Hosting/DatabaseMigrationExtensions.cs` (**U2**) | Emits `MigrationFailure`; `SentrySdk.Flush` before the rethrow |
|
||||
| `Core/Hosting/Security/IAdminTokenValidator.cs` (**U1**) | Replaced by a single `Validate` returning `AdminTokenResult` — see deviations |
|
||||
| `Core/Hosting/Security/AdminTokenValidator.cs` (**U1**) | Classifies the rejection cause from the exception type |
|
||||
| `Core/Identity/Services/AuthService.cs` | `ILogger` dependency; emits `FailedLogin` |
|
||||
| `Modules.Availability/Middleware/AvailabilityMiddleware.cs` | Emits `AdminBypassRejected` |
|
||||
| `Modules.Availability/Controllers/MasterController.cs` | Emits `MasterApiKeyRejected` on all four rejection paths |
|
||||
| `Modules.Master/Controllers/SlaveStatusController.cs` | Same |
|
||||
| `Api/Program.cs`, `Api.Slave/Program.cs` | `AddCmsLogging` → `UseCmsSentry` → `AddCmsObservability` → `MapSentryTunnel` |
|
||||
| `Api/appsettings.json`, `Api.Slave/appsettings.json` | `Observability` section; `Logging` raised to `Information`; EF command logging pinned; `RateLimiting:SentryTunnel` |
|
||||
| `frontend/src/lib/config.ts` | Four new fields; `apiBaseUrl` accepts empty **or** an http(s) URL |
|
||||
| `frontend/src/main.tsx` | `initSentry()` first; boundary inside `AuthProvider`; `UmamiAnalytics` |
|
||||
| `frontend/vite.config.ts` | `__APP_VERSION__` define; dev proxy for `/sentry-tunnel` → local API |
|
||||
| `frontend/src/vite-env.d.ts` | Five `VITE_` variables and `__APP_VERSION__` |
|
||||
| `frontend/src/i18n/locales/{nl,en}/translation.json` | `error.unexpected.title` / `.message` |
|
||||
| `frontend/package.json` | `@sentry/react` `10.68.0` |
|
||||
| `Core.Tests/*.csproj` | Links both hosts' real `appsettings.json` into the test output |
|
||||
|
||||
## Test Files Created
|
||||
|
||||
| File | Tests | Covers |
|
||||
|---|---|---|
|
||||
| `Core.Tests/Hosting/Observability/SentryEventScrubberTests.cs` | 12 | Four headers removed, body nulled, diagnostic fields retained, transactions scrubbed |
|
||||
| `Core.Tests/Hosting/Observability/SentryTunnelTargetTests.cs` | 8 | Endpoint derivation, no-DSN state, unparseable DSN fails, path outside `/api` |
|
||||
| `Core.Tests/Hosting/Observability/SecurityEventsTests.cs` | 10 | **Identical rendered message across argument values**, levels, distinct IDs, tagging |
|
||||
| `Core.Tests/Hosting/Security/AdminTokenRejectionReasonTests.cs` | 11 | Each rejection reason from a real token |
|
||||
| `Core.Tests/Hosting/DeployedConfigurationTests.cs` | 13 | The committed `appsettings.json` of both hosts |
|
||||
| `frontend/src/lib/sentry.test.ts` | 5 | Skip without a DSN, tunnel not an ingest URL, tags, PII off |
|
||||
| `frontend/src/lib/config.test.ts` | 7 | Same-origin resolution, explicit URL preserved, malformed rejected |
|
||||
| `frontend/src/components/UmamiAnalytics.test.tsx` | 6 | Never in dev, nothing without an ID, injected once, nothing rendered |
|
||||
| `frontend/src/components/SentryErrorBoundary.test.tsx` | 5 | Fallback shown, **no exception text**, retry remounts, works without a DSN |
|
||||
| `frontend/src/lib/api-client.test.ts` (extended) | +1 | Relative URL construction with an empty base |
|
||||
|
||||
---
|
||||
|
||||
## Two Findings Worth Reading
|
||||
|
||||
### 1. `z.string().url()` never caught the typo its own rule cites
|
||||
|
||||
BR-U4-24 says a malformed value must not be silently accepted, and names `htp://localhost:7221` as the case. **Zod 4's `url()` validates by handing the value to the `URL` constructor, which accepts any scheme** — verified directly:
|
||||
|
||||
```
|
||||
z.string().url().safeParse('htp://localhost:7221') → success: true
|
||||
z.string().url().safeParse('ftp://x.nl') → success: true
|
||||
```
|
||||
|
||||
So the *pre-existing* validation, before this unit touched it, would have accepted the exact typo the rule exists to catch. The schema is now `z.url({ protocol: /^https?$/ })`, which rejects both. This was not a regression introduced here; it was found because BR-U4-24 asked for a test that the old schema could not have passed.
|
||||
|
||||
### 2. Comments in `appsettings.json` are now verified, not assumed
|
||||
|
||||
Several non-obvious values gained `//` comments. The JSON configuration provider tolerates them — but "tolerates" was worth verifying rather than assuming, because the failure mode is *both hosts refusing to start after a release switch*. `DeployedConfigurationTests` loads both real files through the real provider, and also runs `ValidateOnStart` against the committed `SecurityHeaders` section, so a policy-name typo fails here rather than in a deployment.
|
||||
|
||||
---
|
||||
|
||||
## Deviations from the NFR Design
|
||||
|
||||
**`IAdminTokenValidator` ended up with one method, not two.** The design specified adding an overload returning the reason alongside the existing boolean. Implementing it that way immediately broke two existing tests in a revealing way: the middleware called the new overload, the tests stubbed the old one, and an `NSubstitute` substitute returns `false` by default — so the *access decision silently inverted* while both methods still existed and compiled.
|
||||
|
||||
That is the shape of the defect, not just of the test failure: two methods where the difference is invisible at the call site, and a caller using the boolean form gets the right decision and silently emits no security event. Replaced with a single `Validate(string?) → AdminTokenResult` record. Five call sites and two test files updated; behaviour otherwise identical.
|
||||
|
||||
**`Set-Cookie` was already in the NFR design's scrub list** and is implemented; noted here because the functional design named only `Cookie`.
|
||||
|
||||
**`MigrationFailure` uses `SentrySdk.Flush`, not `FlushAsync`.** `MigrateCoreDatabase` is synchronous, and making it async would change a U2 signature and both hosts' startup for no benefit.
|
||||
|
||||
---
|
||||
|
||||
## Business Rule Coverage
|
||||
|
||||
| Rule | Where | Test |
|
||||
|---|---|---|
|
||||
| BR-U4-01, BR-U4-02 console always active at `Information` | `AddCmsLogging`, `appsettings.json` | `HostConfiguration_ShouldAllowInformationLevelLogging` |
|
||||
| BR-U4-03 correlation ID on every entry | `ActivityTrackingOptions` + `IncludeScopes` | Carried to Build and Test |
|
||||
| BR-U4-04 logging before Sentry | `Program.cs` order | Carried to Build and Test |
|
||||
| BR-U4-05 no secrets in logs | EF category pinned; event templates | `HostConfiguration_ShouldPinEfCommandLoggingBelowInformation` |
|
||||
| BR-U4-06, BR-U4-07 threshold split | `MinimumEventLevel` / `MinimumBreadcrumbLevel` | Carried to Build and Test |
|
||||
| BR-U4-08 absent DSN supported | `UseCmsSentry` early return | `HostConfiguration_ShouldShipWithoutASentryDsn` |
|
||||
| BR-U4-09 never blocks a request | SDK is fire-and-forget; tunnel returns `202` | Carried to Build and Test |
|
||||
| BR-U4-10, BR-U4-11 environment and release tags | `UseCmsSentry` | `sentry.test.ts` (frontend side) |
|
||||
| BR-U4-12…14 scrubbing in-process | `SentryEventScrubber` | 12 scrubber tests |
|
||||
| BR-U4-15…21 tunnel | `SentryTunnelExtensions`, `SentryTunnelTarget` | 8 target tests; endpoint carried to Build and Test |
|
||||
| BR-U4-22…25 frontend configuration | `config.ts` | 7 config tests + the api-client test |
|
||||
| BR-U4-26 frontend Sentry skipped | `initSentry` | `sentry.test.ts` |
|
||||
| BR-U4-27…29 Umami | `UmamiAnalytics` | 6 component tests |
|
||||
| Six security events | `SecurityEvents` + emission sites | 10 event tests |
|
||||
|
||||
---
|
||||
|
||||
## Carried to Phase-Level Build and Test
|
||||
|
||||
| Behaviour | Why it needs a running host |
|
||||
|---|---|
|
||||
| **Trace ID propagates master → slave** | The entire justification for resolving OPEN-01 as the W3C trace ID. Needs both hosts and a real master/slave call |
|
||||
| `TraceId` actually appears in rendered console output | `ActivityTrackingOptions` populates the scope; `IncludeScopes` renders it. Set one and forget the other and every line looks normal with no correlation ID and no error |
|
||||
| Tunnel: `404` without a DSN, `413` oversized, `202` on upstream failure, `503` when availability-disabled | Needs the endpoint in a real pipeline |
|
||||
| `security_event` tag present on a real Sentry event | The processor reads `Extra["SecurityEvent"]`; that the SDK populates it from the log state is verified against a live event rather than assumed |
|
||||
| Sentry event and breadcrumb thresholds observed end to end | Needs a DSN and a real send |
|
||||
| `Observability__SentryDsn` environment variable overrides the empty committed value | Confirms the D-16 secret path works |
|
||||
| Both hosts start with all new sections | Partly pre-empted by `DeployedConfigurationTests` |
|
||||
Reference in New Issue
Block a user