Skip to content

Changelog

Gilt ab Work7 0.2.1 · Stand 16.09.2026

Alle relevanten Aenderungen an Work7 werden hier dokumentiert. Format: Keep a Changelog · Versionierung nach SemVer

[Unreleased]

Added

  • Postman-Suite Public API v1 (Sept. 2026): src/postman/work7-public-api.postman_collection.json mit Umgebungen Test und Prod (work7-public-api.<test|prod>.postman_environment.json) und Runner src/postman/run-public-api.mjs (newman per npx, JUnit nach src/test-results/). 36 Requests: /me, Auth-Negativtests, Listen/Filter/Detail/404 für Projekte, Kontakte, Anfragen, Rapporte, Angebote, Rechnungen, Zeiten, Termine, Datenablage; Scope- und Modul-Erwartungen (403 insufficient_scope/module_disabled) werden aus /me abgeleitet, schreibende Requests nur mit allow_writes=true (Prod-Umgebung: aus). npm-Skripte api:test, api:prod. Anleitung: src/postman/README.md, Runbook docs/runbooks/runbook-public-api-tests.md.
  • Doku-Assistent (Sept. 2026): Schwebender Chat-Button „Frag die Doku" auf docs.work7.net. Nutzer stellen Fragen zur Dokumentation, der Assistent (gpt-5-mini) formuliert Suchanfragen und beantwortet sie ausschließlich aus den indexierten Inhalten (Handbuch, Public API, Features) mit Quellenangaben (Seite › Abschnitt, verlinkt). Lexikalische Suche über MiniSearch, kein Vector-DB. Rate-Limits pro IP (20/10 min) und global (3000/Tag). Nutzer-Daten werden nicht gespeichert (KI-Hinweis in der UI). Neue Node-Server src/docs-site/server/index.mjs statt nginx (Hono, SSE-Events), Index-Build bei jedem Docs-Build. Env: AZURE_OPENAI_*, DOCS_CHAT_*. Doku: 22-doku-assistent.
  • Datenablage (int.inbound, Sept. 2026): Endpunkte POST /api/v1/inbound (Upload raw bytes beliebigen Content-Types mit source, optional filename/batch/part, max. 2048 MB) und GET /api/v1/inbound (Aufzeichnung mit Pagination und source-Filter). Scopes int.inbound:read, int.inbound:write. Speicherung mandantengetrennt in Azure Blob (Container work7-inbound), gestreamt ohne RAM-Buffering. Doku: datenablage.
  • Plattform-Admin (Migration 114, Sept. 2026): Flag users.is_platform_admin für das Betreiberkonto. Nur der Plattform-Admin wirkt über alle Betriebe (Admin-Dashboard, Kunden anlegen, Modul-Freischaltung). Siehe 01-auth-onboarding-rbac.
  • Admin-Dashboard – Kunden anlegen & Session-Wechsel (Sept. 2026): Plattform-Admin kann im Dashboard Betriebe anlegen (Name, Kürzel, Plan, Land, Admin-E-Mail/Name). Admin-Konto wird inaktiv angelegt (kein Passwort). „Betriebe"-Panel zeigt alle Betriebe mit Plan/Nutzerzahl; „Zugang senden" schickt Einladungs-E-Mail mit 7-Tage-gültigem Link (kopierbar falls Mail misslingt), „Einrichten" wechselt Plattform-Admin-Session in den fremden Betrieb zur Vorkonfiguration (Banner zeigt Betrieb an, Audit-Log beide Seiten, Rücksprung automatisch). Alte „Neuen Betrieb einladen"-Panel entfernt; Endpunkt /api/admin/access-invite bleibt für Kompatibilität.
  • Ablage-Sync Phase 1 — Ordner-Register: Migration 115 ergänzt storage_folders (Entität ↔ Ordner-ID beim Anbieter), storage_sync_log und document_storage_mappings.folder_id. putTenantObject(target: { entity, id, category }) löst Zielordner über das Register auf (resolveUploadTarget/FolderResolver), übernimmt Altordner nach bisherigem Schema, legt fehlende mit folderNameFor an (Projekt {Nummer} {Name}, Kategorien deutsch, anbietertaugliche Namen, Kollisionen (2)). Anbieter-Operationen ensureFolder/findChild/getFolder/renameFolder/moveFolder/listChildren für SharePoint, Google Drive, Dropbox, Nextcloud; fetchWithRetry beachtet 429/503 mit Retry-After. Umgestellt: Anfrage-Dokumente, Mail-Anhänge (app + landing), Abnahme-Fotos, WhatsApp-Projektbilder, Materialimport. Projektanlage legt Projektordner samt Unterordnern best-effort an (In-Process-Listener addDomainEventListener in App und Agent-Worker). Originaldateinamen mit Umlauten (Konflikt (2)), WhatsApp-Bilder als WhatsApp {Datum Uhrzeit} {Absender}.jpg; WhatsApp-Zwischenablage liegt in Azure Blob statt im Firmenspeicher. Azure Blob nutzt ID-Pfade ohne Register. Skript scripts/storage-folders-backfill.ts --company … [--dry-run], Runbook docs/runbooks/runbook-ablage-backfill.md.
  • Ablage-Sync Phase 2 — ereignisgetriebener Sync: BullMQ-Queue storage-sync (Job-ID {company}:{entity}:{id}, 3 s Verzögerung, 5 Versuche mit Backoff), eingereiht in publishDomainEvent über setStorageSyncEnqueuer (App + Agent-Worker, nur Mandanten mit OAuth-Anbieter), Consumer im Agent-Worker; ohne Redis Rückfall auf Sync im Prozess. Reine Planung planFolderOps für alle Regeln aus Plan §5 (Umbenennen, Kundenwechsel, Archiv/Gelöscht/Reaktivieren, _Ohne Kunde, Anfrage angelegt/gelöscht/gewonnen mit Umzug der Dateien ins Projekt), Executor syncEntityFolder mit Session-Advisory-Lock je Mandant und kurzer Transaktion je Operation (Wiederanlauf nach DB-Fehler ohne verwaiste Referenzen), Register-/Referenz-Umschreibung (Dropbox/Nextcloud inkl. Pfadspalten der Fachtabellen), nach letztem Versuch state='error'. Übernommene Altordner werden beim ersten Lauf auf D1/D5 umbenannt. FolderOps um deleteFolder und moveFolder(…, newName) ergänzt; In-Process-Listener aus Phase 1 entfernt. updateDocumentStorageMappingLocation filtert jetzt nach company_id.
  • Ablage-Sync Phase 2, Nachbesserungen: D4-Wiederanlauf (Datei schon verschoben → nur Referenz nachziehen, verwaiste Referenzen vor dem Entfernen reparieren), idempotentes Entfernen, Einreihen erst nach COMMIT (afterCommit in withTransaction), kein verworfenes Event mehr (weiterer Nachlauf, Soll-Fingerabdruck am Laufende), Zeitbudget je Job und lock_timeout/statement_timeout auf der Sync-Verbindung.
  • Ablage-Sync Phase 3 — Abgleich und Status: Cron POST /api/cron/storage-reconcile?mode=light|deep (Kubernetes-CronJobs im Chart, abschaltbar über cronJobs in den Values, zusammen mit webhook-out); planReconcile (Soll gegen Register über gespeicherten Soll-Fingerabdruck, Migration 116) und tiefer Abgleich über SharePoint root/delta bzw. Drive changes.list mit Cursor, Dropbox/Nextcloud in Tranchen; externe Änderungen nach D6 (Name/Ort wiederherstellen, gelöschte Ordner neu anlegen, Dateien als „nicht auffindbar“ markieren: document_storage_mappings.missing_since). bootstrapTenantWorkspace legt Grundordner über das Register an und startet den tiefen Abgleich; metadata.workspaceBootstrap entfällt. Status-Ansicht in Einstellungen › Integrationen › Dateispeicher (GET /api/settings/storage/status, POST /api/settings/storage/reconcile), Admin-Benachrichtigung bei Fehlerzustand > 24 h (höchstens täglich). D9: Aufbewahrung der WhatsApp-Zwischenablage (7 Tage zugeordnet, Rückfrage nach 23 Tagen, Löschung frühestens nach 30 Tagen und 7 Tage nach Rückfrage; agent_media.retention_notified_at/purged_at).
  • Ablage-Sync Phase 3, Nachbesserungen: D9 markiert Medien nur bei zugestellter Rückfrage (atomar, bei Versandfehler zurückgenommen; unbekannter Absender → Admins), löscht nie ohne zugestellte Rückfrage; Abgleich und Aufbewahrung laufen unter einer DB-Laufsperre (acquireReconcileLease), die auch „Jetzt abgleichen“ nutzt (409 während des nächtlichen Laufs); Drive-Cursor bei ungültigem Token neu; Änderungsabfrage auf 20 Seiten je Lauf begrenzt. Status-Ansicht: Datum/Uhrzeit sauber formatiert, deutsche Namen der obersten Ordner (Kunden, Anfragen, Zwischenablage, Abwesenheiten, Import), Tabelle scrollt horizontal.
  • Ablage-Sync Phase 4 — Bestandsbereinigung: Skript scripts/storage-cleanup.ts --company … [--apply] [--json] (Standard Probelauf, ein Mandant, unter Mandanten-Sperre): Altordner aus früheren Umbenennungen werden nur bei eindeutiger Zuordnung (über Datei-Referenzen) in den registrierten Ordner zusammengeführt, Namenskonflikte mit „ (2)“, Referenzen/Mappings nachgezogen (Dropbox/Nextcloud über den Pfad); leere projects/-Zwischenebenen und leere Altordner werden entfernt, fremde Dateien und Ordner nie. Bericht vorher/nachher, jede Aktion in storage_sync_log (manual). Runbook-Ablauf in docs/runbooks/runbook-ablage-backfill.md.

