Files
Fuchs_Intranet/CODEX.md
T
StefanandClaude Sonnet 5 bcf1f2dbb6
Playwright Tests / test (push) Canceled after 0s
feat: migrate DB error logging onto the generic OCORE MultiLogger SQL sink
Retires Fuchs_intranet.debug_log() and every direct EXECUTE [dbo].[fds__admin_logdebug]
call in favor of a single path: ILogger. A new SQL log sink (OCORE.MultiLogger.SqlLoggerProvider,
gated by Fuchs:Logging:SqlSink:Enabled, default on) mirrors every _logger.LogWarning/-Error/
-Critical call into [dbo].[fds__debuglog] via a new fds__admin_logdebug_ext proc, at Warning+.

- Program.cs registers the sink and RequestLogScopeMiddleware (request id/path/authenticated
  user, pushed after UseAuthentication) so every log line in a request — including ones written
  deep inside OCORE's SQL helpers — is attributed automatically.
- FIS_SQLOptions, IntranetController.Do's catch-all, PdfPreviewAsync, and the banking-upload
  handlers now log once, with an explicit EventId preserving the exact CodeReference text the
  retired debug_log() calls used to write, so existing dashboards/queries over fds__debuglog
  keep working unchanged.
- Fuchs_DataService/FdsMfr.cs's FdsSqlOptions had the identical double-write hazard (it's hosted
  in the same process per ADR 0010) — the original 2026-09-16 log review's mfr__getSchema/
  fds__getUpdateableRequests deadlock rows came from exactly this class. Fixed the same way.
- fds__debuglog gains LogLevel/Machine/Category/Account/AuthUser/IsDebug columns; the legacy
  fds__admin_logdebug proc is left in place unused, not dropped.
