Anfuehrungszeichen: drei Loecher in meiner eigenen Wache

Die zweite Sitzung hat in den Praesentationen des Vaults 83 Stellen
gefunden — und damit auf Loecher in MEINER Wache gezeigt. Ich habe
sie nachgemessen, alle drei waren echt.

LOCH 1 — SIE SAH NUR `server/workspace-*.js`

Also ausgerechnet nicht `workspace.js`, die groesste Datei des
Hauses. Der weitere Filter fand dort sofort drei Stellen:

    workspace.js:3404  Kanal „Chat-Moderation"     (Protokoll)
    workspace.js:3422  „${titel}" -> ${haus}       (Protokoll)
    workspace.js:3304  „${… || "ohne Titel"}"      (siehe Loch 3)

Das ist heute der DRITTE Fall von „die Wache kennt nur die halbe
Menge" — nach pruef-struktur (sah nur die Pruefdateien, nicht die
Anwendung) und pruef-fingermass (sah keine variable Breite).

LOCH 2 — IHR AUSDRUCK GILT JE ZEILE

Ein `„`, das erst in der naechsten Zeile mit einem geraden `"`
schliesst, fiel durch. Gemessen: drei echte Faelle, zwei davon
sichtbarer Seitentext.

    workspace/leistung.html       „nicht / zugeordnet"
    workspace/leistung.html       „Backstage-Tabelle / einfuegen"
    workspace/assets/js/ampel.js  „noch ' + 'verbessern"

Beim letzten laeuft das Zitat ueber eine Zeichenketten-Verkettung.

Neue Regel ohne Fehlalarme: erst alle RICHTIG geschlossenen Paare
entfernen (auch mehrzeilig), dann die einzeilig falschen (die meldet
die alte Regel). Was danach noch ein `„` hat und binnen drei Zeilen
ein gerades `"`, ist ein Fund. Ausgenommen bleibt das Zeichen ALS
WERT — in `content: "„";` und in den Sprachtabellen ist das `„` der
Inhalt und das `"` die Begrenzung.

LOCH 3 — EINE AUSNAHME, DIE EINEN FUND VERSCHLUCKT HAT

Gefunden durch die ZAHL, nicht durch den Blick: Der weitere Filter
liess die Ausnahmen von 12 auf 13 steigen. Die dreizehnte war

    `… „${zeile[titelSpalte] || "ohne Titel"}"`

Der Ausdruck blieb am `"` von `"ohne Titel"` haengen; als Inhalt kam
`${zeile[titelSpalte] || ` heraus, das endet auf ein Leerzeichen,
und damit galt „der Satz geht in der naechsten Zeile weiter". Das
echte Schlusszeichen stand hinter dem `}` und war gerade.

Dass die Ausnahmen gezaehlt UND genannt werden, hat ihn gefunden.
Genau dafuer steht die Zeile dort.

Behoben an der Wurzel: Anfuehrungszeichen innerhalb einer Einsetzung
`${…}` werden vorher durch X ersetzt, bei gleicher Laenge und
gleichen Zeilenumbruechen. Das nimmt einer ganzen Sorte
Fehlklassifizierung die Grundlage — die Ausnahmen fielen danach von
13 auf 11.

UND EIN VIERTES, BEIM MESSEN AUFGEFALLEN

`„Van-Van”` in assets/js/data-modis.js schliesst mit U+201D, dem
ENGLISCHEN Zeichen. Zweimal, beide Male in einer DEUTSCHEN Zeile
(`de:` und `"de-CH":`) — nicht einmal eine Uebersetzung, in der es
richtig waere. Eigene Regel dafuer, die keine Ausnahme braucht: Wer
mit `„` oeffnet, schliesst deutsch; in fremdsprachigen Zeilen steht
gar kein `„` am Anfang.