Changed

  • SharePoint auf App-Berechtigungen umgestellt (Sept. 2026): Der Dateispeicher SharePoint nutzt statt delegiertem PKCE-OAuth jetzt Microsoft-Graph-Application-Permissions (Sites.Selected, Client Credentials gegen {loginBaseUrl}/{tenantId}/oauth2/v2.0/token, Scope …/.default, MICROSOFT_CLIENT_ID/MICROSOFT_CLIENT_SECRET wie Mail/Kalender). Die Tenant-ID kommt aus der bestehenden Microsoft-365-Verbindung (sync_connections.token_state->>'microsoftTenantId'), kein Eingabefeld. Kein OAuth-Redirect mehr: neue Route POST /api/settings/storage/sharepoint/connect (Site-URL, optional Site-ID) löst die Site auf, prüft den Schreibzugriff und speichert sharepointSiteUrl/sharepointSiteId; connect/sharepoint/start|callback lehnt mit klarem Fehler ab. Der OneDrive-Modus entfällt — eine Team-Site ist Pflicht. App-Token je Tenant im Speicher (Ablauf minus 120 s), keine SharePoint-Tokens mehr in der Datenbank (tenant_storage_oauth_credentials hält nur noch Site-URL/Site-ID; Spalte ist bereits nullable, keine Migration nötig). Unterscheidbare Fehler: Site nicht freigegeben (401/403), Site nicht gefunden (404), Microsoft 365 nicht verbunden. Die Oberfläche zeigt den fertigen PowerShell-Freigabe-Befehl (Connect-MgGraph + New-MgSitePermission) zum Kopieren, GET /api/settings/storage/setup liefert dafür die Client-ID (nie das Secret). Google Drive, Dropbox und Nextcloud bleiben unverändert beim delegierten PKCE-Flow. Anleitung: microsoft-365-einrichten (gemeinsame Seite für Postfach, Kalender und SharePoint inkl. Berechtigungen und PowerShell-Freigabe).
  • API-Schlüssel tragen die Umgebung (Sept. 2026): Neue Schlüssel heißen w7_test_<prefix>_<secret> auf Test/Dev und w7_live_… auf Prod (vorher überall w7_live_). Die Marke liefert apiKeyEnvMarker() (packages/domain/src/modules/api-keys.ts) aus WORK7_ENV (je Umgebung in den Values gesetzt); fehlt sie, wird sie aus NEXT_PUBLIC_APP_URL/NEXTAUTH_URL abgeleitet. parseApiToken vergleicht die Marke bewusst nicht — bestehende Schlüssel bleiben gültig, die Trennung leisten weiterhin die getrennten Datenbanken. GET /api/company/api-keys liefert keyEnv, die Oberfläche zeigt damit das echte Präfix statt immer w7_live_.
  • Microsoft 365 verbinden (Einstellungen › Integrationen › Kalender & E-Mail): Nur noch ein Knopf „Bei Microsoft anmelden“ (Admin-Consent). Nach dem Rückruf läuft POST /api/microsoft/connection-test automatisch und legt die Verbindung an; Tenant-ID-Feld, Probe-User-Anzeige, „Provider speichern“ und „Verbindung testen“ entfallen. Bei aktiver Verbindung: Status „Mit Microsoft 365 verbunden“ und „Erneut anmelden“.
  • Admin-Dashboard je Betrieb: Administratoren (Rolle 1) sehen und verwalten nur Benutzer des eigenen Betriebs; die Regel „mindestens ein Admin“ gilt je Betrieb.
  • Registrierung: Der erste Benutzer eines neu registrierten Betriebs ist Admin (Rolle 1) statt Manager. Registrierungseinladungen verfallen nach 14 Tagen.

