leistungsbilanz-ts/docs/spec/revit-excel-r0-audit.md

528 lines
35 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# R0 – Audit der Linear-Excel-Anbindung
Stand: 29.09.2026. Geprüfter Code: `cf022b2`.
**Status: R0 abgeschlossen und mit Korrekturen für R1 freigegeben.** Dieser Bericht
ist ein Umsetzungsvorschlag, kein Nachweis einer bereits implementierten
Excel-Anbindung. Der neue Nutzerauftrag ersetzt die CSV-/IfcGUID-Vorgaben und
die alte Phasenliste 14.0–14.6. R1 bereinigt die bestehenden Dokumente; R0
ändert weder Laufzeitcode noch Datenbank noch Oberfläche.
Die 13 Freigabekorrekturen vom 29.09.2026 sind in den
[verbindlichen Anforderungen](revit-excel-integration-requirements.md) und im
[R0–R10-Plan](revit-excel-phase-14-plan.md) eingearbeitet. Bei Abweichungen
gelten diese beiden R1-Dokumente.
## 1. Geprüfter Bestand und Keep/Change/Remove
Gelesene Grundlagen: `AGENTS.md`, `docs/current-architecture.md`,
`docs/project-history-and-external-model-architecture.md`,
`docs/spec/revit-csv-integration-requirements.md` und
`docs/spec/revit-csv-phase-14-audit-and-plan.md`. Der alte Plan widerspricht
sich bereits intern: Sein Status nennt die Erstübernahme noch nicht vorhanden,
während Implementierung und Tests sie nachweisen. Alte Erledigt-Markierungen
werden deshalb nicht übernommen.
| Bereich / Codebeleg | Entscheidung | Begründung und Änderung |
| --- | --- | --- |
| `src/db/repositories/project-command-transaction.persistence.ts` | Keep | Eine Grenze für Fachänderung, Inverse, Revision und History; auch automatische Snapshots bleiben darin. |
| `src/db/repositories/external-model-state.repository.ts`, `src/db/schema/external-*.ts` | Change | Quelle, Batches, Raum-Mappings und Objekte wiederverwenden; neue Identität, Konfiguration und typisierte Matrix. Schlanke Objekt-Reads von großen Batch-Blobs trennen. |
| `src/domain/models/external-initial-import-project-command.model.ts`, zugehöriger Store | Change | Vollständiger erwarteter/Zielzustand und Row-freier Import bleiben; Worksharing-ID, Gerätearten und atomare Regelübernahme ergänzen. |
| `src/external-model/application/external-initial-import-plan.ts`, `external-initial-import-target.ts` | Change | Raumvorschläge, Kataloge, vollständige Entscheidungen und stabile UUIDs behalten. Objektentscheidungen und Gerätearten ergänzen. |
| `src/db/repositories/external-object-assignment.persistence.ts` und drei Zuordnungs-Stores | Change | Mengeninvariante und exakte Inversen behalten; Geräteart, effektiven Verteiler, Anwesenheit und Übernahme der Row-Werte beim Lösen ergänzen. |
| `CircuitDeviceRow.manualQuantity`, bestehende Mengenprüfungen | Keep | `quantity = manualQuantity + Summe(externer effektiver Mengen)`; Mehrfachsteckdosen bleiben unteilbar. |
| `src/external-model/application/external-circuit-list-objects.ts` | Change | Listen-/Projektprüfung behalten; vollständige Planungswerte, Geräteart, effektiver Verteiler, Status und Row-Anzahlen liefern. |
| `src/domain/models/project-state-snapshot.model.ts`, Snapshot-Persistenz und Transfer | Change | Schema 10; historische CSV-Zustände ausdrücklich ablehnen; alle neuen internen Referenzen remappen. |
| `src/external-model/csv/*`, CSV-Preview, CSV-Validierungen und Controller | Remove/replace | XLSX ersetzt CSV vollständig; kein zweiter aktiver Transportpfad. |
| `src/frontend/components/revit-csv-modal.tsx`, Einbindung auf Projektseite | Remove/replace | Eigene Revit-Seite; Einstellungen nur im Modal, Import in eigenem Wizard. |
| `src/frontend/components/Sidebar.tsx`, `src/frontend/utils/api.ts`, `src/frontend/types.ts` | Change | Route `revit`, neue DTOs und Endpunkte, CSV-Imports entfernen. |
| `src/frontend/components/circuit-tree-editor.tsx`, Grid-Drop-/Projektionsmodule | Change | Separater externer Drag-Payload; Row-, Circuit- und Freiplatz-Ziele bewusst unterscheiden. Bestehende Grid-Bedienung behalten. |
| Raum-/ProjectDevice-Formulare und Commands | Keep | Sichtbar vorbelegte, ausdrücklich bestätigte Anlage als eigener Undo-Schritt. |
| `scripts/verify-revit-reference-csv.ts`, Transporttests | Replace | Pfadargument für lokalen XLSX-Prüfer; ausschließlich synthetische Workbooks in Tests. |
### Konkrete Lücken gegenüber dem Auftrag
- `external-model-contracts.ts` importiert heute CSV-Verträge. Die neutralen
Konfigurations-/Matrixverträge gehören künftig in `external-model/domain`;
nur `external-model/xlsx` darf ExcelJS kennen.
- `ifc_guid` ist derzeit NOT NULL und je Quelle eindeutig; Matching,
Snapshots, DTOs und Commands setzen diese Identität voraus.
- `internalDeviceType` ist derzeit ein freier String in Planungswerten.
Es gibt keinen Geräteart-Katalog mit stabilen IDs und Standardwerten.
- Der Erstimport setzt den ProjectDevice-Link, verwendet im Target aber
weiterhin `suggestedPlanningValues`. Die neue Priorität ProjectDevice vor
Geräteart muss ausdrücklich implementiert werden; ein Link allein genügt
nicht. Revit-Leistung wird künftig nicht automatisch zum Planungswert.
- Der Import-Target setzt `distributionBoardId = null`; die Raumvorgabe liegt
separat im Mapping. Projektion und Zuordnungsprüfung vergleichen heute
direkt das Objektfeld. Eine zentrale Auflösung des effektiven Verteilers
fehlt und ist vor der Editor-Nutzung erforderlich.
- Die bisherige Row-Zuordnung darf laut Validator am Objekt ausschließlich
`circuitDeviceRowId` ändern; der Store schreibt auch nur dieses Feld.
Lösen mit aktuellen Row-Planungswerten benötigt eine neue Command-Version.
- Die Kompatibilitätsprüfung vergleicht Raum, Kategorie, Auswahlkenner und
Planungswerte, aber keine stabile Geräteart. Unterschiedliche Gerätearten
dürfen nicht versehentlich in dieselbe Row aggregiert werden.
- Rows speichern `powerPerUnit` in **kW**, externe Planung
`powerPerUnitW` in **W**. Eine zentrale bidirektionale Konvertierung ist
nötig; Exportleistung darf nicht um Faktor 1000 falsch sein.
- Externe FKs auf Row, ProjectDevice, Raum-Mapping und Verteiler verwenden
teilweise `ON DELETE SET NULL`. Bestehende allgemeine Löschpfade sichern
externe Objektübergänge nicht durchgängig in ihren Inversen. Auch
Unterbaum-/History-Löschungen müssen abgesichert werden.
- Ein Editor-Drawer, projektweite Objekttabelle, Exportnachweis und
ausführbarer Folgeimport fehlen. `follow_up` als vorhandener Enumwert ist
kein implementierter Folgeimport.
- Konfigurationsversionen steigen heute nur bei normalen Updates gegenüber
dem aktuellen Wert; Undo setzt alte Versionen zurück. Für eine tatsächlich
monotone Generation muss diese Semantik korrigiert werden.
- XLSX-Parsing ist asynchron. Das heutige erneute synchrone CSV-Parsing
innerhalb der SQLite-Transaktion darf nicht einfach durch `await` ersetzt
werden.
## 2. Prüfung der lokalen Musterdatei
`docs/spec/kbp-Stromkreis.xlsx` wurde ausschließlich lesend auf ZIP-/OOXML-
Ebene untersucht; keine Inhalte wurden hochgeladen oder in Fixtures kopiert.
Die Datei ist unversioniert und darf nicht committed werden.
| Befund | Ergebnis |
| --- | --- |
| Dateigröße / entpackte ZIP-Einträge | 56.765 / 337.740 Bytes |
| Arbeitsblätter / Blattnamenlänge | 1 / 31 Zeichen |
| Zellbereich | A1:O490 |
| Kopfzeile / Leerzeile / Objekte / Abschluss | 1 / 2 / 3–489 / 490 |
| Objektidentitäten | 487 syntaktisch passende Worksharing-IDs, keine Duplikate |
| Kopfzeilenkommentare | 14, zu den 14 Spalten nach der ID-Spalte |
| Fehlende Raumangaben | 67 Objekte ohne Raumnummer und Raumname |
| IfcGUID-Duplikate | Keine bei exaktem, case-sensitivem Vergleich |
| Formeln | Keine; synthetische Tests dafür erforderlich |
| Spaltendefinitionen | 16, obwohl nur 15 Datenspalten vorhanden sind |
Das beobachtete Profil aus dem Auftrag wird damit bestätigt. Zusätzliche
Spaltenformatdefinitionen dürfen beim Export nicht auf die gemappten Spalten
gekürzt werden. Das Muster enthält die neuen getrennten Stromkreisfelder und
die Bezeichnung noch nicht. Fehlende Rückgabespalten werden angezeigt; sie
dürfen nicht ohne Linear-Parameterbindung erfunden werden.
Die frühere Angabe von zwei IfcGUID-Duplikatgruppen war falsch: PowerShells
standardmäßiges Gruppieren verglich ohne Beachtung der Groß-/Kleinschreibung.
Ein erneuter ordinaler, case-sensitiver Vergleich ergab keine Duplikate. Die
Summenzeile hat nur in B490 einen Wert; `Familie und Typ` ist leer.
Nicht nachgewiesen: erfolgreicher Rückimport in Linear, dessen genaue
Auswertung der Kommentar-/VML-Metadaten und der ExcelJS-Roundtrip. R2 endet
deshalb mit einer aus der Musterdatei erzeugten Rückgabedatei und einem
verbindlichen Linear-Test durch den Nutzer vor R3.
## 3. Bibliothek, Adapter und Uploadgrenzen
**Vorschlag: genau eine neue direkte Abhängigkeit `exceljs`.** Seine
öffentliche API unterstützt XLSX, Zellnotizen, Formate, Spaltenbreiten und
typisierte Werte. Das passt zum beobachteten klassischen Kommentarformat.
Version und transitive Abhängigkeiten werden in R2 festgeschrieben und
geprüft. Die veröffentlichte Version ist laut Paketregister 4.4.0; eine
aktuelle Releasepflege ist damit nicht bewiesen. Dies ist ein ausdrückliches
Wartungsrisiko, kein als erfüllt abgehakter Bibliotheksnachweis.
Quellen, am 29.09.2026 geprüft:
[ExcelJS-Projekt](https://github.com/exceljs/exceljs),
[offizielle Paketdokumentation](https://www.npmjs.com/package/exceljs).
Keine Bibliothek wird in R0 installiert. R2 muss vor Integration Kommentar-
Roundtrip, Node-24-Verträglichkeit und Abhängigkeitsrisiken prüfen; bei einem
Blocker wird die Bibliotheksentscheidung neu vorgelegt.
Vorgeschlagene Grenzen, zentral konfiguriert und synthetisch getestet:
- ausschließlich `.xlsx`, kein `.xls` oder `.xlsm`;
- maximal 10 MiB Upload;
- genau ein Arbeitsblatt und keine Formelausführung;
- feste Zeilen-, Spalten- und Zellgrenzen nach dem Einlesen.
Es gibt keinen eigenen ZIP-Prüfer und keinen Parser-Worker.
Neutrale Matrix: Blattname, Kopfzeilen-/Startpositionen, Spaltenmetadaten
(Index, Name, Kommentartext, erkannter Parametertyp/Einheit), geordnete
Zeilen mit Originalzeilennummer und Klasse sowie Zellen als diskriminierte
Union `string | number | boolean | empty`. Formeltext und Cache-Status sind
optionale Transportmetadaten; fachlich zählt nur der gecachte skalare Wert.
Fehlender Cache oder Fehlerwert erzeugt einen Hinweis bzw. bei Pflichtfeldern
einen Blocker. Originalbytes bleiben unverändert gespeichert.
Eine Zeile ohne Worksharing-ID ist nur verdächtig, wenn `Familie und Typ`
gefüllt ist. Sie erzeugt einen Hinweis und wird übersprungen, ohne zu
blockieren. Andere ID-lose Zeilen, auch die Summenzeile mit Inhalt in B,
werden still übersprungen. Nicht passende nichtleere IDs blockieren.
Worksharing-ID und IfcGUID bleiben als Quelltext erhalten und werden exakt,
ordinal und case-sensitiv verglichen; ungültige äußere Leerzeichen werden
gemeldet statt still korrigiert. IfcGUID wird optional gespeichert, doppelte
Werte sind nur Hinweise.
Numerische Projektion rundet Leistung auf 0,01 W, nicht die gespeicherte
Quellzelle. Mengen aus `socketCount` müssen positive ganze Zahlen sein.
Rückgabe lädt eine Kopie der Originalbytes, bewahrt den Bereich oberhalb der
ersten Datenzeile samt Kommentaren, Formatierung und Spaltenbreiten und
ersetzt den Datenbereich durch gewählte Objektzeilen in Originalreihenfolge.
Alle Spalten bleiben enthalten; Summenzeilen entfallen. Unveränderte Zellen
behalten ihre importierten Werte und Typen. Formeln in Objektzeilen werden
als gecachte Werte ausgegeben, damit Zeilenauswahl keine falschen relativen
Bezüge erzeugt; dies wird vor Export als Hinweis ausgewiesen. Keine Formeln
werden berechnet. Roundtrip bedeutet strukturelle/semantische Gleichheit,
nicht identische ZIP-Bytes.
## 4. Konfigurationsschema
Neuer neutraler Vertrag `ExternalModelConfiguration`, Schema-Version 1:
```ts
{
schemaVersion: 1,
layout: {
headerRow: 1, firstDataRow: 3, firstDataColumn: 1
},
circuitMode: "combined" | "separate",
columns: {
worksharingId: string, familyAndType: string,
ifcGuid: string | null, roomNumber: string | null,
roomName: string | null, selectionMarker: string | null,
socketCount: string | null, sourceRemark: string | null,
circuitCombined: string | null, boardName: string | null,
circuitIdentifier: string | null, designation: string | null,
electricalPowerW: string | null, voltageV: string | null,
currentA: string | null
},
deviceTypes: [{
id: UUID, name: string,
category: "lighting" | "single_phase" | "three_phase",
defaults: {
powerPerUnitW: number | null, simultaneityFactor: number | null,
cosPhi: number | null, costGroup: string | null
}
}],
familyTypeRules: [{
id: UUID, exactFamilyAndType: string, deviceTypeId: UUID,
quantityRule: "one" | "socketCount"
}]
}
```
Zeilen/Spalten sind intern 1-basiert; UI stellt Spalten als A, B, … dar.
Es gibt nur `columns.worksharingId` und kein separates `layout.idColumn`.
Jede gemappte Kopfzeile muss eindeutig auflösbar sein;
doppelte Ausgabebelegungen werden abgelehnt. Freie Eingabe bleibt zulässig,
aber ein nicht existierender Name erzeugt vor Import/Export einen konkreten
Fehler. Erkennung und Vorbelegung nutzen die exakten Standardnamen aus dem
Auftrag. Unbekannte Spalten bleiben ohne Mapping erhalten.
Geräteart-IDs sind interne UUIDs, keine Namen als Relationsschlüssel. Regeln
sind exakt und je Familien-/Typtext eindeutig; jede importierte Familie muss
eine Geräteart haben. Gerätearten und Regeln werden als ein versioniertes
Konfigurationsaggregat gespeichert. Keine zusätzliche Gerätearttabelle ist
für diesen Umfang nötig; Validatoren erzwingen referenzielle Integrität.
Referenzierte Gerätearten können nicht kommentarlos entfernt werden.
Startwerte: je Feld ProjectDevice vor Geräteart vor leer. Anzeigename:
Auswahlkenner, sonst Geräteartname, sonst Familie/Typ. Quellleistung bleibt
Quelle; eine Übernahme geschieht nur als sichtbare Planungsentscheidung.
Fehlender GZF wird sichtbar mit `1` vorbelegt, ohne zusätzliche Bestätigung.
Geräteart-
Änderungen schreiben keine vorhandenen Objekt-Overrides oder Rows um.
## 5. Konkretes Persistenzschema
Bestehende UUID-/Projekt-FKs und Eigentumsprüfungen werden beibehalten.
`JSON` bezeichnet validierten JSON-Text in SQLite, nicht ExcelJS-Objekte.
| Tabelle | Zielspalten / Constraints |
| --- | --- |
| `external_model_configurations` (ersetzt CSV-Tabelle) | `id TEXT PK`, `project_id TEXT NOT NULL UNIQUE FK projects`, `configuration JSON NOT NULL`. Enthält obiges vollständiges Aggregat; keine eigene Versionszählung. |
| `external_model_sources` | `id`, `project_id UNIQUE`, `name`, `source_type = revit_xlsx`. Genau eine implizite Quelle je Projekt. |
| `external_import_batches` | Letzter Batch vollständig mit IDs, Importart, Zeit, Dateiname, SHA-256, Objektzahl, `applied_project_revision`, `configuration_snapshot JSON`, `original_bytes BLOB`, `document JSON`; ältere Batches nur als Metadaten Datum, Dateiname, SHA-256 und Objektzahl. |
| `external_room_mappings` | Vorhandene Felder einschließlich normalisiertem Quellraumschlüssel, `room_id`, `default_distribution_board_id`; UNIQUE `(source_id, normalized_source_room_key)` bleibt. |
| `external_model_objects` | Bestehende interne IDs/Links; neu `worksharing_id TEXT NOT NULL`, UNIQUE `(source_id, worksharing_id)`; `ifc_guid TEXT NULL`, ohne Unique; `device_type_id TEXT NOT NULL` als validierter Verweis in das Konfigurationsaggregat. |
| Objektzustand | `accepted_source_values JSON` mit rohen typisierten Werten für Raum, Familie/Typ, Marker, Menge, Bemerkung und alle Rückgabefelder; `planning_values JSON` mit Anzeigename, Kategorie, Anschlussart, effektiver Menge, Einzelleistung W, nullable GZF/cosPhi/Kostengruppe/Bemerkung; `overridden_fields JSON`; bisherige Batch-/Raum-/Board-/ProjectDevice-/Row-Verweise und `presence_status` bleiben. |
| `external_export_batches` (neu) | `id TEXT PK`, `project_id FK`, `project_revision INTEGER`, `import_batch_id TEXT`, `exported_at_iso TEXT`, `file_name TEXT`, `sha256 TEXT`, `configuration_snapshot/hash`, `scope TEXT`, `object_count INTEGER`. Append-only Audit-Store. |
`import_batch_id` des Exportnachweises ist absichtlich eine historische UUID
ohne löschenden FK: Undo eines Imports oder Snapshot-Restore darf den Nachweis
nicht entfernen. Exportnachweise liegen außerhalb rücksetzbarer Fachsnapshots
und werden beim Projektduplikat nicht als dort erfolgte Exporte ausgegeben.
Sie bleiben beim Originalprojekt erhalten; diese Abgrenzung wird in R1
dokumentiert. Nur der letzte Importbatch gehört mit Bytes und Matrix zu
Snapshot/Transfer; ältere Batches werden dort nur als Metadaten erhalten.
Indices: bisherige Projekt-/Presence-, Board- und Row-Indices behalten;
zusätzlich `(project_id, device_type_id)`, `external_room_mapping_id`,
`linked_project_device_id`, Export `(project_id, exported_at_iso)`.
Cross-Project-Links und Geräteart-Verweise prüft jeder Command in der
Transaktion; bloße Existenz eines UUID-FK genügt nicht.
Effektiver Verteiler: ein gemeinsamer Resolver verwendet bei verknüpften
Objekten die Row/Circuit-Verteilung, sonst die Objektabweichung, sonst die
Raumvorgabe. `distributionBoardId = null` bedeutet Raumvorgabe verwenden;
bei fehlender Vorgabe ist das Objekt ohne Verteiler. Eine Änderung des
Raum-Standardverteilers betrifft nur unzugeordnete Objekte; verknüpfte Objekte
folgen immer der Row-Verteilung und blockieren diese Änderung nicht.
Planungswerte bleiben von Quellwerten getrennt. Nach Row-Zuordnung sind
Objekt-Planungswerte eingefrorene Startwerte; Anzeige, Abweichung und Export
verwenden die Row. Beim Lösen werden aktuelle Row-Werte einschließlich
Einheitenkonvertierung in die Objektplanung übernommen und als explizite
Overrides markiert. Effektive Objektmenge wird nicht durch die Row-Gesamtmenge
ersetzt. Die Row und ihr manueller Anteil bleiben erhalten.
## 6. Commands und Transaktionsgrenze
Alle öffentlichen Fach-Writes verlangen `expectedRevision`, Anmeldung und
`X-Leistungsbilanz-Request: 1`. `expectedRevision` gehört wie bisher zum
Ausführungsumschlag. Jede Transition enthält vollständige erwartete/Ziel-
Snapshots der betroffenen Datensätze, keine ungesicherten Teilpatches.
| Command | Payload-Skizze und Gegenrichtung |
| --- | --- |
| `external-model-configuration.update` v1 | `{ expected, target }` einschließlich Layout, Mapping, Gerätearten und Regeln. Inverse tauscht den vollständigen Fachinhalt; keine eigene Konfigurationsversion. |
| `external-import.apply-initial` v2 | `{ expectedConfiguration, targetConfiguration, expected, target }`. Quelle, Batch, Mapping, Objekte und neue Regeln zusammen; Inverse stellt leeren externen und vorherigen Konfigurationsinhalt wieder her. Keine Rows. |
| `external-import.apply-follow-up` v1 | `{ configuration: transition, batch: transition, roomMappings: transitions, objects: transitions, rows: quantityTransitions, decisions }`; Server erzeugt deterministischen Diff und alle UUIDs. Inverse tauscht alle betroffenen Zustände. |
| `external-model-object.update-planning` v1 | `{ objects: transitions }`; nur ungebundene Objekte; geänderte Felder in Overrides. Einzel-/Sammelbearbeitung identischer Regeln. |
| `external-model-object.update-distribution-board` v1 | `{ objects: transitions }`; explizite Objektabweichung, Projekt-/Row-Konsistenz. |
| `external-model-room-mapping.update` v1 | `{ mappings: transitions }`; interner Raum/Standardverteiler; betroffene Objekt-/Row-Konsistenz mitprüfen. |
| `external-model-object.update-project-device-link` v1 | `{ objects: transitions, confirmedPlanningFields }`; keine Vorlagenänderung; neue Startwerte nur für ausdrücklich bestätigte Felder. Gebundene Objekte werden über ihre Row bearbeitet. |
| `external-object.update-row-assignment` v2 | `{ rows: transitions, objects: transitions, confirmedConflictObjectIds }`; Linkwechsel, materialisierte Mengen, beim Lösen aktuelle Row-Planungswerte; vollständige Inverse. |
| `external-object.assign-to-new-row` v2 | Bestehender vollständiger Row-/Objekt-/Reserveplan mit neuen Objektverträgen; history-only Inverse entfernt nur exakt unveränderte Erzeugnisse. |
| `external-object.assign-to-new-circuit` v2 | Bestehender Circuit-/Protection-/Row-/Objektplan; BMK einmal planen und festschreiben, gleiche UUIDs bei Redo. |
| `external-model-object.delete-missing` v1 | `{ objects: transitionsToNull, rows: quantityTransitions }`; ausschließlich ausdrücklich bestätigte weiterhin fehlende Objekte, kein Row-/Circuit-Löschen; Inverse stellt IDs, Links und Mengen wieder her. |
Bestehende `project-device.insert`, Room-/Floor-Commands bleiben eigenständige
Undo-Schritte. Familien-/Geräteartentscheidungen im Wizard sind lokale
Entwürfe und werden erst mit dem Import atomar persistiert. Nach separater
Kataloganlage aktualisiert der Wizard die Projektrevision und plant neu,
ohne lokale Entscheidungen zu verlieren.
Die Konfiguration hat keine eigene Versionszählung. Vorschau und Apply binden
sich an `expectedRevision`; weil auch Undo/Redo neue, strikt steigende
Projektrevisionen erzeugen, wird ein veralteter Plan eindeutig ungültig.
Importbatches speichern eine vollständige Konfigurationskopie. Prüfen:
Änderung → Undo → anderer Zweig darf niemals denselben Plan wieder gültig
machen.
XLSX wird vor der synchronen SQLite-Transaktion in einer serverinternen
Vorbereitung geparst. Apply überträgt Bytes erneut; Hash, Matrix und
Entscheidungen werden erneut validiert. Im Store werden Revision,
Konfigurationsinhalt, erwartete Objekte, Links und vollständiger
vorbereiteter Plan atomar geprüft. Vorbereitete Daten sind keine vom Client
behauptete Validierung: Generic-Command-Requests dürfen Importcommands nicht
an dieser serverseitigen Vorbereitung vorbeischleusen. Undo/Redo lesen und
parsen keine Excel-Datei; sie wenden ausschließlich die gespeicherten
Vorher-/Nachher-Zustände an. Keine asynchronen Callbacks in
`better-sqlite3`-Transaktionen.
Row-/Circuit-/Gruppen-Löschcommands erfassen verknüpfte Objekte vollständig.
Nach der Warnung `X Revit-Objekte werden wieder unzugeordnet` lösen sie die
Links im selben Command; ihre Inverse stellt sie exakt wieder her. Sperren
bleiben nur für das Löschen referenzierter Verteiler und Räume und gelten auch
in Unterbaum- und History-only-Pfaden.
## 7. Migration, Snapshot 10, History und Transfer
Neue additive Migration nach `0013_users` (voraussichtlich `0014`, Nummer
beim Beginn von R3 erneut prüfen). Bestehende SQL-/Metadateien unverändert.
1. Vor R3-Anwendung API-Schreibzugriffe stoppen und `npm run db:backup`
ausführen; verifiziertes Backup samt Wiederherstellungsmöglichkeit sichern.
R0 verändert keine DB und benötigt daher kein Migrationsbackup.
2. Vorhandene externe CSV-Entwicklungsdaten dürfen gemäß Auftrag verworfen
werden. Zuerst je verknüpfter Row `quantity = manual_quantity` setzen;
sonst bliebe nach Entfernen der externen Objekte eine Phantomlast.
Rows, Circuits, manuelle Anteile, UUIDs und BMKs bleiben bestehen.
3. Externe Objekte → Mappings → Batches → Quellen → CSV-Konfiguration in
FK-sicherer Reihenfolge entfernen. Tabellen gezielt neu aufbauen, wo
SQLite NOT-NULL-/Unique-Änderungen dies erfordern. Neue Zieltabellen und
Indices erstellen. Keine leeren Worksharing-IDs als Backfill erfinden.
4. Generiertes SQL und Drizzle-Metadaten prüfen; saubere und bestehende
Test-DBs migrieren; FK-/Integritätsprüfung und Mengenabgleich ausführen.
Der Reset ist eine dokumentierte einmalige Entwicklungsmigration,
kein stiller Laufzeit-Import.
5. Alte Revisionsdaten nicht umschreiben, keine CSV-History heimlich
überspringen. An der Ausführungsgrenze alte CSV-Konfigurationscommands,
Initial-/Zuordnungscommands v1 einschließlich ihrer History-Inversen und
Restores mit CSV-Inhalt vor jedem Write ablehnen.
Fehlermeldung für History:
„Dieser Verlaufsschritt verwendet die frühere CSV-/IfcGUID-Anbindung und
kann mit der Excel-Anbindung nicht ausgeführt werden.“
Der History-Status sollte den blockierten obersten Schritt samt Grund zeigen;
keine automatisch geleerten Stacks und keine Änderung bei Ablehnung. Frühere
allgemeine Commands können nach dem Reset zusätzlich an ihren exakten
Vorbedingungen scheitern; das bleibt eine sichere, sichtbare Ablehnung.
Snapshot v10 enthält aktive Excel-Konfiguration, Gerätearten/Regeln, Quelle,
den letzten Batch mit Originalbytes/Matrix, ältere Batches nur als Metadaten
sowie Raum-Mappings, Objekte und Links.
Upgrade v1–9: bestehende nicht-externe Upgrader erhalten. Nur wenn
`externalCsvConfiguration` fehlt/null ist **und** der externe Zustand leer
ist, verlustfrei auf v10 heben. Auch eine gespeicherte CSV-Konfiguration
allein zählt als nicht leerer CSV-Zustand und wird nicht still verworfen.
Snapshot-Checksumme am gespeicherten Original vor Upgrade prüfen.
Fehlermeldung:
„Dieser Projektstand enthält Daten der früheren CSV-/IfcGUID-Anbindung.
Eine automatische Übernahme in die Excel-Anbindung ist nicht möglich.
Bitte verwenden Sie einen Projektstand ohne CSV-Daten.“
Der aktuelle portable Transfer ist bereits **Format 2** (Grobbilanzen),
nicht Format 1 wie im alten Audit. Envelope 2 und dessen Format-1-Lesepfad
bleiben; enthaltenes Projektzustandsschema wird 10. Duplizieren remappt
Projekt-, Konfigurations-, Geräteart-, Regel-, Quellen-, Batch-, Mapping-,
Objekt-UUIDs und alle internen Links, auch in Konfigurationssnapshots der
Batches. Worksharing-IDs, optionale IfcGUIDs und Originalbytes bleiben
unverändert. Grobbilanzen und globale Office-/Auth-Daten behalten ihre
bisherige Abgrenzung. Exportnachweise werden nicht als neue Projektexporte
dupliziert.
## 8. Read-Modelle, Oberfläche und Rückgabe
Vorgeschlagene API-Basis `/api/projects/:projectId/revit`:
`configuration` (GET/PUT), `profile/preview` (POST, zustandslos),
`initial-import/plan|apply`, `follow-up-import/plan|apply` (POST),
`status` und `objects` (GET), `return-workbook` (POST mit Export-Audit).
Der bestehende listenbezogene External-Objects-GET bleibt erhalten.
Objektmutationen verwenden den vorhandenen Project-Command-Endpunkt mit
neuen typisierten Verträgen. CSV-Endpunkte werden entfernt, keine Aliasse.
Export verwendet `expectedRevision` für einen konsistent gelesenen Stand;
die Datei-Erzeugung erfolgt außerhalb des kurzen Read-Snapshots. Danach
separater append-only Audit-Write, ohne Projektrevision oder Undo-Schritt.
Bei Auditfehler kein Erfolg/download; ein Eintrag belegt Dateierzeugung,
nicht einen tatsächlich abgeschlossenen Browserdownload.
Die Revit-Seite lädt einmal eine kompakte Objektliste. Filterung und Sortierung
laufen im Client; die Tabelle wird virtualisiert und verwendet keine Server-
Pagination. Filter-/Sortierzustand, UUID-Auswahl und Sammelaktionen sind
unabhängig vom DOM. „Alle gefilterten“ wird vor einer Mutation zu einer
expliziten ID-Auswahl an der aktuellen Revision aufgelöst. Große Auswahlmengen
werden nicht in getrennte Undo-Schritte aufgeteilt. Kein vollständiges
Batch-Blob im DTO.
Einstellungen verwenden Musterdatei-Erkennung mit editierbaren Positionen,
Feld-Dropdowns/freier Eingabe und Kommentar-Typ-/Einheitenanzeige. Regeln
entstehen im Wizard und sind auf der Seite einklappbar nachbearbeitbar.
Fehlende Raumangaben bleiben zulässig, Board-Zuordnung ist auch ohne Raum
möglich. ProjectDevice-Zuordnung bleibt optional.
Rechter Editor-Drawer: nur vorhandene, ungebundene konfliktfreie Objekte des
effektiven Verteilers. Gruppierung nach internem Raum, Auswahlkenner,
Geräteart-ID, Kategorie und identischen Planungswerten; unterschiedliche
ProjectDevice-Links bleiben ebenfalls getrennt, da eine Row nur einen Link
haben kann. Effektive Einzelmengen sind kein Gleichheitsfeld: Einfach- und
Mehrfachsteckdosen sollen gemeinsam gruppierbar sein. Anzeige enthält
Mengensumme und Objektanzahl. Fachlich heterogene Auswahl darf nicht
willkürlich auf eine einzelne Row zusammenfallen; Konfliktanzeige verlangt
eine kompatible Auswahl. Row-Indikator öffnet Objekte und explizites Lösen.
Eine Funktion `getRevitBoardName(board)` liefert ausschließlich `board.name`.
Gemeinsam: Name + unverändertes `equipmentIdentifier`, ohne Separator.
Getrennt: Name und BMK in eigene Parameter. Ohne Circuit bleiben beide
Stromkreiswerte leer, auch wenn ein Planungsverteiler bekannt ist.
Exportprojektion und Abweichungsanzeige teilen dieselbe reine Funktion:
Row-Anzeigename/-Leistung bei Link, sonst Objektplanung; Objektleistung in W
= effektive Menge × Einzelleistung (Row-kW vorher × 1000), ohne GZF.
Spannung vom Circuit, sonst Projektstandard passend zur Kategorie.
Strom aus Objektleistung, U, cosPhi und Phasenart; Drehstrom mit Faktor √3,
auf 0,01 A. Fehlende/ungültige Werte ergeben leer plus Hinweis. Nullleistung
bleibt bei gültigem Nenner numerisch 0. Kein still angenommenes cosPhi.
Abweichungsvergleich normalisiert Zahlpräzision, Leere und skalare Typen
gegen den **zuletzt importierten**, nicht den zuletzt exportierten Stand.
Umfang: alle im letzten Batch vorhandenen Objekte / abweichende / explizite
Auswahl. Fehlende Objekte haben keine aktuelle Originalzeile und werden
nicht künstlich wieder eingefügt. Alle Spalten bleiben, nicht ausgegebene
Parameter behalten importierte Werte. Fehlende optionale Spalten für
Bezeichnung, Leistung, Spannung und Strom werden mit Hinweis übersprungen.
Nur fehlende Stromkreisspalten des gewählten Modus blockieren. Dateiname gemäß
Auftrag mit bereinigtem Projektnamen und Tagesdatum.
Folgeimport matcht ausschließlich Worksharing-ID. Ein neuer Batch speichert
immer den vollständigen letzten Quellstand. Unveränderte Objekte werden
nicht allein zum Fortschreiben von `lastSeenImportBatchId` neu geschrieben;
aktuelle Sichtbarkeit/Quellwerte kommen aus Batch/Matrix. Akzeptierte
Planungsgrundlage und letzte importierte Beobachtung sind getrennt.
Raum-/Familien-/Marker-/Mengenänderungen brauchen Entscheidungen. Bei
unvereinbarer Row-Zuordnung blockiert die Übernahme bis zum ausdrücklichen
Lösen/Umordnen, statt Rows automatisch zu erzeugen. Übernommene Mengen
ändern alle betroffenen Row-Summen im selben Importcommand. Fehlend setzt
nur den Status, erhält vorerst Link und Menge. Rückgabefeld-Abweichungen
sind Hinweise, keine Übernahmeentscheidungen. Ein völlig unveränderter
Folgeimport erzeugt einen nachvollziehbaren Batch, aber keine unnötigen
Objekt-/Row-Updates.
## 9. Pakete, Verifikation und Risiken
R0 liefert nur diesen Bericht und einen eigenen Commit. R1–R10 starten erst
nach Freigabe; keine Fortschreibung der alten 14.0–14.6-Liste.
| Paket | Arbeit und entscheidende Abnahme |
| --- | --- |
| R1 | Neue Excel-Anforderungen und R0–R10-Plan; alte Dokumente archivieren/auf Verweise kürzen; AGENTS, Architektur, History-Doku, Phase-14-Abschnitt, LLM-Kontext und alle Links nachziehen. Referenzdatei gezielt ignorieren. |
| R2 | XLSX-Adapter und lokaler Referenzprüfer mit Pfadargument. Synthetischer Roundtrip für Kommentare, Blattname, Spaltenbreiten, Typen, Header-/Startpositionen, unbekannte Spalten, Auswahl und Summenausschluss. Aus der lokalen Musterdatei entsteht eine Rückgabedatei mit geänderten Werten. Danach Nutzerabnahme in Linear; R3 bleibt bis zum Erfolg gesperrt. Bei Fehlschlag vor R3 OOXML-Direktänderung als Alternative vorlegen. Keine DB-Änderung. |
| R3 | Neue Konfiguration/Identität, additive Migration, Snapshot 10, Transfer und History-Ablehnungen; Backend-CSV entfernen. Parser-Vorbereitung von synchroner Transaktion trennen. |
| R4 | Revit-Seite, Navigation, Einstellungen; Projektseitenmodal und dessen CSV-Frontend-Verträge entfernen. |
| R5 | Erstimport mit atomaren Regeln, Geräten, Raum-/Board- und Objektentscheidungen; ProjectDevice-Vorbelegung nach Priorität; ein Import-Undo-Schritt. |
| R6 | Kompakte Objekt-Reads, clientseitige Filter/Sortierung, Virtualisierung, Sammel-Commands und Overrides. |
| R7 | Gruppierung, Drag-and-drop, Row-Indikatoren und Lösen inklusive aktueller Row-Werte; drei Drop-Ziele, atomare Objektlösung beim Row-/Circuit-/Gruppen-Löschen und Reload/Undo/Redo. |
| R8 | Drei Exportumfänge, gemeinsame Abweichungsprojektion, typisierte Rückgabe, append-only Nachweis und Linear-Strukturabnahme. |
| R9 | Kontrollierter Folgeimport, fehlende Objekte, explizites Löschen, mengenwirksame Entscheidungen und atomare Inverse. |
| R10 | Vollständige synthetische Ende-zu-Ende-Kette einschließlich Neustart, Snapshot/Restore und Duplikat; mehrere Tausend Objekte; Doku auf ausgelieferten Stand. |
Die temporäre Grenze zwischen R3 und R4 wird ausdrücklich behandelt:
Buildfähige, nicht aktive Kompatibilitätstypen für das alte Modal dürfen bis
R4 bestehen, aber kein aktiver CSV-Backendpfad. Veraltete Projektaktion bis
zur neuen Seite deaktivieren/entfernen, keinen funktionslosen Upload anbieten.
Die Dokumentation darf Zwischenstände nicht als fertigen Workflow ausweisen.
Pflicht je R1–R10: fokussierte Tests, `npm test`, `npm run build:api`,
`npm run build:web`, `npm run typecheck:scripts`,
`npx tsc --noEmit -p tsconfig.next.json`, je ein knapper imperativer Commit.
R0: 31 vorhandene Tests aus sechs Dateien erfolgreich ausgeführt:
`external-model-foundation`, `external-initial-import-plan`,
`external-initial-import-project-command.repository`,
`external-object-row-assignment-project-command.repository`,
`external-circuit-list-objects`,
`external-csv-configuration-project-command.repository` (je `.test.ts`).
Diese decken vorhandene Commit-/Rollback-, Undo-/Redo-, Snapshot-/Transfer-
Grundlagen ab, nicht die neue Excel-Funktion. Der erste Start scheiterte an
Sandbox-`spawn EPERM`; derselbe Lauf außerhalb der Sandbox bestand.
Vollständige Builds/Tests sind für diesen reinen R0-Bericht nicht ausgeführt.
Zusätzlich erforderliche neue Prüfungen:
- Migration mit externen Row-Anteilen; keine Phantomlast und unveränderte BMKs.
- v1–9 ohne CSV verlustfrei, mit CSV klare Ablehnung; alte Command-Inversen
und alte Restore-Payloads ebenfalls ohne Teilwrites ablehnen.
- Config-Änderung/Undo/Neuzweig invalidiert alte Pläne über `expectedRevision`; Apply mit anderem
Dateiinhalt, fehlenden Entscheidungen oder fremden UUIDs rollt vollständig
zurück. Später Historyfehler rollt auch Regeln, Links und Mengen zurück.
- Gebundene Row ändern → Export → Lösen → Undo/Redo behält fachliche Werte.
- Mehrfachsteckdosen: unterschiedliche effektive Mengen, identische
Einzelwerte, richtige Gruppierung und objektweise Rückgabeleistung.
- Row-/Circuit-/Gruppen-Löschen löst externe Links im Command und stellt sie
per Undo wieder her. Raum-/Verteilerlöschen mit Referenzen blockiert.
- Export-Audit scheitert: keine fachliche Revision; SHA-256 entspricht exakt
den gelieferten Bytes. Snapshot/Restore entfernt keine Exportnachweise.
- 5.000/10.000 synthetische Objekte: Parserzeit, Peak-Speicher, Payloadgröße,
Command-/Snapshot-Dauer und UI-Reaktionszeit messen; nur sichtbare Zeilen im
DOM, stabile Auswahl über virtuelle Bereiche.
Wesentliche Restrisiken: Linear-Kommentarbindung ist nur anhand des
Exports beobachtet; ExcelJS-Releasepflege und Roundtrip sind in R2 zu
belegen. Große Originaldateien vervielfachen sich in History/Snapshots;
Limits und Messungen müssen reale Speichergrößen berücksichtigen. Die
CSV-Entwicklungsbereinigung ist absichtlich verlustbehaftet, aber auf den
externen Zustand begrenzt und nur nach verifiziertem Backup zulässig.
Fehlende Zielparameter im Muster verlangen einen neuen vollständigen
Linear-Export, keine geratenen GUID-Kommentare.
**Freigabepunkt:** R0 wurde am 29.09.2026 mit den in R1 dokumentierten
Korrekturen freigegeben. R2 besitzt einen neuen verbindlichen Freigabepunkt:
Vor Beginn von R3 muss der Nutzer die in R2 erzeugte Rückgabedatei erfolgreich
in Linear getestet haben.