DIE AUSNAHMEZAHL IST JETZT STRENG. Sie stand auf „hoechstens 12" —
damit waere der Anstieg auf 13 nicht aufgefallen. Jetzt genau 11,
wie bei den Grundlinien in pruef-fingermass.

GEPRUEFT: pruef-struktur 68 -> 75 Pruefungen, 0 Fehler. Dazu
leistung-optik 59/0, ampel 47/0, treff 85/0, modi-verborgen 87/0 —
die Seiten, deren Text ich angefasst habe. Steuerzeichen: 830
Dateien, keines. Beide Stempel gesetzt.

Sechs echte Stellen berichtigt, vier neue Gegenproben.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-10-01 19:28:38 +02:00
co-authored by Claude Opus 5
parent 2b3f140112
commit 1afb38258c
99 changed files with 1392 additions and 1210 deletions
+185 -3
View File
@@ -806,6 +806,49 @@ console.log(`\n=== Das deutsche Schlusszeichen ===`);
Datei sich beim naechsten Lauf selbst an. */
const suche = new RegExp(AUF + '([^' + ZU + AUF + '"`\n]{1,140})"', "g");
/* ==== ANFUEHRUNGSZEICHEN IN EINER EINSETZUNG SIND KEINE ==========
(01.10.2026, gefunden durch die ZAHL der Ausnahmen, nicht durch
den Blick.)
Der weitere Filter liess die Ausnahmen von 12 auf 13 steigen.
Die neue war server/workspace.js:3304 --
`… „${zeile[titelSpalte] || "ohne Titel"}"`
Das ist keine Ausnahme. Der Ausdruck blieb am `"` von
`"ohne Titel"` haengen; als Inhalt kam `${zeile[titelSpalte] || `
heraus, das endet auf ein Leerzeichen, und damit galt die Regel
„der Satz geht in der naechsten Zeile weiter". Das ECHTE
Schlusszeichen steht hinter dem `}` -- und war gerade. Die
Ausnahme hat den Fund verschluckt.
Dass die Ausnahmen GEZAEHLT werden, hat ihn gefunden. Genau
dafuer steht die Zeile dort.
Behoben an der Wurzel: Anfuehrungszeichen INNERHALB einer
Einsetzung werden vorher durch X ersetzt. Laenge und
Zeilenumbrueche bleiben gleich, alle Zeilennummern stimmen
weiter. Aus
„${zeile[titelSpalte] || "ohne Titel"}"
wird
„${zeile[titelSpalte] || XohneX TitelX}"
und der Ausdruck findet das richtige Zeichen. Das nimmt einer
ganzen Sorte Fehlklassifizierung die Grundlage, statt einen
Einzelfall einzutragen. */
const ohneEinsetzung = (text) => {
let aus = "";
let tief = 0;
for (let i = 0; i < text.length; i++) {
const c = text[i];
if (c === "$" && text[i + 1] === "{") { tief++; aus += "${"; i++; continue; }
if (c === "}" && tief > 0) { tief--; aus += "}"; continue; }
aus += (tief > 0 && (c === '"' || c === "'" || c === "`")) ? "X" : c;
}
return aus;
};
const warumNicht = (innen) => {
if (/[<>=]/.test(innen)) return "HTML-Marke oder Attribut";
if (innen.endsWith(" ")) return "Satz geht in der naechsten Zeile weiter";
@@ -835,7 +878,11 @@ console.log(`\n=== Das deutsche Schlusszeichen ===`);
...sammeln(join(SERVER, "..", "assets")),
...readdirSync(join(SERVER, "..")).filter((n) => n.endsWith(".html"))
.map((n) => join(SERVER, "..", n)),
...readdirSync(SERVER).filter((n) => n.startsWith("workspace-") && n.endsWith(".js"))
/* ALLE Servermodule, nicht nur `workspace-*` (01.10.2026).
Gemessen war dieses Loch leer -- in den uebrigen Modulen stand
keine falsche Stelle. Leer heisst aber nicht „kann nicht
vorkommen", und der weitere Filter kostet nichts. */
...readdirSync(SERVER).filter((n) => n.endsWith(".js"))
.map((n) => join(SERVER, n)),
];
ok(dateien.length > 100, `${dateien.length} Dateien mit sichtbarem Text angesehen`);
@@ -850,6 +897,7 @@ console.log(`\n=== Das deutsche Schlusszeichen ===`);
if (art === ".js" || art === ".mjs") {
text = text.replace(/^\s*\/\/.*$/gm, "");
}
text = ohneEinsetzung(text);
/* Der Pfad AB DER WURZEL des Projekts -- ein abgeschnittener
Pfad ist eine Meldung, der man nicht folgen kann. */
const kurz = pfad.split(sep).join("/").split("/DogiHompage/").pop();
@@ -870,9 +918,143 @@ console.log(`\n=== Das deutsche Schlusszeichen ===`);
/* DIE AUSNAHMEN WERDEN GEZAEHLT UND GENANNT. Eine Ausnahme, die
niemand sieht, waechst -- und irgendwann steht der echte Fall
darin und faellt nicht mehr auf. */
ok(stehenGelassen.length <= 12,
/* DIE ZAHL MUSS GENAU STIMMEN, nicht „hoechstens" (01.10.2026).
Sie stand auf `<= 12`. Dann waere nicht aufgefallen, dass sie
durch den weiteren Filter auf 13 gestiegen ist -- und die
dreizehnte war gar keine Ausnahme, sondern ein verschluckter
Fund (siehe `ohneEinsetzung` oben). Nach dessen Behebung sind
es 11.
Waechst sie, ist eine neue Stelle dazugekommen, die die Regel
nicht beurteilen kann. Sinkt sie, gehoert sie hier gesenkt --
sonst deckt sie Platz fuer die naechste. Dieselbe Strenge wie
bei den Grundlinien in pruef-fingermass. */
const AUSNAHMEN = 11;
ok(stehenGelassen.length === AUSNAHMEN,
`${stehenGelassen.length} Stellen bleiben mit Grund stehen`
+ ` (erlaubt: 12) — ${stehenGelassen.slice(0, 3).join(", ") || "—"}`);
+ ` (unveraendert: ${AUSNAHMEN}) — ${stehenGelassen.slice(0, 3).join(", ") || "—"}`);
/* ==== UND UEBER DIE ZEILENGRENZE (01.10.2026) ====================
Angestossen von der zweiten Sitzung: Sie hat in den
Praesentationen des Vaults 83 Stellen gefunden, weil sie
ZEILENWEISE mitgezaehlt hat, wie viele `„` offen sind. Die Regel
oben gilt je Zeile und kann das nicht.
Nachgemessen, was mir durchgegangen ist -- drei echte Faelle,
zwei davon sichtbarer Seitentext:
workspace/leistung.html „nicht / zugeordnet"
workspace/leistung.html „Backstage-Tabelle / einfuegen"
workspace/assets/js/ampel.js „noch ' + 'verbessern"
Beim letzten laeuft das Zitat ueber eine
Zeichenketten-Verkettung hinweg.
WIE ES GEHT, OHNE FEHLALARME: Erst alle RICHTIG geschlossenen
Paare entfernen (auch mehrzeilig), dann die einzeilig falschen
(die meldet die Regel oben, hier waeren sie doppelt). Was danach
noch ein `„` hat und binnen drei Zeilen ein gerades `"`, ist ein
Fund.
Ausgenommen bleibt dasselbe wie oben -- und zusaetzlich das
Zeichen ALS WERT: In `content: "„";` und in den Sprachtabellen
(`de: "„"`) ist das `„` der Inhalt und das `"` die Begrenzung. */
const mehrzeilig = [];
const alsWert = new RegExp('["\'`]\\s*' + AUF + '\\s*["\'`]');
for (const pfad of dateien) {
const art = pfad.slice(pfad.lastIndexOf("."));
let text = readFileSync(pfad, "utf8")
.replace(/\/\*[\s\S]*?\*\//g, (m) => m.replace(/[^\n]/g, " "))
.replace(/<!--[\s\S]*?-->/g, (m) => m.replace(/[^\n]/g, " "));
if (art === ".js" || art === ".mjs") text = text.replace(/^\s*\/\/.*$/gm, "");
text = ohneEinsetzung(text);
const kurz = pfad.split(sep).join("/").split("/DogiHompage/").pop();
/* Richtig geschlossene Paare weg -- auch ueber Zeilen. */
let rest = text.replace(new RegExp(AUF + "[^" + ZU + AUF + "]{0,300}?" + ZU, "g"),
(m) => m.replace(/[^\n]/g, " "));
/* Und die einzeilig falschen -- die meldet die Regel oben. */
rest = rest.replace(new RegExp(suche.source, "g"), (m) => m.replace(/[^\n]/g, " "));
const zeilen = rest.split("\n");
for (const t of rest.matchAll(new RegExp(AUF, "g"))) {
const zeile = rest.slice(0, t.index).split("\n").length;
const fenster = zeilen.slice(zeile - 1, zeile + 3).join("\n");
const paar = fenster.match(new RegExp(AUF + '([^' + ZU + AUF + ']{0,300}?)"'));
if (!paar) continue;
if (alsWert.test(zeilen[zeile - 1])) continue; /* Zeichen als Wert */
if (/[<>=]/.test(paar[1]) || paar[1].includes("\\")) continue;
mehrzeilig.push(`${kurz}:${zeile} ${AUF}${paar[1].replace(/\s+/g, " ").slice(0, 40)}"`);
}
}
ok(mehrzeilig.length === 0,
mehrzeilig.length
? `ein deutsches Anfuehrungszeichen wird erst in einer SPAETEREN Zeile`
+ ` mit einem geraden " geschlossen:\n ` + mehrzeilig.slice(0, 10).join("\n ")
: "kein Anfuehrungszeichen wird ueber die Zeilengrenze hinweg falsch geschlossen");
/* ==== DAS ENGLISCHE SCHLUSSZEICHEN (01.10.2026) ==================
Deutsch oeffnet mit „ und schliesst mit “. Ein ” dahinter ist
das ENGLISCHE Zeichen (U+201D). In data-modis.js stand zweimal
`„Van-Van”`, beide Male in einer deutschen Zeile (`de:` und
`"de-CH":`) -- also nicht einmal eine Uebersetzung, in der es
richtig waere.
Wer mit „ oeffnet, schliesst deutsch. Deshalb braucht diese
Regel keine Ausnahme fuer fremde Sprachen: Dort steht gar kein
„ am Anfang. */
const englisch = [];
const EN_ZU = "\u201D";
for (const pfad of dateien) {
const text = readFileSync(pfad, "utf8")
.replace(/\/\*[\s\S]*?\*\//g, (m) => m.replace(/[^\n]/g, " "))
.replace(/<!--[\s\S]*?-->/g, (m) => m.replace(/[^\n]/g, " "));
const kurz = pfad.split(sep).join("/").split("/DogiHompage/").pop();
for (const t of text.matchAll(
new RegExp(AUF + "([^" + ZU + AUF + EN_ZU + "\n]{1,160})" + EN_ZU, "g"))) {
const zeile = text.slice(0, t.index).split("\n").length;
englisch.push(`${kurz}:${zeile} ${AUF}${t[1].slice(0, 40)}${EN_ZU}`);
}
}
ok(englisch.length === 0,
englisch.length
? `schliessen ein deutsches Anfuehrungszeichen mit dem ENGLISCHEN `
+ `Zeichen (U+201D):\n ` + englisch.slice(0, 10).join("\n ")
: "keines schliesst mit dem englischen Zeichen");
/* ---- GEGENPROBEN zu den zwei neuen Regeln --------------------- */
{
const ueberZeile = (code) => {
let rest = code.replace(new RegExp(AUF + "[^" + ZU + AUF + "]{0,300}?" + ZU, "g"), " ");
rest = rest.replace(new RegExp(suche.source, "g"), " ");
const zl = rest.split("\n");
let n = 0;
for (const t of rest.matchAll(new RegExp(AUF, "g"))) {
const z = rest.slice(0, t.index).split("\n").length;
const f = zl.slice(z - 1, z + 3).join("\n");
const paar = f.match(new RegExp(AUF + '([^' + ZU + AUF + ']{0,300}?)"'));
if (!paar) continue;
if (alsWert.test(zl[z - 1])) continue;
if (/[<>=]/.test(paar[1]) || paar[1].includes("\\")) continue;
n++;
}
return n;
};
ok(ueberZeile(`unter ${AUF}nicht\nzugeordnet" auf`) === 1,
"Gegenprobe: ein Zitat ueber zwei Zeilen faellt auf");
ok(ueberZeile(`unter ${AUF}nicht\nzugeordnet${ZU} auf`) === 0,
" richtig geschlossen NICHT");
ok(ueberZeile(`content: "${AUF}";`) === 0,
" und das Zeichen als Wert auch nicht");
const enTest = (code) => [...code.matchAll(
new RegExp(AUF + "([^" + ZU + AUF + "\u201D\n]{1,160})\u201D", "g"))].length;
ok(enTest(`${AUF}Van-Van\u201D`) === 1,
"Gegenprobe: das englische Schlusszeichen faellt auf");
ok(enTest(`${AUF}Van-Van${ZU}`) === 0, " das deutsche NICHT");
}
/* ---- GEGENPROBEN ------------------------------------------------ */
const findet = (code) => {
+3 -3
View File
@@ -3301,7 +3301,7 @@ export function haeuserNachtragen(d) {
unmittelbarere Beleg; erst wenn keiner etwas sagt, wird
gefragt, woran die Zeile haengt. */
const h = hausAus(leute(zeile)) || (ausEltern ? ausEltern(zeile) : null);
const wer = `${tabelle}#${zeile.id} „${zeile[titelSpalte] || "ohne Titel"}"`;
const wer = `${tabelle}#${zeile.id} „${zeile[titelSpalte] || "ohne Titel"}“`;
if (h === "crew" || h === "agentur") { schreib.run(h, zeile.id); meldung.gesetzt++; }
else if (h === "konflikt") meldung.konflikt.push(wer);
else meldung.offen.push(wer);
@@ -3401,7 +3401,7 @@ function restzeilenZuordnen(d, meldung) {
.run(new Date().toISOString()).changes;
d.prepare("UPDATE chat_raeume SET haus = 'crew' WHERE id = 3").run();
meldung.gesetzt++;
console.log(`[workspace] Kanal „Chat-Moderation" -> Team Dogi, ${n} aus dem anderen Haus herausgenommen.`);
console.log(`[workspace] Kanal „Chat-Moderation“ -> Team Dogi, ${n} aus dem anderen Haus herausgenommen.`);
}
} catch (f) { console.error("[workspace] Kanal 3:", f?.message); }
@@ -3419,7 +3419,7 @@ function restzeilenZuordnen(d, meldung) {
.run(haus, id, titel).changes;
if (n) {
meldung.gesetzt += n;
console.log(`[workspace] Haus von Hand gesetzt: ${tabelle}#${id} „${titel}" -> ${haus}`);
console.log(`[workspace] Haus von Hand gesetzt: ${tabelle}#${id} „${titel}“ -> ${haus}`);
}
} catch (f) {
console.error(`[workspace] Restzeile ${tabelle}#${id}:`, f?.message);