- FdsDebug.DebugLog (Fuchs_DataService's other ~9 call sites) deliberately left on its direct
  DB-write path — no ILogger call sits alongside it there, so no duplication risk today.

See ADR 0024 (supersedes 0023) and Docs/Concepts/error-logging-pipeline.md.

New tests: ErrorLoggingTests (FIS_SQLOptions), FdsSqlOptionsTests, RequestLogScopeMiddlewareTests.
694/694 pass (the 4 PdfPipelineTests failures seen under full-suite parallel runs are a
pre-existing, unrelated PdfSharp global-font-resolver test-isolation flake — they pass both
individually and in most full-suite runs).

Bumps the OCORE submodule to 3137b56 (SQL sink hardening: redaction matches short fragments
as whole identifier segments, per-entry-tolerant serialization, lazy dataSerialized, opt-in
SqlLoggerOptions.IncludeCategory, LogEntry.Category/AuthUser capture).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-16 17:20:06 +02:00

10 KiB

CODEX.md — Project instructions for Codex

Instruction Sync

This file, CLAUDE.md, and .github/copilot-instructions.md are three views of the same project rules and must stay in sync. When you change a shared rule (architecture, coding standards, configuration, libraries, secrets, observability, testing), make the equivalent change in all three files in the same commit. This file may add Codex / workflow specifics; the shared project facts must match CLAUDE.md and copilot-instructions.md.

Project Overview

  • Fuchs Intranet — ASP.NET Core (.NET 10) web app; the intranet IS the whole website, served from /.
  • Routes: /{fn?}/{id?}/{code?} -> IntranetController.Index; /do/{fn?}/{id?}/{code?} -> IntranetController.Do (dispatches by fn to Do_Process_*).
  • Solution Fuchs_Intranet.slnx. Key projects: Fuchs (web), Fuchs_DataService (MFR sync worker), MFR_RESTClient, CAMTParser, Fuchs.Tests, and the OCORE submodules (OCORE, OCORE_web, OCORE_web_pdf, OCORE_Charting). eRechnungLib is a submodule (ZUGFeRD/Factur-X + XRechnung generation) — invoices are moving to eRechnung output. MT940Parser is an external referenced project.

Build & Test (workflow)

  • Build app: dotnet build Fuchs/Fuchs.csproj -c Debug. Build all: dotnet build Fuchs_Intranet.slnx -c Debug.
  • Frontend assets are source-built: run the gulp tasks in Fuchs/ (npx gulp min, or npx gulp all when copied/static assets also need refreshing) whenever JS or SCSS/CSS sources change. The generated files under Fuchs/wwwroot/web/ are what the app serves.
  • Test: dotnet test Fuchs.Tests/Fuchs.Tests.csproj -c Debug.
  • Always build and run the test suite before committing. The build emits many pre-existing analyzer/platform warnings (CA1416 etc.) — those are expected; only treat : error lines as failures.
  • Commit only when asked. Co-author trailer, when requested: Co-Authored-By: Codex <noreply@openai.com>.
  • The working tree may contain an untracked Fuchs_Database/ SQL project — it is not part of app changes; never git add -A it into an unrelated commit. Stage explicit paths.

Coding Standards

  • C# only. Modern, performance-oriented .NET 10 (async/await, LINQ, DI).
  • Keep files <= 400 (max 600) lines; refactor larger files into focused classes.
  • PascalCase types/methods, camelCase locals/params.

Configuration

  • All settings in Fuchs/appsettings.json — never Web.config / System.Configuration.ConfigurationManager. App settings nested under "Fuchs"; connection strings under "ConnectionStrings".
  • FuchsOcmsIntranet.Initialize(configuration) runs in Program.cs before DI registration.
  • appsettings.Development.json (git-ignored) overrides secrets locally.
  • Fuchs:Email:OverrideRecipient (IOptions<FuchsEmailSettings>, section Fuchs:Email) is a dev/test safety net: when non-empty, ProcessWebComService.SendEmailAsync discards the real recipient of every outbound email and redirects it to this single address instead, so a locally-enabled mailer can never reach a real tenant-owner or end-customer. Set only in appsettings.Development.json — must stay empty in Production.

Libraries

  • Do not upgrade Spire.PDF beyond 8.10.5. Prefer OCORE / OCORE_web / OCORE_web_pdf helpers over rewriting. Do not use OCMS/OCMS_sharp — OCORE only.
  • Do not upgrade the vendored TinyMCE (Fuchs/wwwroot/lib/tinymce/; legacy copy at Fuchs/Scripts/tinymce/) beyond 6.8.6 — the last MIT-licensed release; 7.0+ is commercially licensed. See Fuchs/Docs/Decisions/0013-tinymce-pinned-to-6-8-6.md.

Services & Dependency Injection

  • Business logic lives in DI-registered services under Fuchs/Services/ behind interfaces, injected into IntranetController. Do not reintroduce static God-classes or pass the controller into helpers.
  • Services: IComService (ProcessWeb Mailer API; honors the Fuchs:Email:OverrideRecipient dev safety net — see Configuration), IPdfService, IInvoiceService, IReminderService, IReportService, IWidgetService, IBankingService, IMfrClientFactory, ISystemStatusService (Admin module diagnostics: config snapshot + DB/Key Vault/blob/MFR connectivity probes + test-email; restricted to fds_sys > 4). Stateless ones are singletons; DB/request-scoped ones are scoped (see Program.cs).
  • FdsInvoiceData / FdsReminderData are pure data holders; load/persist/render belongs in services. No Task.Run(...).Wait() sync-over-async.
  • Data access is SQL-first via OCORE helpers + stored procedures (no EF Core).

MFR ERP integration

  • MFR_RESTClient is the REST/OData client for the mfr (Mobile Field Report) ERP. Its contract (base URLs, auth, OData conventions, pagination, error/retry, deep-create + document-upload) is in MFR_RESTClient/Docs/mfr_interface_description.md — read it before changing the client.
  • HTTP Basic auth; configurable timeout; idempotent GETs retry on transient errors (429/5xx, network/timeout) with backoff. Create clients via IMfrClientFactory. Active project is MFR_RESTClient.csproj (legacy .vbproj removed).

Database

  • Schema source of truth: Fuchs_Database SSDT project. SQL-first backend (stored procs, table types e.g. fds__tt__bankingtransactions, functions via OCORE — no EF Core).
  • Changing a proc signature or table type -> update the SSDT project and the calling C# together; verify every [dbo].[…] the backend calls exists in Fuchs_Database.

Bank statement parsing (MT940 + CAMT)

  • MT940Parser (external, SWIFT text) and CAMTParser (in-repo, ISO 20022 camt.052/053/054 XML) feed the same pipeline.
  • BankingService.ParseToDatatable auto-detects (XML -> CAMT, else MT940) -> fds__tt__bankingtransactions. bam/up + the frontend accept both formats. CAMTParser is namespace-agnostic. Keep both column mappings aligned with the banking schema.

Observability

  • OpenTelemetry. Instrumentation is centralised in Fuchs/Observability/FuchsTelemetry.cs (one ActivitySource, one Meter).
  • For meaningful operations: start an activity, record the matching counter/histogram, and log entry/result/timing/errors via injected ILogger<T> using structured placeholders (never interpolated strings) — never {@Foo}; plain Microsoft.Extensions.Logging (unlike Serilog) does not strip the @, it becomes a literal part of the key.
  • Always collected; OTLP export opt-in via Fuchs:Telemetry:OtlpEndpoint. No exporters that hard-fail without a collector.
  • DB log sink (ADR 0024): every _logger.LogWarning/-Error/-Critical call additionally lands in [dbo].[fds__debuglog] via the OCORE MultiLogger SQL sink (fds__admin_logdebug_ext), gated by Fuchs:Logging:SqlSink:Enabled (default on). RequestLogScopeMiddleware (Fuchs/Logging/) attributes every log line in a request to a request id/path/authenticated user automatically. Give a log call an explicit EventId (its Name becomes the row's CodeReference) when the call site should be individually greppable — see FIS_SQLOptions, IntranetController.Do's catch-all, PdfPreviewAsync, and the banking-upload handlers for the pattern. Do not also call the retired Fuchs_intranet.debug_log — one ILogger call is the single write path; a second explicit DB write duplicates the row.

Testing

  • xUnit in Fuchs.Tests. Testing must be extensive, not superficial:
    • For each service/handler change, cover both an intentionally succeeding and an intentionally failing path where feasible (stubs/mocks; InternalsVisibleTo is enabled).
    • Cover edge cases and boundary conditions (empty/null/invalid input, disabled/feature-flag-off states, API/network errors) via [Theory]/[InlineData]/[MemberData] rather than a single happy-path [Fact].
    • When behavior depends on configuration (e.g. appsettings.json vs appsettings.Development.json), prefer loading the real files layered the same way ASP.NET Core does (ConfigurationBuilder + AddJsonFile) over hand-typed literals only, so drift in the actual files is caught (see ProcessWebComServiceTests.SendEmailAsync_OverrideRecipientFromRealAppsettings_ClearsRecipientWheneverConfigured for the pattern).
    • Assert on the observable contract (e.g. the outgoing request payload) and on emitted telemetry (counters/histograms) when the code records them — not just the boolean return value.
    • Name tests MethodName_Scenario_ExpectedResult.
    • Cover pure logic for DB-bound paths that can't be unit-tested.

Secrets (Azure Key Vault)

  • Full naming rules live in .github/copilot-instructions.md (kept in sync). In short: names match ^[0-9a-zA-Z-]+$, hierarchy via -- (-> :), underscores -> -, app prefix fuchs; register new keys in ManagedSecretKeys in appsettings.json.

Decisions & Concepts

  • Fuchs/Docs/Decisions/ holds immutable ADRs (architecture decision records); Fuchs/Docs/Concepts/ holds living design write-ups kept in sync with the code. Each folder's README.md explains the format, naming, and required YAML frontmatter — read it before creating or editing entries there.
  • Accepted decisions must be followed. Before working in an area covered by a decision, read it and conform to it; don't silently deviate. Every file's YAML frontmatter has an applyTo glob — scan frontmatter across the folder first (cheap) and only read the full body of entries relevant to the files you're touching.
  • Capture new decisions and concepts as they happen. When a non-obvious architectural or cross-cutting choice gets settled (by the user or in the course of implementation), add a decision in Docs/Decisions in the same change, and create/update the matching concept doc in Docs/Concepts if the subsystem's design is otherwise non-obvious from the code.

Documentation map

  • Fuchs/Docs/ARCHITECTURE.md — solution architecture (keep current when structure changes).
  • Fuchs/Docs/USER_GUIDE.md — end-user process guide.
  • Fuchs/Docs/Decisions/ — ADRs; see Decisions & Concepts above.
  • Fuchs/Docs/Concepts/ — living subsystem design docs; see Decisions & Concepts above.
  • MFR_RESTClient/Docs/mfr_interface_description.md — mfr ERP REST/OData interface contract.
  • .github/instructions/*.instructions.md — domain-specific contributor guidance.