Fixed

  • Ablage-Sync Phase 0 (Plan docs/plans/plan-ablage-sync.md): Abnahme-Fotos bei SharePoint/Google Drive/Dropbox/Nextcloud wieder ladbar (Vorschau und PDF) — acceptance_media speichert storage_provider/storage_external_id (Migration 115, Backfill aus document_storage_mappings), Download-Route und Report-Resolver (app + landing) laden über StorageRef/downloadStorageRef; Altzeilen ohne Anbieter werden über resolveStorageRef aufgelöst. Uploads nutzen für gleichnamige Kunden dieselben eindeutigen Ordner wie der Bootstrap (-2, id-Reihenfolge); WhatsApp-Bilder und Abnahme-Fotos zu Projekten ohne Kunden landen unter customers/_Ohne Kunde/{projekt} statt im Wurzelordner.

Security

  • Mandantentrennung in mehreren Verwaltungs-, Einstellungs- und Terminfunktionen verschärft; ein nicht mehr benötigter Diagnose-Endpunkt wurde entfernt.

[0.2.1] - 2026-09-12

Changed

  • Agent-Effizienz (Plan docs/plans/plan-agent-effizienz.md): System-Prompt cache-freundlich sortiert (statische Regeln zuerst, wechselnder Kontext unter „AKTUELLER KONTEXT:“ am Ende); Tool-Schemas ohne Validierungs-Ballast (compactSchemaForLlm, rund 12 % weniger Tool-Tokens); chart.render verweist auf die Prompt-Regel statt sie zu wiederholen; jeder Modellaufruf loggt llm_usage mit cached_tokens. Verhalten unverändert, alle Agent-Tests grün.

