# Assessment output strategy Decision: 2026-09-18, explicitly approved by the user. The 2026-09-23 user request starts the A/B HTML deliverable through Client Info (Phase 7 UI integration, cross-cutting report projection). Artifact C and D remain separate future work. ASSESSOR produces **three** artifacts from one assessment: | Artifact | Audience | Nature | | --- | --- | --- | | A — Management Overview (HTML) | customer decision makers | frozen snapshot | | B — Technical Report (HTML) | engineers reproducing a finding | frozen snapshot, complete | | C — Arbeitsgrundlage (XLSX + HTML) | the people doing the work | living document, curated | | D — Delta Report (HTML, management + technical cut) | both, after a rescan | frozen snapshot of the change | The dividing principle: **a report states the finding and its proof; the Arbeitsgrundlage states the measure and who owns it.** Remediation progress is proven by rescans, never by self-reported status. See *Progress is proven by rescan* below. ## Approved delivery decisions | Decision | Choice | | --- | --- | | Delivery | Separate files per artifact | | Design | ASSESSOR application look | | Technical scope (B) | Deviations, diagnostics and score-neutral classes | | Interaction | Static, print- and PDF-ready, no JavaScript | | Language | German, all artifacts | | Arbeitsgrundlage format | XLSX to work in, HTML as the frozen status view | | Arbeitsgrundlage scope | Curated selection, plus the complete list on a second sheet | | Progress evidence | Rescan only; planning fields stay but are marked unverified | | Delta delivery | Own artifact, management and technical cut | Separate files keep the confidentiality boundary real: the Management file can be forwarded without carrying registry keys, SIDs, evidence paths or parser versions in its source. A single file with a hidden section would still contain them. ## Report language Both reports are written in German: headings, prose, labels, table headers, legends and every explanatory sentence. This does not change the Client Info dashboard, which remains English-only by its own decision. The reports are separate customer-facing artifacts; the two rules stand side by side and neither overrides the other. German is the carrier language. **Established English technical terminology stays English** and is not translated, not hyphenated into a German compound and not replaced by an invented German equivalent. This is the general rule, not a list of exceptions. It covers at least: - the agreed GPO verdicts `Deviation`, `Passed`, `Not configured`, `No baseline rule`, `Not comparable`, `Conflicts`, `Duplicates` - the security domain names from `src/features/dashboard/security-domains.ts` - assessment vocabulary such as Finding, Baseline, Score, Scope, Severity, Pass Rate, Review, Owner, Due Date, Remediation, Evidence - platform vocabulary such as Group Policy, Registry, Domain Controller, Privilege, Attack Path, Hardening Reason: these are the terms the readers already use, and most of them appear verbatim in the application's Work filters and in the Client Info dashboard. A reader must be able to match a line in the report to the view it came from, and a translated second vocabulary would weaken the documented agreements. Where a term is not self-explanatory to a management reader, it is introduced once with a short German explanation and then used in its canonical English form. German prose carries the sentence; the term inside it stays English. Original source values, registry keys, value names, principals, evidence paths and native identifiers are never translated or localized. Numbers and dates follow German formatting. The shared projection stays language-neutral and carries data, not display strings. All German wording lives in the renderers. ## Shared report projection Implemented in `src/reporting/report-projection.ts` (moved from `src/domain` on 2026-09-30) (`REPORT_PROJECTION_VERSION = '1.1.0'`), covered by `tests/domain/report-projection.test.ts`. All artifacts read **one** projection built in the platform-neutral domain layer. The projection is the single source of truth; the renderers only choose depth, never content. Two consequences are binding: - A number that appears in both reports is the same number. The Management Overview never rounds, re-derives or re-filters a value that the Technical Report states differently. - Renderers contain no source-specific parsing, no scoring arithmetic and no database access. They receive the finished projection. The projection is built on export only. It is not part of the workspace load path and must not derive presentation fields for records no report section consumes. ## Report A — Management Overview **Superseding user decision 2026-09-29 (Phase 7):** The Management Report is generated offline and without an LLM from the current, selected client's existing report projection. It is a factual overview, not a scanner finding list, mixed-method score or speculative risk assessment. The earlier Report A content contract below (overall-score donut, domain spine, priority findings, remediation status and source overview) is retained as historical context but does not govern the current Management export. Current Report A shows project name, report date, the captured domain and DC count when present, counts of assessed and deviating results, a heatmap of deviations divided by assessed results for each catalog Security Domain, and a second stacked chart of passed and deviating results with the Pass Rate and underlying counts. A domain with no assessed results has no percentage or heat level. The colors and percentages describe result frequency only: they do not express severity, business impact, compromise, protection or progress. The GPO domain chart counts assigned result rows. When the separate policy evaluation exists, its failing comparisons and affected settings are stated separately and never equated to result rows. The export names its statement limits: source observations may come from different times; affected business services, exploitation and current remediation state are not derivable from these facts. It does not extrapolate backup age or add invented risk scenarios, owners, measures or deadlines. Technical results remain in Report B. The document is standalone HTML with inline styles and no external resources. **User decision 2026-09-30 (Report Engine v0.2):** Directly below the three key figures, Report A shows a `Kernaussagen` block with three deterministic statements. Each is a fixed German sentence into which only projection values are inserted: - **Gesamtbild:** assessed and deviating results with their share. - **Schwerpunkt:** the domain or domains with the most deviations and with the highest deviation share. Ties name every tied domain, and the sentence carries the note that share and frequency say nothing about severity or business risk. - **Aussagegrenze:** not run, not applicable, failed and otherwise unassessed checks, which are neither passed nor deviating and enter no rate. A statement without a factual basis is omitted. The statements are language-neutral data in the projection (`statements`, built by `src/reporting/report-narrative.ts`). The wording lives in `src/reporting/report-statement-text.ts`, which both renderers share. The Technical Report proves every statement with the same sentence and the result IDs behind each number (`Belege der Kernaussagen`). Report A contains no IDs. ### Historical Report A design (superseded 2026-09-29) Implemented in `src/reporting/management-overview-report.ts` (`MANAGEMENT_OVERVIEW_RENDERER_VERSION = '1.0.0'`), covered by `tests/exports/management-overview-report.test.ts`. Answers: how do we stand, what is most urgent, what is this based on, what do we not know. Its reading order follows the agreed Client Info grammar. **Named deviation from the `assessor-ui` skill:** a delivered document uses no application shell — no sidebar, topbar, command band or context strip — and none of the four page archetypes, because it is read and printed outside the application. Typography roles, donut geometry, tile and segment grammar, status colour rules and the panel inset apply unchanged. Light is the document default because a report is printed on white; dark renders from the same semantic roles through `prefers-color-scheme`. The bundled Inter is referenced by family name with a system fallback. No font, style, script or image is loaded when the file is opened. ### 1. Assessment identity Customer, environment (domain, forest, domain controller count from the environment inventory), assessment period, report date, ASSESSOR product version. ### 2. Overall posture The single score as a donut using the shared donut geometry, with its method label. Binding wording from `SCORING_MODEL.md`: - The GPO contribution is *conformance of configured, comparable in-scope settings*. Binding German wording: „Konformität der konfigurierten, vergleichbaren Einstellungen im Scope". It is never labelled baseline compliance, baseline coverage or proof that recommended settings are implemented — in German neither „Baseline-Compliance", „Baseline-Abdeckung" nor any phrasing implying the baseline is fully rolled out. - PingCastle enters as `100 − native risk`; the raw risk stays secondary context. - A mixed-method index is named as such. Security Domain values are pass rates and are not a decomposition of the index. ### 3. Security Domains The eight domains from `src/features/dashboard/security-domains.ts` as a connected pass-rate spine beside the overall score. Not a table. ### 4. Priority Findings Grouped by security domain. **There is no global cross-tool ranking.** Inside a domain, findings are ordered by the declared priority basis of their own source, and that basis is named at the point of display. Per entry: title, domain, the source's own rank value with its basis, one sentence of impact, plus owner and status when a review exists. GPO deviations form their own equal-weight block with their count, the affected policy settings and the scope limit. They are never assigned an invented criticality. No registry keys, no SIDs, no file paths, no raw values. See *Priority without a common severity* below for why. ### 5. Remediation status Open, in progress, accepted, overdue, and findings without an owner — derived from the stored reviews including accepted-risk data. If no reviews are maintained, the section is omitted rather than rendered empty. ### 6. Coverage and gaps What was assessed, what was not, unassessed controls, and every scope limit that bounds a statement. A GPO import covering a subset of the domain is reported as scope-limited and never presented as a domain-wide or effective-device claim. This section is what makes the report defensible. ### 7. Source overview Per tool: run or file date, score contribution, and the method that produced it. ### Excluded from Report A Per-record tables, raw values, evidence paths, diagnostics, parser, analyzer and manifest versions. ## Report B — Technical Report The initial static renderer is implemented in `src/reporting/technical-report.ts`. It retains the current internal result projection, original Work fields when a canonical occurrence matches, and the retained per-backup GPO instructions. Missing source facts are labelled as unavailable; the renderer never invents actual/expected values or an endpoint-effective state. A dedicated completeness audit of diagnostics, reimport metadata and all source formats remains open and must not be mistaken for a final acceptance of every section below. Answers: what exactly is configured, what should it be, where does that stand, how does ASSESSOR know, what do I do. ### 1. Scope and provenance Imported artifacts, processing, analyzer and manifest versions, reimport status, aggregation and evaluation run identifiers. Enough to reproduce the result. ### 2. Result inventory Totals per status, severity, domain and tool, including the score-neutral classes. Passed results appear here as a count. ### 3. Findings in detail Grouped by security domain, ordered by severity inside each group. Per finding: - technical identity and scope - actual against expected value, with the field-specific deviation marking used by the Finding Reader: `Operation`, `Data` and `Type code` for registry policy differences, several fields only where each is independently proven - principal sets compared per member: only configured principals absent from every retained baseline variant are marked; a principal also present in the expected baseline stays neutral - GPO source attribution as vertically stacked sections per exact backup identity, each with evidence reference, source-local sequence and every original instruction immediately visible - conflict and duplicate flags, kept separate from the policy verdict - baseline variants where more than one exists - the remediation step from the review ### 4. GPO policy register All seven verdicts in full: `Deviation`, `Passed`, `Not configured`, `No baseline rule`, `Not comparable`, `Conflicts`, `Duplicates`. The score-neutral verdicts are labelled as score-neutral at the point of display, so that a reader cannot mistake `Not configured` for a remediation obligation. ### 5. Diagnostics Unresolved identities, parser gaps, scope limits, declared-against-counted mismatches and incomplete backups. Each observation appears once with its authoritative resolver reason and is not restated by snapshot aggregation. ### 6. Appendix Complete result index. ## Artifact C — Arbeitsgrundlage Decision: 2026-09-18, explicitly approved by the user. Answers: what do we do, who owns it, by when, where does it stand. The measured volume makes this its own artifact: the stored data holds **1,806 actual deviations** (CIS-CAT 969, Maester 350, PingCastle 277, Purple Knight 210) plus the GPO deviations. Rendering each of those with full technical depth inside Report B would produce a document nobody works with. Size is not the decisive reason, though. A report is a frozen snapshot that proves what was found on a date and is archived unchanged. An Arbeitsgrundlage is a living document that the customer edits for weeks: owner, due date, status, comment. Forcing both into one file makes both worse. ### Data model The Arbeitsgrundlage is the stored review model in `src/domain/review.ts`: `status`, `priority`, `owner`, `due`, `prerequisites`, `access`, `nextStep`, `evidence`, `comment`, the markers `quickwin`, `blocked`, `external`, `question` and `notRelevant`, plus `riskAcceptance`. `include` is the curation switch. The consultant selects what reaches the customer as a work item. Shipping 1,806 unfiltered items produces no action at all. ### Structure - **Sheet 1 — Maßnahmen.** Only the curated selection, with a stated selection criterion. One row per work item. - **Sheet 2 — Vollständige Abweichungen.** All deviations for reference, so the customer can pull further items in without the working list overflowing. The HTML variant renders the same content as a frozen status view for status meetings and filing. It is generated from the same projection, never maintained separately. ### No duplicated technical depth A work item carries its stable finding identifier, the measure, prerequisites, required access and the owner — not the evidence, not the source attribution, not the baseline variants. Those stay in Report B and are reached through the identifier. One fact, one place. Repeating the technical detail would leave two diverging versions after the first correction. ## Progress is proven by rescan Decision: 2026-09-18, explicitly approved by the user: *"Den Fortschritt dokumentiere ich durch Rescans — für mich zählt nur der technische Beweis."* Remediation progress is established by comparing scan runs, never by a status somebody ticked. This follows `TIMESLOT_COMPARISON_PLAN.md`, which already replaced the conclusion *absent equals remediated*. ### Binding rules - No artifact presents a self-reported status as a resolved finding. A work item marked done in the Arbeitsgrundlage stays an open deviation in every report until a rescan proves otherwise. - A delta uses the lifecycle vocabulary of the timeslot contract — demonstrably remediated, still open, unresolved, not comparable — never a binary done/open. *Unresolved* is a legitimate and frequent outcome and is shown as such. - Absence of a finding is not proof of remediation. Remediation is claimed only where the profile-bound negative-proof contract is satisfied for exactly that rule and object. - Result presence and deviation lifecycle stay separate. A rule can still be reported while its concrete deviation is fixed. - No effective-endpoint claim is derived from a backup-configuration comparison. ### Why the caveat is not theoretical The stored data shows how easily absence would be misread: Maester carries **845 `not_run`** and 10 `execution_error`, Purple Knight 34 `not_applicable` and 10 `not_run`. A check that did not run would otherwise turn into a success message — the most dangerous false statement a progress report can make, because it errs in the comfortable direction. ### Artifact D — Delta Report An own artifact per rescan, in the same two cuts as the reports. The initial report stays frozen and is never rewritten; the change is a separate document with its own reporting date. **The comparison is against a chosen reference run, and that choice is made in ASSESSOR — not at export time.** The export renders a comparison that already exists; it contains no run-selection logic, no fallback to "the previous run" and no implicit pairing of its own. Consequences: - The chosen reference run is part of the stored comparison, together with its contract version, engine versions and input fingerprint. The export reads it and never re-derives it. - The document names both compared runs explicitly — reference and current, each with its identity and date — so it stays self-describing once it is out of the application. - Without a stored comparison there is no Delta Report. The export is unavailable rather than guessing a pairing. This keeps comparison a domain concern and presentation a separate one: the Delta Report is a renderer, not a second comparison engine. - **Management cut:** score movement, what is demonstrably remediated, what persists, what is newly found, and how much of the comparison is unresolved or not comparable. The unresolved share is stated, not hidden. - **Technical cut:** per finding the lifecycle verdict with its evidence — which run, which identity match, which contract qualified it — plus the disqualifying diagnostics where a comparison was not admissible. ### Planning fields in the Arbeitsgrundlage `status`, `due`, `owner` and the markers stay in the XLSX as the customer's own steering aid. They are labelled as unverified self-report at the point of display. ASSESSOR never promotes them into a report as a result. The export therefore stays one-way by design, not by limitation: progress does not need a return channel because it comes from the next scan. ## Priority without a common severity Decision: 2026-09-18, explicitly approved by the user. Severity is not a property every scanner delivers. Measured against the stored data: | Tool | Results | Severity coverage | Native ranking signal | | --- | --- | --- | --- | | CIS-CAT | 1,233 | none, all `unknown` | none | | PingCastle | 277 | derived from Points | Points 0–50+ | | Maester | 1,590 | 99.4 % | native severity | | Purple Knight | 1,309 | 100 %, 187 `critical` | native severity | | GPO / baseline | — | none | none | A severity-ranked global list would therefore drop CIS-CAT and GPO out of the Priority Findings entirely, let Purple Knight dominate merely because it is the only source that emits `critical` natively, and keep PingCastle permanently below `critical` because `pingCastleSeverityFromPoints` caps at `high` by construction. That ordering would describe the report formats, not the customer's risk. ### Binding rules - Priority Findings are grouped by security domain. The eight domains are tool-neutral and already exist. - Within a domain, findings are ordered by the declared priority basis of their own source. The basis is visible to the reader. - No ranking is ever produced across sources with incomparable bases. A single ordered list that mixes them is not permitted. - GPO deviations are equal-weight. They get their own block with count, affected policy settings and scope limit, and never an inferred criticality. - A source without any defensible ranking basis lists its findings unranked and says so. Unranked is an acceptable answer; an invented rank is not. ### Profile obligation Implemented as `ToolPriorityBasisDefinition` in `src/domain/tool-profile-definition.ts`, required on every `ToolProfileDefinition` and enforced by `tests/profiles/declarative-profiles.test.ts`. Analogous to the existing `securityScoring` obligation, every tool profile declares a **priority basis**: the exact mapped properties, scale, direction and semantics of the value used to order its findings — or an explicit reason why no usable basis exists. Never infer a priority from an arbitrary field named severity, level, score or rank. A declaration may also list `excludedProperties`: fields that resemble a basis but are not one, each with its reason. This is what stops a later change from quietly promoting them. | Profile | Declared basis | | --- | --- | | Purple Knight | `severity` — native criticality, the only source reaching `critical` without derivation | | Maester | `severity` — native per test | | PingCastle | `numeric` on Points, `0-50+`, higher-is-worse | | CIS-CAT | `unavailable` — benchmarks state no per-recommendation criticality | | Generic | `unavailable` — unrecognized fields establish no scale or direction | PingCastle is declared on Points rather than severity on purpose: its canonical severity is *derived* from Points by `pingcastle-severity-from-points` and is capped at `high` by construction. Points keeps the granularity the capped severity loses. `reportPriorityBasisFrom` turns an `unavailable` declaration into an explicit `equal-weight` basis carrying the profile's own reason, so a report states why a source is unranked instead of implying the declaration is missing. A derived, normalized cross-tool ASSESSOR priority is **not** approved. It would require its own versioned rule and an explicit decision of its own. ### Guarded mis-mapping risk `src/profiles/cis-cat/cis-cat-definition.ts` declares `severityHeaders: /^(?:severity|level)$/i`. CIS `Level 1` / `Level 2` is a hardening profile tier, not a criticality. It falls through to `unknown`, which is harmless, and it is now recorded as an excluded property with its reason and covered by a test. It must not be "corrected" into a severity, because that would manufacture criticalities the benchmark never stated. ## Report Engine boundary Decision: 2026-09-30, explicitly approved by the user. Implementation plan: Phase 7, *Report Engine als eigenstaendiger Nur-Lese-Baustein*. The Report Engine is a self-contained, read-only building block inside ASSESSOR. It produces the reports offline, without an LLM, and never changes ASSESSOR logic or data. - **Location.** The projection, correlation, input assembler, `client-report-projection` and both renderers were moved unchanged to `src/reporting/` on 2026-09-30. The shared security-domain catalog, scoring and review hydration stay ASSESSOR components and are only imported, so that a report number always equals the Client Info and Work number. An architecture test enforces the boundary. - **Read-only access.** The engine reads through a read-only repository port and a dedicated read-only database pool. It detects a repository revision change during assembly and repeats the read instead of mixing states. - **Server rendering.** Since v0.1 the server renders both files once per run and archives them. Client Info downloads exactly the archived file through `POST /api/v1/projects/:id/report-runs`. - **Frozen state.** Every report run is archived server-side in its own folder and never modified afterwards. The folder holds `projection.json`, both HTML files and a manifest with file hashes, engine versions, repository revision, scope and time. The archive lives outside the repository and outside synced folders (`ASSESSOR_REPORT_ARCHIVE_DIR`) and is deleted together with its client. `projection.json` and the manifest are internal. They are never delivered with Report A, which keeps the separate-files confidentiality boundary. - **Boundaries.** - Only the shared security-domain catalog. - Severity only per source, never summed across scanners. - Review state only as unverified self-report, and never in Report A. - Scenarios and cross-finding relationships only from the context engine (Phase 13E), never from report-local rules. ## Guardrails for both reports - Score-neutral classes never appear in Priority Findings and never enter the score. They remain fully inspectable in Report B. - A GPO verdict is taken only from the authoritative rolled-up policy verdict and the explicit conflict and duplicate flags. Never from comparison prose, historical results, baseline text, guidance or evidence. - Unknown or unavailable facts render as an em dash, never as an inferred zero. - Both files are self-contained: inline styles, no CDN, no external fonts, scripts or images, no network access when opened. A report leaves the building and must not load anything. - The bundled Inter variable font is embedded or a local system stack is used; no font is downloaded at view time. - Shared typography roles, donut geometry, marking semantics and panel inset apply as in the application. Status colour stays in accent lines, markers, arcs and score nodes, never as a full status fill. - Static output: no JavaScript, printable to PDF without layout loss. ## Relationship to the existing export ### Current-view export — explicit user extension, 2026-09-18 The Work toolbar's CSV/MD/HTML buttons now export the current filtered and sorted view, including Results, with exactly two content columns: `Soll-Zustand` and `Abweichungen`. This supersedes their old marked-only selection. The user can still restrict the view with `My Workspace`; that restriction combines with the other active filters. A view export is a frozen, scoped inspection snapshot, not the curated Arbeitsgrundlage C, a replacement for reports A/B, or a delta. `src/exports/work-view-export.ts` supplies one click-time presentation projection for these three formats, consuming existing Work records and the shared Results resolver without ranking, scoring, re-evaluation or database queries. The shared `report-projection.ts` contract for artifacts A–D remains unchanged. Native remediation, baseline variants, exact source attribution and verification gaps stay visible. Review completion never becomes a technical resolution. Long CSV cells are preserved in labelled continuation rows (same finding ID), keeping two columns and avoiding Excel's cell-size truncation. HTML is self-contained, script-free, with locally bundled Inter embedded and responsive wrapping. The following paragraph records the earlier strategy baseline; its description of the toolbar's marked-only behaviour has been superseded by this extension. `src/exports/dashboard-export.ts` and `ActionlistControls` already export the review model as CSV, Markdown and a minimal HTML page. That HTML knows nothing of the score, the security domains, GPO verdicts or provenance and uses Segoe UI with fixed colours. The existing action-list export is the seed of artifact C, not of the reports. The reports are new; the action-list HTML is replaced by the artifact C renderer, and the CSV path stays useful for ticket-system import. ## Open decisions - How many findings each domain block shows before it is truncated, and how a tie inside one source's ranking basis is broken. - Whether the Microsoft baseline packages carry any per-setting classification that could later give GPO deviations a defensible basis. Until proven, GPO stays equal-weight. - The selection criterion wording for the curated sheet, and whether it is fixed or written per engagement. - Which profiles can satisfy a negative-proof contract at all. Profiles that cannot will never produce a *demonstrably remediated* verdict, and the Delta Report must say so per source rather than leave the reader guessing. - File naming convention across all artifacts, including how a delta names its two compared runs. ## Dependency Artifact D depends on the timeslot comparison actually producing stored results. `assessor_timeslot_evidence_comparisons` exists with its contract version, engine versions and input fingerprint, but currently holds no rows. Until the comparison is implemented per `TIMESLOT_COMPARISON_PLAN.md`, artifacts A, B and C are deliverable and D is not. ## Change log - 2026-09-18: Strategy established. Delivery, design, technical scope and interaction decided by the user; content inventory per report fixed. - 2026-09-18: Report language decided by the user — German for both reports. The agreed GPO verdict vocabulary and the security domain names stay canonical English inside the German text; source values are never localized. The Client Info English-only rule is unaffected. - 2026-09-18: User correction — established English technical terminology stays English as a general rule, not as an enumerated exception. German carries the prose; technical terms are never translated or turned into German compounds. - 2026-09-18: Priority model decided by the user after the user raised that not every tool provides severity. Verified against stored data: CIS-CAT and GPO have no severity at all, PingCastle cannot reach `critical` by construction. Priority Findings are grouped by security domain with a per-source, declared and visible ranking basis; no cross-tool ranking; GPO equal-weight in its own block. Profiles must declare a priority basis. A normalized cross-tool priority is explicitly not approved. - 2026-09-18: Third artifact added on the user's proposal. The per-finding working basis becomes its own deliverable — XLSX to work in plus an HTML status view, curated through `include` with the complete deviation list on a second sheet. Decisive reason is the differing lifecycle, not only the measured volume of 1,806 deviations. Work items reference findings by identifier and never repeat the technical depth of Report B. - 2026-09-18: Progress model fixed by the user — progress is documented by rescans, only technical proof counts. A fourth artifact is added: the Delta Report, in a management and a technical cut, using the timeslot lifecycle vocabulary. Self-reported status never becomes a reported result; planning fields remain in the Arbeitsgrundlage marked as unverified. The earlier open question about re-importing a maintained worklist is closed: the export is one-way by design. - 2026-09-18: Reference run decided by the user — a delta compares against a chosen reference run, and the choice is made in ASSESSOR. The export renders a stored comparison and carries no run-selection logic, no previous-run fallback and no implicit pairing. Both runs are named in the document. - 2026-09-18: Shared projection implemented as `src/domain/report-projection.ts`. The security-domain assignment is injected by the caller so the domain layer never points at the feature layer. Profiles do not yet declare a priority basis, so the projection accepts one per source and falls back to `undeclared` with its own diagnostic rather than guessing. Declaring the basis inside `ToolProfileDefinition` is the follow-up step. - 2026-09-18: Profile obligation implemented. `ToolPriorityBasisDefinition` is required on every registered profile and enforced by a contract test. Purple Knight and Maester declare native severity, PingCastle declares Points because its severity is derived and capped, CIS-CAT and Generic declare unavailable with a reason. CIS `Level` is recorded as an excluded property so it cannot be promoted into a criticality later. - 2026-09-18: Management Overview renderer implemented as `src/exports/management-overview-report.ts`. Static, self-contained German HTML with no script, inline handler, external URL or `url()`. Deviation from the `assessor-ui` shell and page archetypes named above; all token, typography, donut and status rules kept. An out-of-range score is clamped and a missing one draws no arc at all. - 2026-09-30: Report Engine boundary decided by the user. The report modules move unchanged to `src/reporting/` and access is read-only. Every run is archived immutably server-side, outside the repository and synced folders. Domain, severity, review-state and scenario boundaries are confirmed, with scenarios only from the Phase 13E context engine. Documentation only; no implementation yet. - 2026-09-30: Report modules moved unchanged to `src/reporting/`, with their tests in `tests/reporting/`. `tests/reporting/reporting-boundary.test.ts` enforces the importer allow-list and the absence of writing repository calls. Output and numbers are unchanged. - 2026-09-30: Report Engine v0.1 (product 2.10.0). A read-only port, a PostgreSQL reader with read-only sessions and a revision check before and after the read (repeat, otherwise refuse) ensure a consistent read. Every run is archived write-once on the server. The Client Info buttons now download the server-rendered, archived file, and the browser no longer renders reports. Report content is unchanged. - 2026-09-30: Report Engine v0.2. The user chose the statements Gesamtbild, Schwerpunkt and Aussagegrenze, placed below the key figures, with evidence in the Technical Report and the archive. Projection 1.2.0 adds ID lists per domain and per coverage group plus the statements. The narrative module is 1.0.0, the Management renderer 2.1.0 and the Technical renderer 1.1.0, whose result index now shows the result ID.