862988966f
Die Unterlagen fuehrten weiterhin v1.1 als laufenden Meilenstein, obwohl alle 17 Phasen und 83 Plaene ausgefuehrt sind und die Phasen 15 bis 17 zu v1.2 gehoeren. Wer neu draufschaute, sah einen falschen Projektstand. Korrigiert in ROADMAP.md, STATE.md und PROJECT.md: v1.0 bleibt ausgeliefert. v1.1 traegt jetzt den ehrlichen Zwischenstand — alle Plaene fertig, alle Phasen ausser 14 verifiziert, offen allein der Live-Test von Phase 14 gegen ein echtes Exchange-Postfach; inhaltlich abgeschlossen, formal nicht. v1.2 ist abgeschlossen, alle drei Phasen verifiziert. Ergaenzt um den Hinweis, dass kein neuer Meilenstein begonnen wurde und die drei verbleibenden Pruefpunkte Fremdsysteme brauchen. Ausserdem die Zeile zu Phase 2 praezisiert. Sie stand auf "Incomplete", was nach liegengebliebener Arbeit klang und im Widerspruch zu v1.0 als ausgeliefertem Meilenstein stand. Tatsaechlich sind alle vier Baupläne fertig und seit v1.0 in Betrieb; offen ist allein die nie ausgefuehrte Browser-Abnahme, die der User am 2026-09-07 bewusst zurueckgestellt hat, weil Tessera zunaechst nur intern laeuft. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
359 lines
38 KiB
Markdown
359 lines
38 KiB
Markdown
---
|
|
gsd_state_version: 1.0
|
|
milestone: v1.2
|
|
milestone_name: Plattform-Berechtigungen
|
|
current_phase: 17
|
|
current_phase_name: eigene-ausschreibungs-quellen-je-nutzer
|
|
status: verified
|
|
stopped_at: "Wortlaut-Korrekturen abgeschlossen (Quick 260907-ipz): vier Du-Stellen auf Siezen, Loeschdialog mit echter Ein-/Mehrzahl in beiden Sprachen. Im Browser an zwei Gruppen gegengeprueft. Damit sind alle in dieser Sitzung gefundenen Sprachmaengel behoben."
|
|
last_updated: "2026-09-07T12:00:00.000Z"
|
|
last_activity: 2026-09-07
|
|
last_activity_desc: Schriftzug traegt im dunklen Modus die Markenfarbe; Mandanten-Abnahme zurueckgestellt
|
|
progress:
|
|
total_phases: 17
|
|
completed_phases: 17
|
|
total_plans: 83
|
|
completed_plans: 83
|
|
---
|
|
|
|
# Project State
|
|
|
|
## Project Reference
|
|
|
|
See: .planning/PROJECT.md (updated 2026-07-17)
|
|
|
|
**Core value:** Eine zentrale Plattform, in der beliebige Workflow-Tools als Module lizenziert, aktiviert und genutzt werden koennen -- ohne zwischen verschiedenen Anwendungen wechseln zu muessen.
|
|
**Current focus:** Kein laufender Meilenstein — v1.2 ist abgeschlossen, ein neuer wurde noch nicht begonnen.
|
|
|
|
## Current Position
|
|
|
|
Phase: 17 (eigene-ausschreibungs-quellen-je-nutzer) — VERIFIED / passed
|
|
Plan: 3 of 3
|
|
Status: Phase abgeschlossen und im Browser gegengeprueft — bereit fuer /gsd-ship
|
|
Last activity: 2026-09-07 — Browser-Gegenproben #7/#8/#9 nachgeholt, alle bestanden
|
|
|
|
Progress: [██████████] 100%
|
|
|
|
## Performance Metrics
|
|
|
|
**Velocity:**
|
|
|
|
- Total plans completed: 2
|
|
- Average duration: 12 min
|
|
- Total execution time: 0.38 hours
|
|
|
|
**By Phase:**
|
|
|
|
| Phase | Plans | Total | Avg/Plan |
|
|
|-------|-------|-------|----------|
|
|
| 01-foundation-portal-shell | 2/3 | 23 min | 12 min |
|
|
|
|
**Recent Trend:**
|
|
|
|
- Last 5 plans: 01-01 (16 min), 01-02 (7 min)
|
|
- Trend: improving
|
|
|
|
*Updated after each plan completion*
|
|
| Phase 02-authentication-multi-tenancy P02 | 8min | 3 tasks | 26 files |
|
|
| Phase 02-authentication-multi-tenancy P03 | 5min | 2 tasks | 20 files |
|
|
| Phase 05-dashboard-calendar P01 | 14min | 4 tasks | 28 files |
|
|
| Phase 05-dashboard-calendar P02 | 4min | 4 tasks | 16 files |
|
|
| Phase 05-dashboard-calendar P03 | 11min | 4 tasks | 14 files |
|
|
| Phase 05-dashboard-calendar PP04 | 5min | 2 tasks | 11 files |
|
|
| Phase 06-desktop-client-ci-cd P01 | 5min | 2 tasks | 13 files |
|
|
| Phase 06 P03 | 3min | 3 tasks | 3 files |
|
|
| Phase 07-dkv-fleet-module P03 | 5min | 4 tasks | 8 files |
|
|
| Phase 07-dkv-fleet-module P04 | 7min | 3 tasks | 8 files |
|
|
| Phase 07-dkv-fleet-module P05 | 8min | 3 tasks | 11 files |
|
|
| Phase 07-dkv-fleet-module P06 | 5min | 2 tasks | 7 files |
|
|
| Phase 09 P03 | 17 | 3 tasks | 6 files |
|
|
| Phase 09-cert-manager-module P04 | 4 | 3 tasks | 5 files |
|
|
| Phase 09-cert-manager-module P05 | 8 | 3 tasks | 6 files |
|
|
| Phase 09 P06 | 7 | 3 tasks | 7 files |
|
|
| Phase 10 P01 | 15min | 3 tasks | 4 files |
|
|
| Phase 10 P02 | 20min | 3 tasks | 6 files |
|
|
| Phase 10 P03 | 35min | 3 tasks | 9 files |
|
|
| Phase 10 P04 | 25min | 3 tasks | 5 files |
|
|
| Phase 10 P05 | 20min | 3 tasks | 5 files |
|
|
| Phase 10 P06 | 3min | 2 tasks | 4 files |
|
|
**Per-Plan Metrics:**
|
|
|
|
| Plan | Duration | Tasks | Files |
|
|
|------|----------|-------|-------|
|
|
| Phase 11 P01 | 8min | 3 tasks | 11 files |
|
|
| Phase 11 P02 | 4min | 3 tasks | 12 files |
|
|
| Phase 11 P03 | 9min | 4 tasks | 12 files |
|
|
| Phase 11 P04 | 12min | 2 tasks | 5 files |
|
|
| Phase 11 P05 | 24min | 3 tasks | 15 files |
|
|
| Phase 11 P06 | 35min | 3 tasks | 12 files |
|
|
| Phase 12 P01 | 35min | 3 tasks | 7 files |
|
|
| Phase 12 P02 | 8min | 3 tasks | 5 files |
|
|
| Phase 12 P03 | 15min | 2 tasks | 3 files |
|
|
| Phase 12 P04 | 12min | 3 tasks | 10 files |
|
|
| Phase 13 P01 | 35min | 3 tasks | 6 files |
|
|
| Phase 13 P02 | 20min | 2 tasks | 4 files |
|
|
| Phase 13 P03 | 45min | 3 tasks | 5 files |
|
|
| Phase 13 P06 | 15min | 2 tasks | 5 files |
|
|
| Phase 13 P04 | 55min | 3 tasks | 6 files |
|
|
| Phase 13 P05 | 40min | 2 tasks | 4 files |
|
|
| Phase 14 P01 | 13min | 2 tasks | 10 files |
|
|
| Phase 14 P02 | 30min | 3 tasks | 21 files |
|
|
| Phase 14 P04 | 25min | 2 tasks | 6 files |
|
|
| Phase 14 P05 | 50min | 3 tasks | 19 files |
|
|
| Phase 15 P01 | 24min | 3 tasks | 10 files |
|
|
| Phase 15-modul-berechtigungen-gruppen-user-grants P02 | 11min | 2 tasks | 11 files |
|
|
| Phase 15 P05 | 9min | 2 tasks | 5 files |
|
|
| Phase 15 P03 | 32min | 3 tasks | 8 files |
|
|
| Phase 15 P06 | 35min | 3 tasks | 8 files |
|
|
| Phase 15 P07 | 30min | 3 tasks | 7 files |
|
|
| Phase 15 P08 | 30min | 2 tasks | 12 files |
|
|
| Phase 16 P01 | 34min | 3 tasks | 10 files |
|
|
| Phase 16-ad-gruppen-synchronisation P02 | 5min | 3 tasks | 5 files |
|
|
| Phase 16-ad-gruppen-synchronisation P03 | 12min | 2 tasks | 3 files |
|
|
| Phase 16-ad-gruppen-synchronisation P04 | 3min | 3 tasks | 5 files |
|
|
| Phase 16 P05 | 6min | 2 tasks | 4 files |
|
|
| Phase 17 P01 | 76min | 3 tasks | 12 files |
|
|
| Phase 17 P02 | 58min | 3 tasks | 14 files |
|
|
| Phase 17 P03 | 62min | 3 tasks | 13 files |
|
|
|
|
## Accumulated Context
|
|
|
|
### Roadmap Evolution
|
|
|
|
- Phase 17 added (2026-08-12): Eigene Ausschreibungs-Quellen je Nutzer. TenderEmailConfig (heute `tenantId @unique`) und TenderRssFeedSource (heute `url @unique`, plattformweit) wandern auf `userId`; die Rollenpruefung faellt fuer diese beiden Abschnitte weg, das Abrufintervall der oeffentlichen Quelle bleibt Admin-Sache. Ausschreibungsdaten bleiben plattform-global (D-03 aus Phase 10 unangetastet) — geaendert wird nur, wer Quellen einspeist, nicht wer Treffer sieht. Ausloeser: Backlog `2026-08-11-tender-radar-einstellungen-mischen-rollen.md`; die urspruengliche Zustimmung zur gemeinsamen Konfiguration beruhte auf einer missverstaendlichen Erklaerung.
|
|
- Phase 15 added (2026-08-04): Modul-Berechtigungen — Gruppen & User-Grants. Zweistufiger Modulzugriff (Mandanten-Aktivierung + Grants pro Gruppe/User), Gruppen mit optionaler AD-Bindung, default geschlossen, ADMIN/SUPER_ADMIN umgehen Grants, nur Zugriff an/aus. Startet Milestone v1.2 Plattform-Berechtigungen.
|
|
|
|
### Decisions
|
|
|
|
Decisions are logged in PROJECT.md Key Decisions table.
|
|
Recent decisions affecting current work:
|
|
|
|
- [Roadmap v1.1]: 5 phases (10-14) derived from 29 v1.1 requirements, standard granularity — DÖE-only MVP (10: ingestion, 11: filter/UI, 12: notifications) ships first as legally-clean demoable slice, then scraping adapters + cross-source dedup (13), then RSS + email-alert long tail (14)
|
|
- [Roadmap v1.1]: Tender data modeled as platform-global (no tenantId on Tender) — only TenderSavedSearch/TenderMatch are tenant+user-scoped; deviates deliberately from the DKV per-tenant template
|
|
- [Roadmap v1.1]: Scheduler must be poll-once-fan-out-many from Phase 10 onward — explicitly NOT the DKV `findFirst()` single-tenant pattern (documented pitfall)
|
|
- [Roadmap v1.1]: Notification phase (12) requires an explicit matched-vs-notified state with backfill suppression to avoid first-activation email floods and duplicate digest+instant sends
|
|
- [Roadmap v1.1]: INGEST-07 (vergabe24/aumass hard denylist) enforced in the adapter registry in Phase 13, not just documented
|
|
- [Roadmap v1.1]: inbox/ module extraction (ImapProvider/ExchangeInboxProvider out of dkv/) scoped as a one-time prerequisite refactor inside Phase 14, not done earlier
|
|
- [Roadmap]: 6 phases derived from 44 v1.0 requirements, standard granularity
|
|
- [Roadmap]: Research recommends NestJS + Next.js + PostgreSQL RLS + Keycloak + Tauri stack
|
|
- [01-01]: Used Traefik v2.11 instead of v3.4 due to Docker API version incompatibility on host
|
|
- [01-01]: Traefik placed on frontend-net + backend-net for routing to both web and api services
|
|
- [01-01]: API Dockerfile copies full monorepo node_modules structure for pnpm workspace compatibility
|
|
- [01-02]: OKLCH color space for all design tokens (Tailwind v4 native, perceptually uniform)
|
|
- [01-02]: Dark mode uses oklch(0.17 0.01 260) dark gray-blue for comfortable contrast with yellow primary
|
|
- [01-02]: Cookie-based locale (NEXT_LOCALE) instead of URL routing for portal app
|
|
- [01-02]: CSS custom property --current-sidebar-width for responsive main content margin
|
|
- [Phase ?]: Route groups (auth)/(portal) for layout separation: auth pages standalone, portal pages wrapped in AppShell
|
|
- [Phase ?]: Controller-level tenant isolation for ADMIN role as defense-in-depth alongside RLS
|
|
- [Phase ?]: Remember-me controls cookie maxAge (30d session vs browser-session) not separate token type
|
|
- [Phase ?]: react-grid-layout v2 uses dragConfig/resizeConfig instead of isDraggable/isResizable
|
|
- [05-02]: SearchProvider defaults as constants merged with user DB rows (no seed migration)
|
|
- [05-02]: useRef with explicit undefined initial value for React 19 strict TypeScript
|
|
- [05-03]: DAVClient class constructor instead of createDAVClient factory (tsdav v2 type compatibility)
|
|
- [05-03]: Dynamic imports for ews-javascript-api and @microsoft/microsoft-graph-client (lazy-load)
|
|
- [05-03]: In-memory Map cache with 5-min TTL for calendar events (Redis not needed at current scale)
|
|
- [05-03]: ews-javascript-api imported as any (no TypeScript definitions available)
|
|
- [Phase ?]: Optimistic UI for calendar visibility toggle — reverts on API error
|
|
- [Phase ?]: Auto-run testSource after adding new calendar source for immediate feedback
|
|
- [Phase ?]: URL constructor for client-side https-only validation (T-05-14)
|
|
- [Phase ?]: StoreExt trait import required for app.store() in Tauri 2.x
|
|
- [Phase ?]: frontendDist=../src local page, navigate() for runtime URL override
|
|
- [Phase ?]: CSP connect-src wildcard for configurable server URL (D-02)
|
|
- [Phase ?]: Plain docker compose build statt build-push-action (Gitea JWT Pitfall 4)
|
|
- [Phase ?]: Ephemeral runner mode (GITEA_RUNNER_EPHEMERAL=1) fuer Credential-Revokation pro Job
|
|
- [Phase ?]: Separate docker-compose.ci.yml fuer opt-in CI-Infrastruktur
|
|
- [07-01]: DKV PDF uses two extraction formats: single-tx (tab-separated) vs multi-tx (columnar) — both handled in DkvParserService
|
|
- [07-01]: Research Pattern 4 regex replaced with empirical dual-format tab/columnar parser after testing against real invoice.pdf
|
|
- [07-01]: CalendarCryptoService exported from CalendarModule for DKV credential encryption reuse
|
|
- [07-02]: Max attachment size 25MB enforced in both ImapProvider and ExchangeInboxProvider before buffering (T-07-05)
|
|
- [07-02]: ExchangeInboxProvider uses WellKnownFolderName.Inbox + FindItems (not FindAppointments — email vs calendar EWS API)
|
|
- [07-02]: export type {} required for type-only re-exports under isolatedModules TypeScript setting
|
|
- [07-03]: SettingsService.getStartupSmtpConfig uses findFirst (tenant-agnostic) for MailModule startup transport
|
|
- [07-03]: MailModule forRootAsync factory priority: DB SmtpConfig → MAIL_* env → TESSERA_SMTP_* env → localhost:1025 fallback
|
|
- [07-03]: DkvMailService injects SettingsService (not PrismaService directly) to reuse decryption logic
|
|
- [07-03]: user-files/ path resolved via path.resolve(__dirname, 4 levels up) from apps/api/dist/dkv/ to monorepo root
|
|
- [07-03]: Export prune sorted by mtime ascending (oldest first), delete all beyond last 10
|
|
- [07-04]: DkvScheduler v1 uses findFirst() — single-tenant; multi-tenant scheduling deferred
|
|
- [07-04]: Circular dep DkvService<->DkvScheduler avoided via controller coordination after PUT config
|
|
- [07-04]: CronJob resolved via require() workaround (pnpm strict isolation: transitive dep)
|
|
- [07-04]: rechnungsnummer from email subject regex /d{2}-d{9}-d{3}/; fallback=email-{uid}
|
|
- [07-05]: refreshKey lift: parent increments on checkNow success; InvoiceHistoryTable reruns useEffect
|
|
- [07-05]: onItemsLoaded callback: InvoiceHistoryTable notifies parent; parent passes items to ExportFileList (avoids second fetch)
|
|
- [07-05]: Password blank on load: configToForm() always sets password=''; hasPassword boolean drives UX hint only
|
|
- [07-05]: CsvImportButton replace: two-step inline confirm (not full modal); accept=".csv" client-side guard
|
|
- [Phase ?]: T-09-01/T-09-02
|
|
- [Phase ?]: 09-03
|
|
- [Phase ?]: 09-03
|
|
- [Phase ?]: 09-03
|
|
- [Phase ?]: splitCerts PEM path reuses parsePemChain; P7B sniffs first bytes
|
|
- [Phase ?]: splitCerts format crt comparison removed — detectFormat returns pem|der|pfx|p7b only
|
|
- [Phase 10]: Tender ist plattform-global (D-03): kein tenantId, keine RLS/forTenant() — Verhindert versehentliches Ausblenden globaler Daten fuer einen zweiten Mandanten
|
|
- [Phase 10-02]: category: 'procurement' fuer Ausschreibungs-Radar im Marketplace gewaehlt (freies kebab-case, keine Enum-Beschraenkung)
|
|
- [Phase 10-02]: Singleton doe-opendata Poll-Config wird direkt in TendersModule.onModuleInit() upserted, isActive:true per Default (D-04)
|
|
- [Phase 10-02]: Platzhalter-Seite tender-radar/page.tsx nutzt hartkodierten deutschen Text statt next-intl (volle i18n ist CONFIG-03, Phase 14)
|
|
- [Phase 10-03]: Real DÖE fixtures live-captured (pubDay=2026-07-19), not synthetic — 8 notices spanning all D-02 tag classes
|
|
- [Phase 10-03]: sourceNoticeId = OCDS release.id (stable, no version suffix), not the zip entry filename
|
|
- [Phase 10-03]: eForms-DE XML primary for deadlineAt/estimatedValue/procedureType; OCDS primary for ocid/buyerName/title/cpvCodes/region/plz
|
|
- [Phase 10-03]: bundesland left null this plan — NUTS-to-Bundesland mapping deferred to Phase 11 filter UI
|
|
- [Phase ?]: Poll-once-fan-out-many scheduler: single named cron job, no tenant parameter — deliberately drops DKV's activeTenantId/findFirst per-tenant framing (Pitfall D)
|
|
- [Phase ?]: SCHEMA-02 change detection implemented via prisma.tender.upsert({ where: { dedupKey } }) — identical notice never duplicates, changed contentHash updates in place
|
|
- [Phase ?]: D-05 retention as two-phase updateMany/deleteMany with deadlineAt:{lt} filters — null-deadline rows structurally excluded, never auto-expired
|
|
- [Phase ?]: TendersController talks to PrismaService directly (no intermediate service layer) — source-config upsert and global read are simple enough for this plan's scope
|
|
- [Phase ?]: Comment wording avoids the literal tenantId token in tenders.controller.ts to prevent false-positive grep-gate failures (same pattern as Plan 10-04)
|
|
- [Phase ?]: TenderQueryDto.status defaults to active at the controller call site, not baked into the DTO, mirroring DkvController's page/limit default-at-usage pattern
|
|
- [Phase ?]: Admin-Settings-Formular fuer den DOE-Poll (Intervall 5-1440, Aktiv-Toggle) spiegelt InboxConfigForm ohne Credential-Felder, da die DOE-Quelle keine Auth-Oberflaeche hat
|
|
- [Phase 10]: Keine module-loader-Whitelist noetig fuer settings/page.tsx (verschachtelte Route unter bereits whitelisteter tender-radar-Seite)
|
|
- [Phase ?]: estimatedValue kommt als String (Prisma Decimal) im JSON-Response — formatValue() im Frontend prüft explizit auf null/NaN statt zu koerzieren
|
|
- [Phase ?]: openOnly/includeNullValue Default-Semantik lebt im Builder, nicht im DTO
|
|
- [Phase ?]: deadlineFrom/deadlineTo URL-Param-Namen sind 1:1 identisch zu TenderQueryDto-Feldnamen fuer den Saved-Search-Vertrag aus Plan 11-06
|
|
- [Phase ?]: bundesland-Filter matcht exakt gegen die befüllte, indexierte Spalte (statt region-startsWith); region bleibt als eigenständiger Präfixfilter erhalten.
|
|
- [Phase ?]: BUNDESLAND_OPTIONS im Web als kleine Konstante gespiegelt (kein Shared-Package) — web nutzt @tessera/shared nicht, 16-Werte-Katalog rechtfertigt keine neue Cross-Package-Abhängigkeit.
|
|
- [Phase ?]: CPV-Divisions-Kurzkatalog (2-stellig, ~45 Einträge) statt EU-Vollkatalog; hasSome-Match gegen precomputed cpvDivisions-Spalte statt Raw-SQL-Präfix-Match
|
|
- [Phase ?]: cpv-URL-Param als wiederholter Key (?cpv=45&cpv=71) statt Komma-Join; DTO normalisiert Einzelwert per @Transform zu Array
|
|
- [Phase ?]: TenderDetail fetcht selbstständig via getTender(tenderId) im useEffect (Muster SourceConfigForm); page.tsx bleibt reiner ?tender-Param-Reader
|
|
- [Phase ?]: ResultsList.tsx modifiziert (nicht im Plan gelistet) — Rule 3: Zeilen-Klick-Handler war notwendig, um den Plan-eigenen key_link/Done-Kriterium zu erfüllen
|
|
- [Phase ?]: TenderTriage (neu, per-user, kein forTenant/RLS) + favOnly-Sentinel-ID '__none__' für garantierten Zero-Match statt versehentlich ungefilterter Liste
|
|
- [Phase ?]: TenderSavedSearch hat keinen Tender-FK — speichert nur Filterkriterien, unkritisch bei Retention.
|
|
- [Phase ?]: page/tender-URL-Params bewusst aus dem Suchprofil-Payload ausgeschlossen (Navigations-/View-State, kein Filter-State).
|
|
- [Phase ?]: Single notifiedAt field (not two per-channel timestamps) as the matched-vs-notified eligibility gate (12-01, D-06)
|
|
- [Phase ?]: Delta-only matching (no backfill/suppression table) structurally prevents backfill-flood for new saved-search profiles (12-01, D-07)
|
|
- [Phase ?]: TenderMailService swallows missing-SmtpConfig and send-failure into a single boolean (never throws) so the digest cron gets one clean success signal for stamping notifiedAt
|
|
- [Phase ?]: TenderDigestScheduler.runDigest(now) takes an injectable clock parameter for testable Monday-only weekly-digest gating
|
|
- [Phase ?]: Instant-Dispatch filtert die bereits geladenen savedSearches (kein zweiter Query); notifiedAt/channel='instant' nur nach Erfolg gestempelt — identisches Gate wie Digest (D-06)
|
|
- [Phase ?]: Digest-interval selector inline in settings/page.tsx (already 'use client'); instantAlert toggle uses plain checkbox for chip-based SavedSearchBar UI
|
|
- [Phase ?]: SCHEMA-03 fingerprint: title+buyer dominant, CPV division, value-bucket, deadline-day, sha256; dedupKey untouched, fingerprint additive
|
|
- [Phase ?]: One-time TS backfill scripts run via compiled dist/ output (not raw .ts execution) to keep tsc --noEmit clean
|
|
- [Phase ?]: SourceRegistry.register() throws DeniedPortalError for any portal in DENYLISTED_PORTALS (vergabe24, aumass), enforced at DI-registration time not just documented (INGEST-07)
|
|
- [Phase ?]: TenderDedupService tier-2 (source:noticeId) match is unconditional (not gated by dedupActive) — idempotent same-source re-poll must work even with a single active source
|
|
- [Phase ?]: Fingerprint always computed on tender.create regardless of dedupActive, so DÖE-only tenders become fingerprint-matchable the instant a 2nd source activates without further backfill
|
|
- [Phase ?]: 13-06: portalLabel()-Map bleibt lokal in TenderDetail.tsx (nicht geteilt) — einziger Consumer bisher; Fallback auf sourceUrl-Block bei fehlendem/leerem sources[].
|
|
- [Phase ?]: 13-04: cheerio (nicht node-html-parser) fuer NetServer-HTML-Parsing gewaehlt — Legitimacy-Gate flaggte node-html-parser als 'too-new' (Fehlmessung: Latest-Version-Datum statt Package-Alter); cheerio bestand das Gate sauber, Human-Approval eingeholt
|
|
- [Phase ?]: cosinex/DTVP-Selektoren voll befuellt statt needs-JS-deferred (D-01) — Trefferliste ist server-gerendert, live geprueft 2026-07-23
|
|
- [Phase ?]: cosinex/DTVP liefert echte Notice-Deep-Links (pid=) — besser als NetServer-Fallback (Such-URL); Rule-1-Fix: arrayBuffer()+TextDecoder('iso-8859-1') statt res.text(), da Portal ISO-8859-1 mit rohen Latin-1-Bytes sendet
|
|
- [Phase ?]: [quick-260723-e7i]: TenderNormalizerService dispatcht per sourceType (normalizeDoe/normalizeBag) mit geteiltem assemble()-Tail — schliesst den Normalizer-Gap aus 13-VERIFICATION.md fuer NetServer/Cosinex-Bag-Records
|
|
- [Phase ?]: 14-01: DKV inbox providers moved verbatim into shared apps/api/src/inbox/ module; dkv.types.ts re-exports InboxConfig/InboxAttachment/InboxEmail so no DKV consumer import changed (D-01)
|
|
- [Phase ?]: 14-01: fetchMessages() added as a sibling method (findBodyParts/getItemBodySoap) on both providers without touching fetchPdfAttachments (D-02); DKV not switched to it
|
|
- [Phase ?]: 14-01: httpntlm loaded via raw require() bypasses vi.mock — tests seed require.cache with a stub before dynamically importing the provider
|
|
- [Phase ?]: 14-02: fast-xml-parser does not decode numeric HTML entities (Ü) — added explicit decodeNumericEntities() in RssAdapter so service.bund.de titles render correctly
|
|
- [Phase ?]: 14-02: TenderRssFeedSource save-time hostname/SSRF guard is a SEPARATE enforcement point from the code-level SourceRegistry denylist (RSS feed URLs are runtime admin input, not covered by the DI-boot-time gate)
|
|
- [Phase ?]: 14-02: TenderSourcePollConfig.pollGranularity ('day'|'tick') added — 'day' sources keep the byte-unchanged lastIngestedDay gate, 'tick' sources (rss) fetch every active scheduler tick (D-15)
|
|
- [Phase ?]: 14-02: seeded service.bund.de active-by-default RSS feed; zero subreport-elvis rows (no single canonical URL, admin adds relevant municipality feeds)
|
|
- [Phase ?]: 14-03: D-13 read filter fails CLOSED for an unresolved requesting tenant (no auth context) — only global tenders visible, never a private-tenant leak
|
|
- [Phase ?]: 14-03: email-alert TenderSourcePollConfig seeded isActive=false (no safe default mailbox, unlike RSS's service.bund.de) — framework-ready-activation-deferred
|
|
- [Phase ?]: PORTAL_URLS typed as Record<(typeof DENYLISTED_PORTALS)[number], string> so the compiler enforces a URL for every denylisted portal (no re-declared set, no silent gap)
|
|
- [Phase ?]: CoverageBanner's denylist block is independent of the onlyDoe coverage-note condition — component renders when either block has content, not gated behind the DOE-only check
|
|
- [Phase ?]: 14-05: tenderRadar i18n namespace added; Bundesland/CPV filter option values stay canonical German for backend compatibility, only labels translated; portal display slugs left untranslated as proper nouns
|
|
- [Phase ?]: [260728-lih]: DELIBERATE back-compat break — empty groupFilterDns now means 'sync nothing' (was 'import everyone under baseDn'); early-return guard in syncUsersForTenant runs before search/deactivation so an empty selection never mass-deactivates existing LDAP users
|
|
- [Phase ?]: [260729-d3k]: syncUsersForTenant no-op guard re-keyed from empty groupFilterDns to empty parsed base-DN list — Base-DN(s) are now the sync scope, groupFilterDns is an optional extra restriction (ou= = extra bases, group DN = memberOf constraint)
|
|
- [Phase ?]: [15-01]: D-01/D-05/D-06/D-02 wie in 15-CONTEXT.md gesperrt umgesetzt (Nutzer-Checkpoint mit 'proceed' bestaetigt)
|
|
- [Phase ?]: [15-01]: RLS fuer Group/GroupMembership/ModuleGrant aktiviert (T-15-11) statt sie wie Tender* RLS-frei zu lassen
|
|
- [Phase ?]: [15-02]: isDefault:true läuft in this.prisma.$transaction([updateMany, update]); partieller Unique-Index aus 15-01 bleibt Sicherheitsnetz
|
|
- [Phase ?]: [15-02]: remove() fängt zusätzlich P2025 ab (NotFoundException statt unbehandeltem 500) — Rule 2, für Concurrency-Anforderung aus must_haves
|
|
- [Phase ?]: [15-02]: UserService.create ist die einzige Codestelle für D-11/D-12 — LdapService erbt die Regel ohne eigene Kopie (ldap.service.ts unverändert)
|
|
- [Phase ?]: [15-05]: WIDGET_MODULE_MAP bleibt am Ende dieser Phase bewusst leer (D-22) — kein neuer Widget-Typ, keine Schemaänderung, nur die Filtermechanik
|
|
- [Phase ?]: [15-05]: vi.hoisted() für die je-Testfall mutierbare WIDGET_MODULE_MAP-Mock-Referenz — vi.mock wird an den Dateianfang gehoben, ein normaler top-level const wäre zur Factory-Ausführungszeit noch nicht initialisiert
|
|
- [Phase ?]: [15-03]: assertTargetBelongsToTenant als eigenständige Cross-Tenant-Prüfung eines referenzierten Fremdobjekts vor jedem Grant-Insert (T-15-01), kein Vorbild im Bestandscode
|
|
- [Phase ?]: [15-03]: Kein Import von ModuleRegistryModule in GroupsModule — ModuleGrantsService injiziert ausschließlich PrismaService
|
|
- [Phase ?]: [15-03]: getCatalogFlags liefert Map nur für aktive Module, Controller mappt fehlenden Eintrag auf beide Flags false
|
|
- [Phase ?]: [15-06]: Task 2/3-Split von page.tsx haelt jeden Task-Commit fuer sich buildbar (Task 2 ohne Import der erst in Task 3 entstehenden Komponenten)
|
|
- [Phase ?]: [15-06]: Gruppen-Erstellung mit sofortiger AD-Bindung laeuft zweistufig (POST /groups, dann PATCH ldapDn), weil CreateGroupDto aus 15-02 nur name entgegennimmt
|
|
- [Phase ?]: [15-06]: matrixCheckboxLabel/directCheckboxLabel (Wave-4-Schluessel) als next-intl-ICU-select mit granted-Parameter modelliert, ein Schluessel bedient freigeben/entziehen
|
|
- [Phase ?]: [15-07]: aria-label des Grant-Checkboxes beschreibt die vom Klick ausgeloeste Aktion (granted: String(!isGranted)), nicht den aktuellen Haekchen-Zustand
|
|
- [Phase ?]: [15-07]: UserAccessModal leitet Gruppenmitgliedschafts-Chips ausschliesslich aus der Vereinigung aller viaGroups-Namen von GET /module-grants/users/:userId ab, kein zweiter Endpoint
|
|
- [Phase ?]: [15-07]: ActivateModuleDialog ruft onSuccess bereits nach dem erfolgreichen activate-Call auf, unabhaengig vom Ausgang des nachfolgenden module-grants-Calls
|
|
- [Phase ?]: [15-08]: isAdmin-Gate auf /marketplace und /marketplace/[slug] entfernt (D-08: Katalog bleibt Schaufenster fuer jeden authentifizierten Benutzer) — isAdmin gated jetzt nur noch die Aktivieren/Deaktivieren-Aktion (canManage-Prop)
|
|
- [Phase ?]: [15-08]: MarketplaceCard-Klick delegiert an getrennte onOpenDetail/onLockedClick-Callback-Props statt eigener Router-Logik in der Karte — bleibt praesentational und ueber vi.fn() testbar
|
|
- [Phase ?]: [quick-260805-fok]: ensureDefaultGroup-Waechter prueft ausschliesslich group.count === 0, nie die fehlende isDefault-Markierung (D-13); Reparatur laeuft als zweiter sequenzieller await-Schritt in AdminSeedService.onApplicationBootstrap statt als eigener Hook in GroupsModule (Ordering-Falle wie in tender-scheduler.service.ts)
|
|
- [Phase ?]: Checkpoint 1 (16-01 Task 1): approve-both — Group.internalName + ldapObjectGuid + Unique-Index in einer Migration, freigegeben 2026-08-06
|
|
- [Phase ?]: Migrationsverfahren angepasst: prisma migrate dev verweigert nicht-interaktive Shell — Ersatz via migrate diff + Handdatei + migrate deploy, inkl. Baseline der 24 Altmigrationen per migrate resolve --applied
|
|
- [Phase ?]: [16-02]: reassignDefaultBeforeDelete() nutzt bewusst nicht findOwned() — eigenes still-false-Muster fuer Batch-Sync-Laeufe (D-06), kein NotFoundException-Abbruch
|
|
- [Phase ?]: [16-02]: Namenssperre fuer importierte Gruppen (D-03) ist eine Backend-Invariante in GroupsService.update() (BadRequestException), nicht nur ein UI-Disable
|
|
- [Phase ?]: [16-02]: PERM-02 bleibt in REQUIREMENTS.md bewusst auf [ ] — dieser Plan liefert nur den Backend-Teil, das Requirement schliesst erst mit Plan 16-05
|
|
- [Phase ?]: [16-03]: syncBoundGroupsForTenant() als eigene, in Task 1 noch unverdrahtete Methode gebaut; Task 2 liefert ausschliesslich die Verdrahtung als Schritt 5a vor 5b samt Call-Order-Test — Reihenfolge ist die zentrale Korrektheitsbedingung der Phase
|
|
- [Phase ?]: [16-03]: A1/A2-Live-Pruefung gegen ViCoTest nicht durchfuehrbar (kein erreichbares AD in dieser Sandbox) — als WINDOWS.md #4 (unrun-verify) festgehalten, negatives Ergebnis ist Stopp-Grund fuer die D-05-Loeschsemantik
|
|
- [Phase ?]: [16-03]: PERM-02 bleibt in REQUIREMENTS.md bewusst auf [ ] — schliesst erst mit Plan 16-05
|
|
- [Phase ?]: [16-04]: Group.internalName vorgezogen von Task 2 nach Task 1 (Rule 3) - GroupFormModal.tsx kompiliert sonst nicht
|
|
- [Phase ?]: [16-04]: PERM-02 bleibt in REQUIREMENTS.md bewusst auf [ ] - schliesst erst mit Plan 16-05
|
|
- [Phase ?]: [16-05]: PERM-02 in REQUIREMENTS.md auf [x] gesetzt - alle 5 Phase-16-Erfolgskriterien code-vollstaendig ueber 16-01..16-03; dieser Plan liefert die Sichtbarkeitsschicht (Sync-Bericht D-05/D-06) und die dritte D-04-Anzeigestelle (Freigabe-Matrix)
|
|
- [Phase ?]: [16-05]: A1/A2-Live-Pruefung gegen echtes AD (WINDOWS.md #4) bleibt trotz PERM-02-Abschluss offen - Korrektheitsannahme unter SC-3/SC-4, kein eigenes Erfolgskriterium; negatives Ergebnis waere Stopp-Grund fuer D-05-Loeschsemantik
|
|
- [Phase ?]: [17-01]: Checkpoint 1 (gate=blocking) 'weiter' — beide Datenbank-Umbauten der Phase freigegeben, gestuetzt auf gemessene 0 Bestandszeilen (lokal + alpha)
|
|
- [Phase ?]: [17-01]: TenderEmailConfig.userId @unique ersetzt tenantId @unique (D-01); tenantId bleibt denormalisiert, wird auf create UND update mitgeschrieben
|
|
- [Phase ?]: [17-01]: email-config-Routen von @Roles(ADMIN,SUPER_ADMIN) auf @UseModule('tender-radar') umgestellt — Postfach ist jetzt Nutzereinstellung (D-01, T-17-06 accept)
|
|
- [Phase ?]: [17-01]: eigene Seite /modules/tender-radar/my-sources statt Erweiterung von /settings/general/account (D-01 offener Punkt 4)
|
|
- [Phase ?]: [17-02]: Checkpoint-Freigabe aus 17-01 deckte diese Migration bereits ab, kein zweiter Halt (gemessene 1 Bestandszeile blieb plattformweit)
|
|
- [Phase ?]: [17-02]: Startbestueckung (tenders.module.ts) vorgezogen aus Task 3 nach Task 1 (Rule 3) - find-then-create statt upsert-on-url, Prismas Compound-Unique-Typ verlangt userId als Pflicht-String
|
|
- [Phase ?]: [17-02]: seedServiceBundRssFeed() aus TendersModule.onModuleInit extrahiert (tenders.seed.ts), damit die Bestueckungs-Idempotenz echten Produktivcode testet
|
|
- [Phase ?]: [17-02]: DELETE /rss-feeds/:feedId von @Roles auf @UseModule umgestellt - Besitzpruefung im Dienst ersetzt die Rollenpruefung vollstaendig (T-17-07)
|
|
- [Phase ?]: [17-03]: RssFeedListForm scope-Prop (personal/platform) bedient beide Seiten; Berechtigung entscheidet der Server (T-17-08)
|
|
- [Phase ?]: [17-03]: createRssFeed-Antwort traegt kein isPlatformWide (nur GET mappt es) — Komponente leitet es lokal aus dem verwendeten scope ab (Rule 1)
|
|
- [Phase ?]: [17-03]: Anzeige-Rollenpruefung auf settings/page.tsx ueber useAuthStore (unbekannt/erlaubt/verweigert); verbindliche Pruefung bleibt serverseitig
|
|
- [Phase ?]: [17-03]: REQUIREMENTS.md SRC-01..05 nachtraeglich ergaenzt — Luecke aus 17-01/17-02, dort schon in SUMMARY-Frontmatter gefuehrt
|
|
|
|
### Pending Todos
|
|
|
|
None yet.
|
|
|
|
### Blockers/Concerns
|
|
|
|
- [Roadmap v1.1]: DÖE OpenData API pagination/rate-limit parameters unverified (Swagger UI is JS-rendered) — resolve via a live API call during Phase 10 planning, not assumed from docs.
|
|
- [Roadmap v1.1]: Whether AI-AG NetServer / cosinex VMP search pages require JS rendering is unverified — needs a Phase 13 start-of-phase spike before committing to playwright.
|
|
- Phase 14 Plan 03 (14-03): Task 4 human-verify OPEN — needs a real portal-alert mailbox (incl. Exchange/EWS live path) from the operator before INGEST-05's Exchange path is production-ready. Tasks 1-3 complete and committed (4d6fbb1, 8983231, 1be6b15, 48e1252); API 387/387, web 144/144 green.
|
|
- Phase 15 Plan 06 (15-06): manueller Browser-Durchklick aus dem Plan-Verification-Block nicht ausgefuehrt (kein Browser-Tool in dieser Session) — vor /gsd-ship nachholen, siehe WINDOWS.md unrun-verify #1
|
|
|
|
### Quick Tasks Completed
|
|
|
|
| # | Description | Date | Commit | Directory |
|
|
|---|-------------|------|--------|-----------|
|
|
| 260630-gbh | User Settings: Passwort ändern (nur non-LDAP) + Profilbild setzen | 2026-06-30 | merge | [260630-gbh-user-settings-passwort-ndern-nur-non-lda](.planning/quick/260630-gbh-user-settings-passwort-ndern-nur-non-lda/) |
|
|
| 260701-abc | Fix i18n: marketplace.accessDenied + calendar form hardcoded EN strings | 2026-07-01 | e5b76b7 | [260701-abc-i18n-missing-keys](.planning/quick/260701-abc-i18n-missing-keys/) |
|
|
| 260707-csw | LDAP AD Anbindung: Zugangsdaten aus XWiki vorbefuellen und Import-Filter fuer Benutzer/Gruppen | 2026-07-07 | a5c500d | [260707-csw-ldap-ad-anbindung-zugangsdaten-aus-xwiki](.planning/quick/260707-csw-ldap-ad-anbindung-zugangsdaten-aus-xwiki/) |
|
|
| 260707-lgh | Favoriten-Widget: Icon-Proxy fuer Cross-Origin-Resource-Policy-Seiten (claude.ai) | 2026-07-07 | f06a2ff | [260707-lgh-favoriten-widget-icon-proxy-fuer-cross-o](.planning/quick/260707-lgh-favoriten-widget-icon-proxy-fuer-cross-o/) |
|
|
| 260708-cuc | Fix: FavoriteLink-Tabelle fehlt in Prod-DB, nie als Migration committed (500 auf GET /favorites) | 2026-07-08 | afef9b2 | [260708-cuc-fix-favoritelink-tabelle-fehlt-in-prod-d](.planning/quick/260708-cuc-fix-favoritelink-tabelle-fehlt-in-prod-d/) |
|
|
| 260708-rev | LDAP: CTL-spezifisches AD-Prefill entfernt (Multi-Tenant, "das war nie das Ziel") | 2026-07-08 | 8e8305c | (direct) |
|
|
| 260708-tst | LDAP: Verbindung testen vor dem Speichern einer Config moeglich | 2026-07-08 | 39aa4bf | (direct) |
|
|
| 260709-abd | LDAP: anonymous bind (bindDn/bindPassword optional, Schema nullable + Migration) | 2026-07-09 | 010aceb | (direct) |
|
|
| 260709-ciu | Auth: Benutzernamen ueberall case-insensitive (Login, Seed, LDAP-Sync + Daten-Migration) | 2026-07-09 | baff7ce | (direct) |
|
|
| 260709-lda | LDAP: ldapts empty-array-Attribut-Bug (E-Mail-Kollision auf Unique-Constraint) | 2026-07-09 | 246dc89 | (direct) |
|
|
| 260709-sbx | LDAP: Suchbox fuer die entdeckten Gruppen/OUs-Liste | 2026-07-09 | aaa2922 | (direct) |
|
|
| 260714-lex | LDAP: Per-User Exclude/Denylist-Filter (Service-Accounts vom Sync ausschliessen) — live verifiziert: deaktiviert 4 Accounts, 2 echte User aktiv | 2026-07-14 | 9d1323f | (direct) |
|
|
| 13 | Normalizer-Gap Phase 13 schliessen: NetServer/Cosinex-Bag-Dispatch (TenderNormalizerService) | 2026-07-23 | 9881005 | — |
|
|
| 260728-lih | LDAP: Sync strikt selektiv (leere Auswahl = No-Op statt Voll-Import) + Auto-Sync-Default aus (syncIntervalMin 60→0) | 2026-07-28 | c54e424,57bc7f9,63a07ab | [260728-lih-ldap-sync-selektiv-und-auto-sync-default](.planning/quick/260728-lih-ldap-sync-selektiv-und-auto-sync-default/) |
|
|
| 260729-d3k | LDAP: Multi-Base-DN und Base-DN als Sync-Scope (statt leerer Gruppenfilter = No-Op) | 2026-07-29 | 5cbd530,96be7e1 | [260729-d3k-ldap-multi-base-dn-und-base-dn-als-scope](.planning/quick/260729-d3k-ldap-multi-base-dn-und-base-dn-als-scope/) |
|
|
| 260805-d0r | Benutzer-Detail zeigte Gruppenmitgliedschaften aus Freigaben statt aus Mitgliedschaften — GET /module-grants/users/:userId liefert jetzt { groups, modules }, Chips mit Herkunfts-Badge; im Browser gegengeprüft: Mitgliedschaft bleibt sichtbar, auch wenn die Gruppe kein Modul freigibt | 2026-08-05 | ecadf69,f8ff74b,8ce3748 | [260805-d0r-benutzer-detail-zeigt-gruppenmitgliedsch](.planning/quick/260805-d0r-benutzer-detail-zeigt-gruppenmitgliedsch/) |
|
|
| 260811-enc | LDAP-Bind-Passwort war das einzige Zugangsdatum im Klartext in der DB. Jetzt AES-256-GCM ueber den bestehenden CalendarCryptoService, Spalte umbenannt zu encryptedBindPassword, Entschluesselung zentral in getConfig()/getAllActiveConfigs(), idempotenter Bootstrap-Backfill fuer Altbestand. Falscher Schluessel wirft, statt still "kein Passwort" zu liefern (sonst wuerde aus einem authentifizierten Bind unbemerkt ein anonymer) | 2026-08-11 | 4f687ea | (direct) |
|
|
| 260811-j04 | DOE-Ausschreibungen verlinkten auf die API (rohes JSON) statt auf die Bekanntmachung — betraf Trefferliste, Detailansicht und Alarm-Mails. Adapter baut die URL jetzt aus der Bekanntmachungsnummer, Migration schreibt 2846 bestehende Zeilen in Tender und TenderSource um. Zielseite im Browser fuer beide Kennungsformen verifiziert (numerisch und UUID) — ein HTTP-Statuscheck taugt dort nicht, die Seite antwortet auf jede Kennung mit 200 | 2026-08-11 | ecf7872 | [260811-j04-doe-tender-links-point-to-the-api-instea](.planning/quick/260811-j04-doe-tender-links-point-to-the-api-instea/) |
|
|
| 260811-f9i | KRITISCH: objectGUID-Existenzpruefung fand nie etwas — der Filter wurde als `\xx`-escapter String gebaut, ldapts wandelt das nicht in Rohbytes; beide Suchen (Base-DNs und WR-03-Fallback) teilten ihn, also haette der erste echte Sync JEDE AD-gebundene Gruppe samt Mitgliedschaften und Modulfreigaben geloescht. Jetzt EqualityFilter ueber den rohen Buffer, `escapeLdapFilterBuffer()` entfernt. Read-only am echten AD gemessen (escapter String 0 Treffer, EqualityFilter 1 korrekter Treffer); Regressionstests gegengeprueft (alter Code = 8 rote Tests) | 2026-08-11 | d2019dc | [260811-f9i-fix-objectguid-existence-sweep-to-use-eq](.planning/quick/260811-f9i-fix-objectguid-existence-sweep-to-use-eq/) |
|
|
| 260805-fok | Standardgruppe bei Mandanten-Anlage + Startup-Reparatur — GroupsService.ensureDefaultGroup(tenantId) mit D-13-Waechter (null Gruppen, nicht fehlende Markierung), verdrahtet in TenantService.create und AdminSeedService.ensureDefaultGroupsForAllTenants; schliesst die Migrations-Backfill-Luecke auf frischen Installationen (Testserver: tenants=1 users=4 groups=0) | 2026-08-05 | 9d1254c,0d7d8a5 | [260805-fok-standardgruppe-bei-mandanten-anlage-und-](.planning/quick/260805-fok-standardgruppe-bei-mandanten-anlage-und-/) |
|
|
| 21 | Verschluesselungsschluessel in den Beispiel-Umgebungsdateien dokumentiert: .env.example hatte gar keinen Eintrag, .env.prod.example nannte noch den alten Namen CALENDAR_ENCRYPTION_KEY. Compose-Teil des Backlog-Punkts war bereits mit 7bda56d erledigt (Vorgabewert raus, :?-Abbruch statt Ersatzwert) | 2026-08-11 | 379606e | — |
|
|
|
|
## Deferred Items
|
|
|
|
Items acknowledged and carried forward from previous milestone close:
|
|
|
|
| Category | Item | Status | Deferred At |
|
|
|----------|------|--------|-------------|
|
|
| Live-Test AD | WINDOWS #4 — Annahmen A1 (objectGUID uebersteht Umbenennung) und A2 (byteweise Hex-Filter-Syntax) gegen ein echtes Active Directory pruefen. Negatives Ergebnis waere Stopp-Grund fuer die Loeschsemantik D-05 | offen, bewusst auf das Live-System verschoben | 2026-09-07 |
|
|
| Live-Test AD | WINDOWS #6 — Sync ausloesen und die drei Zahlenzeilen sowie die Amber-Zeile bei verschobener Standardmarkierung pruefen | offen, bewusst auf das Live-System verschoben | 2026-09-07 |
|
|
| Live-Test E-Mail | WINDOWS #12 — echtes Portal-Alert-Postfach ueber Exchange/EWS anbinden und eine echte Alarm-Mail ingestieren; der handgeschriebene NTLM/SOAP-Weg ist bisher nur gegen Mocks geprueft | offen, bewusst auf das Live-System verschoben | 2026-09-07 |
|
|
|
|
**Entscheidung des Users vom 2026-09-07 zur Mandantenfaehigkeit:** Tessera wird
|
|
zunaechst **nur intern** eingesetzt. Die Mandantentrennung ist damit vorerst
|
|
zweitrangig — sie bleibt in der Architektur verankert und wird nicht zurueckgebaut,
|
|
aber ihre Abnahme hat keine Dringlichkeit. Konkret betrifft das den offenen
|
|
Abnahmeplan 02-05 (Phase 2), dessen Erfolgskriterium 4 die Datentrennung zweier
|
|
Mandanten im Browser nachweisen soll: bewusst zurueckgestellt, nicht vergessen. Vor
|
|
einem Einsatz bei externen Kunden ist er nachzuholen.
|
|
|
|
**Entscheidung des Users vom 2026-09-07:** Diese drei Punkte werden spaeter am
|
|
Live-System geprueft, nicht in der Entwicklungsumgebung. Sie bleiben im Ledger
|
|
ausdruecklich `open` und sind NICHT auf `waived` gesetzt — sie sollen nachgeholt
|
|
werden, nur eben dort, wo ein echtes Verzeichnis und ein echtes Postfach erreichbar
|
|
sind. Kein Anlass, sie vorher erneut vorzulegen.
|
|
|
|
## Session Continuity
|
|
|
|
Last session: 2026-09-07T11:50:00.000Z
|
|
Stopped at: Offen: Mandanten-Branding (Backlog), drei Ledger-Punkte fuer den Live-Test.
|
|
Resume file: None
|
|
Last activity: 2026-07-14 - Built LDAP per-user exclude/denylist filter (9d1323f), migration applied on live DB, verified via Playwright: sync deactivated 4 excluded service accounts (administrator/krbtgt/guest/dns-ldap), 2 real LDAP users stay active, 0 wrongly created
|