Gedankenstriche durch Bindestriche ersetzt
Prüfung / Lint, Typen, Tests (push) Canceled after 0s
Prüfung / Images bauen (pdf) (push) Canceled after 0s
Prüfung / Images bauen (web) (push) Canceled after 0s
Prüfung / Images bauen (worker) (push) Canceled after 0s
Images / bauen (pdf, docker/pdf.Dockerfile) (push) Canceled after 0s
Images / bauen (web, docker/web.Dockerfile) (push) Canceled after 0s
Images / bauen (worker, docker/worker.Dockerfile) (push) Canceled after 0s

375 Vorkommen in 210 Dateien. Betrifft Kommentare, Oberflaechentexte und die
README; an keiner Stelle Bedeutung, nur Zeichensetzung.
This commit is contained in:
Harlekin
2026-09-04 08:54:52 +02:00
parent 38be773636
commit e0b03ea336
210 changed files with 368 additions and 368 deletions
@@ -211,7 +211,7 @@ $$ LANGUAGE sql STABLE;
-- Wie bei case_refresh_search_vector: mit IF verzweigt, nicht mit einem
-- CASE-Ausdruck. Der würde auch im nicht genommenen Zweig verlangen, dass
-- NEW."documentId" existiert — auf "Document" gibt es das Feld nicht.
-- NEW."documentId" existiert - auf "Document" gibt es das Feld nicht.
CREATE OR REPLACE FUNCTION document_refresh_search_vector() RETURNS trigger AS $$
DECLARE
target text;
@@ -41,7 +41,7 @@ ALTER TABLE "SavedView" ADD CONSTRAINT "SavedView_userId_fkey"
-- Schwärzungen stufenweise auflösen
--
-- `strip_redactions` entfernt jede markierte Stelle. Für den Vektor einer
-- Stufe muss aber stehen bleiben, was diese Stufe lesen darf — sonst findet
-- Stufe muss aber stehen bleiben, was diese Stufe lesen darf - sonst findet
-- ein Leser der Stufe 4 einen Namen nicht, den er auf der Seite sieht.
-- ===========================================================================
@@ -1,6 +1,6 @@
-- Phase 9: Ladewerte messen.
--
-- Nur Zahlen und das Routenmuster. Keine Kennungen, keine konkreten Pfade —
-- Nur Zahlen und das Routenmuster. Keine Kennungen, keine konkreten Pfade -
-- die Messung soll sagen, ob die Anwendung schnell ist, nicht wer wann welche
-- Akte geöffnet hat. Siehe docs/09-performance-und-ux.md.
@@ -2,7 +2,7 @@
--
-- Eine persönliche Sprungliste, kein Verlauf: je Nutzer und Datensatz genau
-- eine Zeile, die beim nächsten Öffnen überschrieben wird. Der Primärschlüssel
-- erzwingt das — es gibt keine Form, in der diese Tabelle sagen könnte, wie oft
-- erzwingt das - es gibt keine Form, in der diese Tabelle sagen könnte, wie oft
-- oder in welcher Reihenfolge jemand etwas geöffnet hat.
--
-- Wer wann was gelesen hat, beantwortet weiterhin allein das Zugriffsprotokoll
@@ -9,7 +9,7 @@
-- Zeitpunkt dieser Migration stand in keinem Kanal eine Nachricht.
-- Die Nachrichtentabelle ist gegen DELETE gesperrt. Für das einmalige
-- Aufräumen wird die Regel abgenommen und danach wieder gesetzt — sie ist die
-- Aufräumen wird die Regel abgenommen und danach wieder gesetzt - sie ist die
-- Zusicherung, dass niemand im Betrieb Nachrichten verschwinden lässt.
DROP RULE chat_no_delete ON "ChatMessage";
@@ -2,7 +2,7 @@
--
-- Damit wird eine Lücke sichtbar, die es schon vorher gab: ein Beteiligter darf
-- die Akte lesen, gleich in welcher Rolle. Bei „Betroffener" fiel das nicht auf,
-- bei „Täter" wäre es offensichtlich falsch — der Beschuldigte läse die
-- bei „Täter" wäre es offensichtlich falsch - der Beschuldigte läse die
-- Ermittlung gegen sich selbst mit.
--
-- Die Prüfung in lib/access/caseFilter.ts unterscheidet deshalb ab jetzt
@@ -1,7 +1,7 @@
-- Abteilungen: die Ebene über der Personalakte.
--
-- Bisher stand die Abteilung als Freitext in der Personalakte. Damit liess sich
-- niemand fragen, was es zu einer Abteilung insgesamt gibt — jede Schreibweise
-- niemand fragen, was es zu einer Abteilung insgesamt gibt - jede Schreibweise
-- war eine eigene Abteilung. Als eigenes Modell ist sie verwaltbar, und die
-- Zuordnung einer Fallakte ergibt sich aus ihren Beteiligten und deren
-- Personalakten. Eine Fallakte trägt deshalb **keine** Abteilung: die
@@ -28,7 +28,7 @@ CREATE INDEX "Department_isArchived_name_idx" ON "Department"("isArchived", "nam
ALTER TABLE "PersonnelFile" ADD COLUMN "departmentId" TEXT;
-- Was schon als Freitext dastand, wird zur Abteilung. Der Schlüssel ist der
-- getrimmte Text in Grossbuchstaben, der Name der Text selbst — beides lässt
-- getrimmte Text in Grossbuchstaben, der Name der Text selbst - beides lässt
-- sich im Adminbereich nachziehen.
INSERT INTO "Department" ("id", "key", "name", "updatedAt")
SELECT gen_random_uuid()::text,
@@ -125,7 +125,7 @@ CREATE INDEX "PersonnelFile_departmentId_idx" ON "PersonnelFile"("departmentId")
-- Abteilung als dritter Besitzer wäre eine halbe Sache: die Vorlagen kennen
-- über TemplateUse nur die beiden Orte, und ein Feld, das nichts füllen kann,
-- ist schlimmer als keins. Was zu einer Abteilung an Dokumenten gehört, wird
-- wie alles andere abgeleitet — über ihre Mitglieder und deren Fälle.
-- wie alles andere abgeleitet - über ihre Mitglieder und deren Fälle.
-- Der Chat bekommt einen dritten Ort. Der CHECK aus der vorigen Migration wird
-- entsprechend erweitert: genau einer, weiterhin.
@@ -161,7 +161,7 @@ $$ LANGUAGE plpgsql;
-- Dieselben Zusicherungen wie beim Verlauf einer Akte (harte Regel 2): kein
-- DELETE, jede Änderung erzeugt eine Version, jede Zurücknahme lässt einen
-- Grabstein stehen. Die Regeln stehen in der Datenbank und nicht nur in der
-- Anwendung — die Anwendung kann man umgehen.
-- Anwendung - die Anwendung kann man umgehen.
-- ===========================================================================
CREATE TABLE "DepartmentEntry" (
@@ -216,7 +216,7 @@ CREATE RULE dept_entry_version_no_delete
AS ON DELETE TO "DepartmentEntryVersion" DO INSTEAD NOTHING;
-- Autor, Entstehungszeit und Abteilung sind unantastbar. Wer etwas anderes
-- sagen will, zieht zurück und schreibt neu — und genau das ist dann sichtbar.
-- sagen will, zieht zurück und schreibt neu - und genau das ist dann sichtbar.
CREATE OR REPLACE FUNCTION dept_entry_immutable_guard() RETURNS trigger AS $$
BEGIN
IF NEW."authorId" IS DISTINCT FROM OLD."authorId"
@@ -1,7 +1,7 @@
-- Beteiligte in den Suchindex der Fallakte.
--
-- Wer nach einem Namen sucht, will die Akten finden, in denen die Person
-- vorkommt — auch dann, wenn sie weder im Titel steht noch im Text erwähnt
-- vorkommt - auch dann, wenn sie weder im Titel steht noch im Text erwähnt
-- wird, sondern nur als Zeuge oder Betroffener eingetragen ist.
--
-- Das verrät nichts Neues: der Name eines Beteiligten steht ohnehin in der
@@ -74,7 +74,7 @@ END;
$$ LANGUAGE plpgsql;
-- Der Verteiler kennt zwei neue Anlässe: eine geänderte Beteiligung, und einen
-- geänderten Namen. Beim Namen sind alle Akten der Person betroffen — sonst
-- geänderten Namen. Beim Namen sind alle Akten der Person betroffen - sonst
-- fände die Suche nach einer Umbenennung weiter den alten Namen.
CREATE OR REPLACE FUNCTION search_index_trigger() RETURNS trigger AS $$
DECLARE
+12 -12
View File
@@ -1,4 +1,4 @@
// AfiS-Aktensystem — Datenmodell
// AfiS-Aktensystem - Datenmodell
// Fachliche Grundlage: docs/04-datenmodell.md
//
// Konventionen:
@@ -113,7 +113,7 @@ model RoleMapping {
}
enum RoleCategory {
/// "AfiS", "Personalabteilung" — regelt, wer sich anmelden darf
/// "AfiS", "Personalabteilung" - regelt, wer sich anmelden darf
MEMBERSHIP
/// Dienstgrade, regeln die Rechte
RANK
@@ -202,9 +202,9 @@ enum TemplateUse {
/// Bauform des Dokuments, siehe docs/13-entscheidungen.md.
enum DocumentLayout {
/// beschriftete Felder, ausgefuellt — der Vorfallsbericht
/// beschriftete Felder, ausgefuellt - der Vorfallsbericht
FORM
/// fester Wortlaut mit Platzhaltern, mehrseitig — der Sonderarbeitsvertrag
/// fester Wortlaut mit Platzhaltern, mehrseitig - der Sonderarbeitsvertrag
PROSE
/// ein einzelnes Freitextfeld, dargestellt wie ein Vermerk
NOTE
@@ -415,7 +415,7 @@ model CaseParticipant {
/// Zwei Gruppen, und der Unterschied entscheidet ueber den Zugriff: LEAD,
/// INVESTIGATOR und OBSERVER **arbeiten** an der Akte und duerfen sie deshalb
/// lesen. WITNESS, SUBJECT, SUSPECT und ACCOMPLICE sind Angaben **ueber** den
/// Fall und verschaffen keinen Zugang — sonst laese der Beschuldigte die
/// Fall und verschaffen keinen Zugang - sonst laese der Beschuldigte die
/// Ermittlung gegen sich selbst mit. Siehe lib/cases/terms.ts.
enum ParticipantRole {
LEAD
@@ -467,8 +467,8 @@ enum LinkKind {
// Personalakten
// ---------------------------------------------------------------------------
/// Akte zu einer Person. Ueberwiegend zu **Externen** — Sicherheitsdienst,
/// Zeugen, Vertragspartner — die kein Discord-Konto haben. `userId` bleibt
/// Akte zu einer Person. Ueberwiegend zu **Externen** - Sicherheitsdienst,
/// Zeugen, Vertragspartner - die kein Discord-Konto haben. `userId` bleibt
/// deshalb der Ausnahmefall, nicht die Regel.
model PersonnelFile {
id String @id @default(uuid(7))
@@ -512,7 +512,7 @@ model PersonnelFile {
}
/// Auswahl der Status-Etiketten, im Adminbereich pflegbar. Die Namen sind
/// Daten, keine Oberflaechentexte — siehe die Sprachkonvention: Code englisch, Oberflaeche deutsch.
/// Daten, keine Oberflaechentexte - siehe die Sprachkonvention: Code englisch, Oberflaeche deutsch.
model PersonnelStatusTag {
key String @id // "tot", "gekuendigt"
name String // "Tot", "Gekuendigt"
@@ -554,7 +554,7 @@ enum AssignmentRole {
}
/// Die Freigabestufen der Foundation, wie sie im Kopf eines Dokuments stehen.
/// **Sie steuern keinen Zugriff** — das tut allein ClearanceLevelConfig.
/// **Sie steuern keinen Zugriff** - das tut allein ClearanceLevelConfig.
/// Siehe docs/13-entscheidungen.md, "Zwei Freigaben im Kopf".
model FoundationClearance {
level Int @id // 1..5
@@ -849,7 +849,7 @@ model NotificationOutbox {
///
/// Fast jede Person gehoert einer an, und die Abteilung ist die Ebene ueber
/// der Personalakte: was zu ihr aktenkundig ist, ergibt sich aus ihren
/// Mitgliedern. Deshalb traegt eine Fallakte **keine** Abteilung — die
/// Mitgliedern. Deshalb traegt eine Fallakte **keine** Abteilung - die
/// Zuordnung entsteht ueber die Beteiligten und deren Personalakten und ist
/// damit immer aktuell. Siehe docs/13-entscheidungen.md.
model Department {
@@ -881,7 +881,7 @@ model Department {
/// Dieselbe Zusicherung wie beim Verlauf einer Akte, in schmalerer Form: es
/// gibt kein DELETE, jede Aenderung erzeugt eine Version, jede Zuruecknahme
/// laesst einen Grabstein stehen. Was fehlt, sind die Teile, die nur eine
/// Ermittlung braucht — Korrekturkette, Anhaenge, Karenzzeit. Ein
/// Ermittlung braucht - Korrekturkette, Anhaenge, Karenzzeit. Ein
/// Abteilungsvermerk ist ein laufendes Protokoll, kein Beweismittel.
model DepartmentEntry {
id String @id // clientseitig erzeugt, UUIDv7
@@ -926,7 +926,7 @@ model DepartmentEntryVersion {
/// Der Vektor zur Stufe N enthaelt alles, was ein Leser dieser Stufe sehen
/// darf: Kopfdaten, Abschnitte und Eintraege bis einschliesslich N, und aus
/// dem Fliesstext die geschwaerzten Stellen bis N. Eine Suche nimmt genau
/// eine Zeile — die zur Stufe des Suchenden — und kann deshalb nichts finden,
/// eine Zeile - die zur Stufe des Suchenden - und kann deshalb nichts finden,
/// was er nicht lesen darf.
///
/// Der Vorgaenger war ein einziger Vektor mit nur dem, was jeder sehen darf.
+4 -4
View File
@@ -29,7 +29,7 @@ const CLEARANCE_LEVELS = [
/**
* Startvorlagen nach docs/05-akten-und-vorlagen.md. Die Feldschlüssel sind
* stabil und werden nie geändert — eine Akte merkt sich ihre Vorlage über
* stabil und werden nie geändert - eine Akte merkt sich ihre Vorlage über
* `templateId`, und eine neue Vorlagenversion lässt bestehende Akten unberührt.
*
* Der Zuschnitt der Felder ist bewusst schmal gehalten: den Vorlagen-Editor
@@ -75,7 +75,7 @@ const TEMPLATES: Vorlage[] = [
const TEMPLATE_FIELDS = [
// Nachgebaut nach dem Vorfallsbericht der Abteilung. Die Foundation-Freigabe
// steht im Kopf des Dokuments, steuert aber keinen Zugriff — das tut allein
// steht im Kopf des Dokuments, steuert aber keinen Zugriff - das tut allein
// die interne Freigabe. Siehe docs/13-entscheidungen.md.
{
key: 'foundation_freigabe',
@@ -200,7 +200,7 @@ const PERSONALAKTE_FIELDS = [
/**
* Dokumentarten. Sie hängen an einer Fallakte, an einer Personalakte oder an
* nichts — was davon, steht in `uses`. Eine weitere Art anzulegen ist ein
* nichts - was davon, steht in `uses`. Eine weitere Art anzulegen ist ein
* Datensatz, kein Deployment: der Vorlagen-Editor reicht.
* Siehe docs/13-entscheidungen.md.
*/
@@ -367,7 +367,7 @@ async function main(): Promise<void> {
isActive: true,
},
// Version 1 gehört dem Seed und wird von ihm aktuell gehalten. Ändert
// der Leiter eine Vorlage im Editor, entsteht Version 2 — die fasst der
// der Leiter eine Vorlage im Editor, entsteht Version 2 - die fasst der
// Seed nie an, und bestehende Akten behalten ohnehin ihre Version.
update: {
name: template.name,
+3 -3
View File
@@ -10,7 +10,7 @@ import { resolveTestDatabaseUrl } from '../dist/testDatabase.js'
* Wird von `pnpm test` vorgeschaltet.
*
* **Neu aufsetzen, nicht nur anlegen.** Verlaufseinträge, Akten und
* Protokollzeilen sind nach Zusicherung 2 der README nicht löschbar — was
* Protokollzeilen sind nach Zusicherung 2 der README nicht löschbar - was
* ein Testlauf anlegt, bleibt für immer stehen. Nach einigen Dutzend Läufen
* standen 2487 Akten in der Datenbank, und Tests, die eine auf zehn Einträge
* begrenzte Liste prüfen, fanden ihre eigene Akte nicht mehr darin. Solche
@@ -45,7 +45,7 @@ try {
const behalten = process.env.KEEP_TEST_DB === '1'
if (rowCount > 0 && !behalten) {
// Offene Verbindungen verhindern das Verwerfen — etwa ein Prisma Studio
// Offene Verbindungen verhindern das Verwerfen - etwa ein Prisma Studio
// oder ein abgebrochener Testlauf.
await client.query(
`SELECT pg_terminate_backend(pid) FROM pg_stat_activity
@@ -87,7 +87,7 @@ execFileSync(process.execPath, [tsxCli, 'prisma/seed.ts'], {
// Die Änderungsrichtlinie ist eine einzige Zeile für die ganze Anwendung. Der
// Seed lässt sie bewusst in Ruhe, damit er die Einstellung des Leiters nicht
// überschreibt — in der Testdatenbank soll sie aber bei jedem Lauf im
// überschreibt - in der Testdatenbank soll sie aber bei jedem Lauf im
// Auslieferungszustand starten, sonst hängt das Ergebnis davon ab, was der
// vorige Lauf hinterlassen hat.
const reset = new pg.Client({ connectionString: testUrl })
+1 -1
View File
@@ -33,7 +33,7 @@ export function resolveTestDatabaseUrl(source = process.env.DATABASE_URL): strin
/**
* Setzt die Umgebung eines Testlaufs auf die Testdatenbank um. Muss laufen,
* bevor irgendein Modul einen Prisma-Client baut — deshalb gehört der Aufruf
* bevor irgendein Modul einen Prisma-Client baut - deshalb gehört der Aufruf
* in die `setupFiles` von Vitest, nicht in eine einzelne Testdatei.
*/
export function switchToTestDatabase(): string {
@@ -48,7 +48,7 @@ suite('Auslieferungszustand', () => {
// Der Seed vergibt keine Rechte. Rollen kommen ausschliesslich aus dem
// Abgleich des Workers und werden im Adminbereich zugeordnet.
// Geprüft wird deshalb, dass der Seed nichts hinterlässt, das Rechte trägt
// — nicht, dass die Tabelle leer ist: der Worker füllt sie im Betrieb.
// - nicht, dass die Tabelle leer ist: der Worker füllt sie im Betrieb.
const mitRechten = await db.roleMapping.findMany({
where: { permissions: { isEmpty: false } },
select: { label: true, permissions: true },
+1 -1
View File
@@ -240,7 +240,7 @@ suite('Datenbankregeln', () => {
})
it('löst Schwärzungen im Suchindex stufenweise auf', async () => {
// Der Vektor zur Stufe N muss enthalten, was diese Stufe lesen darf — und
// Der Vektor zur Stufe N muss enthalten, was diese Stufe lesen darf - und
// nichts darüber. Sonst findet ein Leser der Stufe 4 einen Namen nicht,
// den er auf der Seite sieht, oder ein Leser der Stufe 1 einen, den er
// dort nur als Balken sieht.
+1 -1
View File
@@ -8,7 +8,7 @@ import path from 'node:path'
* Der Anlass war handfest: ein von Hand angelegter GIN-Index über den
* Suchvektor stand in der Migration, aber nicht in `schema.prisma`. Prisma
* hielt das für einen Rückstand und fragte bei jedem `migrate dev` nach einem
* Namen für eine neue Migration — deren Inhalt gewesen wäre, **den Suchindex
* Namen für eine neue Migration - deren Inhalt gewesen wäre, **den Suchindex
* zu löschen**. Wer die Frage in Eile beantwortet, verliert die gestufte
* Volltextsuche und merkt es erst, wenn die Suche leer bleibt.
*