[0.2.0] - 2026-09-12

Erste versionierte Ausgabe nach der Architektur-Konsolidierung. Enthält alles unten Gelistete (Modularisierung, Public API & Webhooks, Doku-Site docs.work7.net, Dashboard-Pins, WhatsApp-Menü-Rework, Freitext-Regeln).

Added

  • Modularisierung (Phasen 0–3, Sept. 2026): Modul-Entitlements-Schicht mit zentralem Manifest (packages/domain/src/modules/manifest.ts). Module sind nun Runtime-Freischaltungen (nicht Package-Split): jeder Mandant hat aktive Module (Fachmodule, Kanäle, Integrationen). Guards in Domain (requireModule), Middleware, Agent-Registry und Cron-Jobs erzwingen zentrale Kontrolle. Migrationen 111 (company_modules) + 112 (api_keys/webhooks); Entitlements-Cache mit Redis+PubSub (≤ 60 s Propagierung). CLI scripts/set-company-modules.ts + Admin-UI /admin/module zum Freischalten. Siehe Plan und Runbook.
  • Public API & Webhooks (Phase 0): API-Keys mit HMAC-Validierung, ausgehende Webhooks pro Modul (Abrechnung Phase 4 folgt). Endpunkte /api/v1/{me,projects,kontakte,rapports,invoices,offers,time-entries,appointments,anfragen}. Doku: api-public-v1.
  • Agent-Module-Filter: Agent-Registry filtert Tools nach Rolle ∧ Modul; Prompt-Blöcke je freigeschaltetem Modul. Test packages/agent/test/registry-modules.test.ts prüft Tool-Manifest-Zuordnung. Siehe Pflegeregel.
  • Zwei-Container-Deployment: work7-landing (Marketing) und work7-app (SaaS + API) mit host-basiertem Ingress-Routing.
  • WORK7_SURFACE-Runtime (landing | app) und Middleware-Guards in src/lib/site-surface.ts.
  • App-Login unter Root / auf app.work7.net (Legacy /auth/signin redirectet).

Changed

  • Primaere Domain auf work7.net: Landing work7.net / www.work7.net, App app.work7.net (Test: test.work7.net, app.test.work7.net).
  • Helm Chart: Dual-Deployment/Services, Ingress pro Host + Component; CI baut/pusht beide Images.
  • Env/Callbacks (NEXTAUTH_URL, CORS_ORIGIN, DOCUSIGN_REDIRECT_URI) auf App-Subdomain.
  • Domain-Regeltests fuer site-surface.ts.
  • Modul-Guard-Verhalten (Phase 1): Gesperrtes Modul → API 403 module_disabled, Agent zeigt klare Ablehnung, Web-Links/Sidebar gefiltert. Keine Daten-Löschen beim Abschalten (Reaktivierung stellt alles her).

Fixed

  • Microsoft-Calendar-/Mail-Routen nutzen einen dedizierten Microsoft-Session-Helper statt des Google-Helpers.
  • calendar/sync/[provider]/webhook vertraut company_id nicht mehr ohne verifizierte Signatur.
  • Agent-Mail-Absender: Worker sendet jetzt über Firmen-Postfach statt no-reply (Microsoft-Pfad in domain-Paket, Fallback auf System). Gmail-Worker-Versand bleibt offen (fehlender Service-Account).

Releases

  • 0.2.1 (2026-09-12): Agent-Effizienz (Prompt-Cache, kompakte Tool-Schemas), Tag v0.2.1.
  • 0.2.0 (2026-09-12): erste versionierte Ausgabe, Tag v0.2.0.

Work7 · Software für Handwerksbetriebe · Doku-Stand Version 0.2.1 (Build fb6311f-dirty)