@cachly-dev/mcp-server 0.10.127 → 0.10.129

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/src/aussetzer.d.ts +89 -0
  2. package/dist/src/aussetzer.js +143 -0
  3. package/dist/src/aussetzer.js.map +1 -1
  4. package/dist/src/bauabdruck-daten.generiert.json +3 -3
  5. package/dist/src/bedeutung.d.ts +0 -7
  6. package/dist/src/bedeutung.js +72 -3
  7. package/dist/src/bedeutung.js.map +1 -1
  8. package/dist/src/bench/cachly-bench.d.ts +15 -0
  9. package/dist/src/bench/cachly-bench.js +15 -0
  10. package/dist/src/bench/cachly-bench.js.map +1 -1
  11. package/dist/src/eingaenge.d.ts +24 -0
  12. package/dist/src/eingaenge.js +32 -0
  13. package/dist/src/eingaenge.js.map +1 -1
  14. package/dist/src/feldreparatur.d.ts +58 -0
  15. package/dist/src/feldreparatur.js +113 -0
  16. package/dist/src/feldreparatur.js.map +1 -0
  17. package/dist/src/handlers/advanced.js +3 -3
  18. package/dist/src/handlers/advanced.js.map +1 -1
  19. package/dist/src/handlers/brain.js +252 -36
  20. package/dist/src/handlers/brain.js.map +1 -1
  21. package/dist/src/handlers/team.js +60 -4
  22. package/dist/src/handlers/team.js.map +1 -1
  23. package/dist/src/interferenz.d.ts +111 -0
  24. package/dist/src/interferenz.js +141 -0
  25. package/dist/src/interferenz.js.map +1 -0
  26. package/dist/src/rangfolge.d.ts +36 -0
  27. package/dist/src/rangfolge.js +35 -0
  28. package/dist/src/rangfolge.js.map +1 -1
  29. package/dist/src/resonanz.d.ts +85 -0
  30. package/dist/src/resonanz.js +140 -0
  31. package/dist/src/resonanz.js.map +1 -0
  32. package/dist/src/speicherlage.d.ts +51 -0
  33. package/dist/src/speicherlage.js +84 -0
  34. package/dist/src/speicherlage.js.map +1 -0
  35. package/dist/src/versuch.d.ts +9 -1
  36. package/dist/src/versuch.js +17 -3
  37. package/dist/src/versuch.js.map +1 -1
  38. package/dist/src/wartung/vektoren-verdichten.d.ts +50 -0
  39. package/dist/src/wartung/vektoren-verdichten.js +200 -0
  40. package/dist/src/wartung/vektoren-verdichten.js.map +1 -0
  41. package/dist/src/wertbeitrag.d.ts +95 -4
  42. package/dist/src/wertbeitrag.js +135 -5
  43. package/dist/src/wertbeitrag.js.map +1 -1
  44. package/glama.json +1 -1
  45. package/package.json +3 -2
  46. package/server.json +2 -2
@@ -44,6 +44,13 @@
44
44
  * danach aus dem Text entfernt, bevor er den Nutzer erreicht.
45
45
  */
46
46
  import type { Redis } from 'ioredis';
47
+ import { meldeUndVermerke } from './aussetzer.js';
48
+ /**
49
+ * So viel Speicher, wie ein Vermerk braucht. Bewusst strukturell getippt und
50
+ * nicht als Redis-Typ: dieses Modul soll ohne Redis-Abhaengigkeit pruefbar
51
+ * bleiben.
52
+ */
53
+ type VermerkSpeicherArt = Parameters<typeof meldeUndVermerke>[0];
47
54
  export declare const VERSUCH_ENV = "CACHLY_VERSUCH";
48
55
  export declare const VERSUCH_SALZ_ENV = "CACHLY_VERSUCH_SALZ";
49
56
  /** Grund fuer `meldeEinmal`: Versuch eingeschaltet, aber kein Salz gesetzt. */
@@ -57,7 +64,7 @@ export declare const OHNE_SALZ = "versuch-ohne-salz";
57
64
  * `meldeEinmal`) und verhaelt sich danach wie der erste — still weiterlaufen
58
65
  * waere die Fehlerklasse, gegen die `aussetzer.ts` gebaut wurde.
59
66
  */
60
- export declare function versuchStart(): string | null;
67
+ export declare function versuchStart(speicher?: VermerkSpeicherArt | null): string | null;
61
68
  /**
62
69
  * Eine neue, stabile Kennung je Recall-Aufruf.
63
70
  *
@@ -177,3 +184,4 @@ export interface VersuchProtokollTeil {
177
184
  * forget) und gibt den bereinigten Text zurueck — den, den der Nutzer sieht.
178
185
  */
179
186
  export declare function schliesseVersuchAb(redis: Redis, kennung: string, teil: VersuchProtokollTeil, fertigeAntwort: string): string;
187
+ export {};
@@ -44,7 +44,7 @@
44
44
  * danach aus dem Text entfernt, bevor er den Nutzer erreicht.
45
45
  */
46
46
  import { randomBytes, createHash } from 'node:crypto';
47
- import { meldeEinmal } from './aussetzer.js';
47
+ import { meldeEinmal, meldeUndVermerke, VERSUCH_LEER } from './aussetzer.js';
48
48
  // ── Der Schalter ─────────────────────────────────────────────────────────────
49
49
  export const VERSUCH_ENV = 'CACHLY_VERSUCH';
50
50
  export const VERSUCH_SALZ_ENV = 'CACHLY_VERSUCH_SALZ';
@@ -59,12 +59,26 @@ export const OHNE_SALZ = 'versuch-ohne-salz';
59
59
  * `meldeEinmal`) und verhaelt sich danach wie der erste — still weiterlaufen
60
60
  * waere die Fehlerklasse, gegen die `aussetzer.ts` gebaut wurde.
61
61
  */
62
- export function versuchStart() {
62
+ export function versuchStart(speicher) {
63
63
  if (process.env[VERSUCH_ENV] !== 'an')
64
64
  return null;
65
65
  const salz = process.env[VERSUCH_SALZ_ENV];
66
66
  if (!salz) {
67
- meldeEinmal(OHNE_SALZ, `${VERSUCH_ENV}=an, aber ${VERSUCH_SALZ_ENV} fehlt — der Versuch verhaelt sich wie "aus"`);
67
+ const text = `${VERSUCH_ENV}=an, aber ${VERSUCH_SALZ_ENV} fehlt — der Versuch verhaelt sich wie "aus"`;
68
+ if (speicher) {
69
+ // Ohne Speicher stand die Meldung nur im Arbeitsspeicher und war nach
70
+ // jedem Deploy weg (Karte opupbt3l9wcq, gefunden von der Gegenrede,
71
+ // Rolle: Der Betreiber). Der Versuch konnte damit WOCHENLANG leer
72
+ // laufen: "keine Daten" sah genauso aus wie "noch nicht genug Daten".
73
+ //
74
+ // meldeUndVermerke faengt jeden eigenen Fehler ab und gibt eine
75
+ // Zusage zurueck, die niemand einloesen muss — deshalb `void`. Diese
76
+ // Funktion bleibt synchron, weil ihre Aufrufer es sind.
77
+ void meldeUndVermerke(speicher, VERSUCH_LEER, text);
78
+ }
79
+ else {
80
+ meldeEinmal(OHNE_SALZ, text);
81
+ }
68
82
  return null;
69
83
  }
70
84
  return salz;
@@ -1 +1 @@
1
- {"version":3,"file":"versuch.js","sourceRoot":"","sources":["../../src/versuch.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"versuch.js","sourceRoot":"","sources":["../../src/versuch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Vektoren von float32 auf int8 umschreiben — der Umzug, den `packe` braucht.
3
+ *
4
+ * ── Warum es das gibt ───────────────────────────────────────────────────────
5
+ *
6
+ * Am 20.08.2026 belegte ein Bestand mit 524 Lektionen 23,6 MB von 25 MB
7
+ * Tarifgrenze — 94 %. Davon waren 11,2 MB Vektoren: 6,0 MB Volltext und Namen,
8
+ * 5,2 MB Eingänge. Die nächste Lektion wäre an einem Schreibfehler gescheitert.
9
+ *
10
+ * `packe` schreibt seither int8 statt float32 (Faktor 3,96). Neu geschriebene
11
+ * Lektionen sind damit klein. Die BESTEHENDEN bleiben groß, bis sie einmal neu
12
+ * gelernt werden — und die meisten werden nie neu gelernt. Dieses Werkzeug
13
+ * holt sie ein.
14
+ *
15
+ * ── Was es tut, und in welcher Reihenfolge ─────────────────────────────────
16
+ *
17
+ * 1. Sichern: jeder betroffene Schlüssel wird VORHER in eine Datei geschrieben.
18
+ * 2. Prüfen: die Sicherung wird zurückgelesen und muss vollständig sein.
19
+ * 3. Umschreiben: erst danach wird der Speicher angefasst.
20
+ *
21
+ * Ohne Schritt 2 wäre die Sicherung eine Behauptung. Eine Sicherung, die nie
22
+ * zurückgelesen wurde, ist keine.
23
+ *
24
+ * Aufruf:
25
+ * REDIS_URL=... npx tsx src/wartung/vektoren-verdichten.ts --sicherung <datei>
26
+ * REDIS_URL=... npx tsx src/wartung/vektoren-verdichten.ts --sicherung <datei> --schreiben
27
+ * REDIS_URL=... npx tsx src/wartung/vektoren-verdichten.ts --sicherung <datei> --zurueck
28
+ *
29
+ * Ohne `--schreiben` wird nur gerechnet und gemeldet. Das ist Absicht: der
30
+ * Trockenlauf ist der Normalfall, das Schreiben ist der Sonderfall.
31
+ */
32
+ import { Redis } from 'ioredis';
33
+ /** Alle Schlüssel zu einem Muster, ohne KEYS (das blockiert den Server). */
34
+ export declare function alleSchluessel(redis: Redis, muster: string): Promise<string[]>;
35
+ /**
36
+ * Ist dieser Wert schon im neuen Format?
37
+ *
38
+ * Wird gebraucht, damit ein zweiter Lauf nichts kaputt macht und nichts doppelt
39
+ * zählt. Ein Umzug, der nur einmal laufen darf, ist ein Umzug mit einer Falle.
40
+ */
41
+ export declare function istInt8(base64: string): boolean;
42
+ export interface Bilanz {
43
+ schluessel: number;
44
+ felder: number;
45
+ vorher: number;
46
+ nachher: number;
47
+ uebersprungen: number;
48
+ }
49
+ /** Rechnet aus, was der Umzug bringt — und schreibt nur, wenn `schreiben` gesetzt ist. */
50
+ export declare function verdichte(redis: Redis, schreiben: boolean): Promise<Bilanz>;
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Vektoren von float32 auf int8 umschreiben — der Umzug, den `packe` braucht.
3
+ *
4
+ * ── Warum es das gibt ───────────────────────────────────────────────────────
5
+ *
6
+ * Am 20.08.2026 belegte ein Bestand mit 524 Lektionen 23,6 MB von 25 MB
7
+ * Tarifgrenze — 94 %. Davon waren 11,2 MB Vektoren: 6,0 MB Volltext und Namen,
8
+ * 5,2 MB Eingänge. Die nächste Lektion wäre an einem Schreibfehler gescheitert.
9
+ *
10
+ * `packe` schreibt seither int8 statt float32 (Faktor 3,96). Neu geschriebene
11
+ * Lektionen sind damit klein. Die BESTEHENDEN bleiben groß, bis sie einmal neu
12
+ * gelernt werden — und die meisten werden nie neu gelernt. Dieses Werkzeug
13
+ * holt sie ein.
14
+ *
15
+ * ── Was es tut, und in welcher Reihenfolge ─────────────────────────────────
16
+ *
17
+ * 1. Sichern: jeder betroffene Schlüssel wird VORHER in eine Datei geschrieben.
18
+ * 2. Prüfen: die Sicherung wird zurückgelesen und muss vollständig sein.
19
+ * 3. Umschreiben: erst danach wird der Speicher angefasst.
20
+ *
21
+ * Ohne Schritt 2 wäre die Sicherung eine Behauptung. Eine Sicherung, die nie
22
+ * zurückgelesen wurde, ist keine.
23
+ *
24
+ * Aufruf:
25
+ * REDIS_URL=... npx tsx src/wartung/vektoren-verdichten.ts --sicherung <datei>
26
+ * REDIS_URL=... npx tsx src/wartung/vektoren-verdichten.ts --sicherung <datei> --schreiben
27
+ * REDIS_URL=... npx tsx src/wartung/vektoren-verdichten.ts --sicherung <datei> --zurueck
28
+ *
29
+ * Ohne `--schreiben` wird nur gerechnet und gemeldet. Das ist Absicht: der
30
+ * Trockenlauf ist der Normalfall, das Schreiben ist der Sonderfall.
31
+ */
32
+ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
33
+ import { Redis } from 'ioredis';
34
+ import { packe, entpacke } from '../bedeutung.js';
35
+ /** Alle Schlüssel zu einem Muster, ohne KEYS (das blockiert den Server). */
36
+ export async function alleSchluessel(redis, muster) {
37
+ const aus = [];
38
+ let cursor = '0';
39
+ do {
40
+ const [next, gefunden] = await redis.scan(cursor, 'MATCH', muster, 'COUNT', 500);
41
+ cursor = next;
42
+ aus.push(...gefunden);
43
+ } while (cursor !== '0');
44
+ return aus;
45
+ }
46
+ /**
47
+ * Ist dieser Wert schon im neuen Format?
48
+ *
49
+ * Wird gebraucht, damit ein zweiter Lauf nichts kaputt macht und nichts doppelt
50
+ * zählt. Ein Umzug, der nur einmal laufen darf, ist ein Umzug mit einer Falle.
51
+ */
52
+ export function istInt8(base64) {
53
+ const b = Buffer.from(base64, 'base64');
54
+ return b.byteLength > 7 && b[0] === 0x01 && b.byteLength % 4 === 1;
55
+ }
56
+ /** Rechnet aus, was der Umzug bringt — und schreibt nur, wenn `schreiben` gesetzt ist. */
57
+ export async function verdichte(redis, schreiben) {
58
+ const b = { schluessel: 0, felder: 0, vorher: 0, nachher: 0, uebersprungen: 0 };
59
+ // Einfache Werte: Volltext- und Namensvektoren.
60
+ for (const muster of ['cachly:lesson:vec:*', 'cachly:lesson:vecname:*']) {
61
+ for (const k of await alleSchluessel(redis, muster)) {
62
+ const roh = await redis.get(k);
63
+ if (!roh)
64
+ continue;
65
+ b.schluessel++;
66
+ if (istInt8(roh)) {
67
+ b.uebersprungen++;
68
+ b.vorher += roh.length;
69
+ b.nachher += roh.length;
70
+ continue;
71
+ }
72
+ const v = entpacke(roh);
73
+ if (!v) {
74
+ b.uebersprungen++;
75
+ continue;
76
+ }
77
+ const neu = packe(v);
78
+ b.vorher += roh.length;
79
+ b.nachher += neu.length;
80
+ b.felder++;
81
+ if (schreiben)
82
+ await redis.set(k, neu);
83
+ }
84
+ }
85
+ // Eingänge: ein Hash je Lektion, ein Vektor je Feld.
86
+ for (const k of await alleSchluessel(redis, 'cachly:lesson:eing:*')) {
87
+ const h = await redis.hgetall(k);
88
+ const felder = [];
89
+ b.schluessel++;
90
+ for (const [feld, roh] of Object.entries(h)) {
91
+ if (istInt8(roh)) {
92
+ b.uebersprungen++;
93
+ b.vorher += roh.length;
94
+ b.nachher += roh.length;
95
+ continue;
96
+ }
97
+ const v = entpacke(roh);
98
+ if (!v) {
99
+ b.uebersprungen++;
100
+ continue;
101
+ }
102
+ const neu = packe(v);
103
+ b.vorher += roh.length;
104
+ b.nachher += neu.length;
105
+ b.felder++;
106
+ felder.push(feld, neu);
107
+ }
108
+ if (schreiben && felder.length > 0)
109
+ await redis.hset(k, ...felder);
110
+ }
111
+ return b;
112
+ }
113
+ const mb = (zeichen) => `${(zeichen / 1024 / 1024).toFixed(2)} MB`;
114
+ async function main() {
115
+ const argv = process.argv.slice(2);
116
+ const flag = (n) => {
117
+ const i = argv.indexOf(`--${n}`);
118
+ return i !== -1 ? argv[i + 1] : undefined;
119
+ };
120
+ const url = process.env.CACHLY_REDIS_URL ?? process.env.REDIS_URL;
121
+ if (!url) {
122
+ console.error('NICHT GELAUFEN: REDIS_URL fehlt.');
123
+ process.exit(2);
124
+ }
125
+ const sicherungsPfad = flag('sicherung');
126
+ if (!sicherungsPfad) {
127
+ console.error('NICHT GELAUFEN: --sicherung <datei> fehlt.');
128
+ process.exit(2);
129
+ }
130
+ const schreiben = argv.includes('--schreiben');
131
+ const zurueck = argv.includes('--zurueck');
132
+ const redis = new Redis(url, { maxRetriesPerRequest: 2, connectTimeout: 8000 });
133
+ try {
134
+ if (zurueck) {
135
+ if (!existsSync(sicherungsPfad)) {
136
+ console.error(`NICHT GELAUFEN: Sicherung fehlt (${sicherungsPfad}).`);
137
+ process.exit(2);
138
+ }
139
+ const s = JSON.parse(readFileSync(sicherungsPfad, 'utf8'));
140
+ let n = 0;
141
+ for (const [k, v] of Object.entries(s.werte)) {
142
+ await redis.set(k, v);
143
+ n++;
144
+ }
145
+ for (const [k, h] of Object.entries(s.hashes)) {
146
+ const felder = Object.entries(h).flat();
147
+ if (felder.length === 0)
148
+ continue;
149
+ await redis.del(k);
150
+ await redis.hset(k, ...felder);
151
+ n++;
152
+ }
153
+ console.log(`ZURUECKGESPIELT: ${n} Schluessel aus ${sicherungsPfad}`);
154
+ return;
155
+ }
156
+ // 1. Sichern — immer, auch im Trockenlauf.
157
+ const werte = {};
158
+ const hashes = {};
159
+ for (const muster of ['cachly:lesson:vec:*', 'cachly:lesson:vecname:*']) {
160
+ for (const k of await alleSchluessel(redis, muster)) {
161
+ const roh = await redis.get(k);
162
+ if (roh)
163
+ werte[k] = roh;
164
+ }
165
+ }
166
+ for (const k of await alleSchluessel(redis, 'cachly:lesson:eing:*')) {
167
+ hashes[k] = await redis.hgetall(k);
168
+ }
169
+ writeFileSync(sicherungsPfad, JSON.stringify({ werte, hashes }), { encoding: 'utf8' });
170
+ // 2. Sicherung zurueckLESEN und pruefen. Ungeprueft ist sie keine.
171
+ const probe = JSON.parse(readFileSync(sicherungsPfad, 'utf8'));
172
+ const wSoll = Object.keys(werte).length;
173
+ const wIst = Object.keys(probe.werte).length;
174
+ const hSoll = Object.keys(hashes).length;
175
+ const hIst = Object.keys(probe.hashes).length;
176
+ if (wSoll !== wIst || hSoll !== hIst || wSoll === 0) {
177
+ console.error(`ABBRUCH: Sicherung unvollstaendig (${wIst}/${wSoll} Werte, ${hIst}/${hSoll} Hashes).`);
178
+ process.exit(3);
179
+ }
180
+ console.log(`Sicherung geprueft: ${wIst} Werte + ${hIst} Hashes in ${sicherungsPfad}`);
181
+ // 3. Erst jetzt rechnen bzw. schreiben.
182
+ const b = await verdichte(redis, schreiben);
183
+ console.log('');
184
+ console.log(` Schluessel angesehen : ${b.schluessel}`);
185
+ console.log(` Vektoren umgeschrieben: ${b.felder}`);
186
+ console.log(` uebersprungen : ${b.uebersprungen} (schon int8 oder unlesbar)`);
187
+ console.log(` vorher : ${mb(b.vorher)}`);
188
+ console.log(` nachher : ${mb(b.nachher)}`);
189
+ console.log(` gespart : ${mb(b.vorher - b.nachher)} (Faktor ${(b.vorher / Math.max(1, b.nachher)).toFixed(2)})`);
190
+ console.log('');
191
+ console.log(schreiben ? ' GESCHRIEBEN.' : ' TROCKENLAUF — nichts veraendert. Mit --schreiben ausfuehren.');
192
+ }
193
+ finally {
194
+ redis.disconnect();
195
+ }
196
+ }
197
+ const direktGestartet = process.argv[1]?.replace(/\\/g, '/').endsWith('/vektoren-verdichten.ts');
198
+ if (direktGestartet)
199
+ main().catch((e) => { console.error('FEHLER:', e); process.exit(1); });
200
+ //# sourceMappingURL=vektoren-verdichten.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vektoren-verdichten.js","sourceRoot":"","sources":["../../../src/wartung/vektoren-verdichten.ts"],"names":[],"mappings":"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"}
@@ -30,6 +30,32 @@
30
30
  * Starter-Korpus mit anderen Namen wäre er blind. Die Starter-Lektionen tragen
31
31
  * seit ihrer Einführung `source: 'starter'` im Datensatz — genau dafür. Diese
32
32
  * Datei benutzt die Markierung, nicht die Namen.
33
+ *
34
+ * ─── NACHTRAG 22.08.2026: DIE MARKIERUNG GAB ES NICHT ──────────────────────
35
+ *
36
+ * Der Absatz darüber stand fünf Tage lang so da und war falsch. Gemessen in
37
+ * der Gegenrede über die Zahl „Brain saved you ~1664h":
38
+ *
39
+ * brain_hygiene(dry_run) → "Startvorrat entwertet (Zähler auf 0): 0"
40
+ * bei 543 gescannten Lektionen.
41
+ *
42
+ * NULL. Der Filter griff bei keiner einzigen. `docker:layer-cache` — der
43
+ * Anlass dieser ganzen Datei — zählte weiterhin voll mit inzwischen 974
44
+ * Abrufen.
45
+ *
46
+ * Der Grund: Die eingebauten Lektionen kommen über `import_public_brain`
47
+ * herein und tragen dabei gar kein `source`-Feld. Die Markierung, auf die
48
+ * sich der Absatz oben verlässt, wird beim Schreiben nie gesetzt.
49
+ *
50
+ * Das ist die eigentliche Lehre, und sie ist unbequemer als die erste: Der
51
+ * Fix war gebaut, beschrieben und begründet — und hat nie gewirkt. Niemand
52
+ * hat nachgemessen, ob er greift. Fünf Tage lang stand in dieser Datei ein
53
+ * Kommentar über einen Schutz, den es nicht gab.
54
+ *
55
+ * Deshalb prüft `istStarterLektion` jetzt drei Dinge (Herkunft, Thema,
56
+ * fremder Installationsweg) — und deshalb steht darunter eine Probe, die
57
+ * nicht den Code liest, sondern eine echte Lektion durch die Funktion
58
+ * schickt. Ein Kommentar ist kein Beweis.
33
59
  */
34
60
  /** fmtStunden macht aus Minuten eine lesbare Angabe ("21 h", "3 d 4 h"). */
35
61
  export declare function fmtStunden(minuten: number): string;
@@ -37,17 +63,48 @@ export declare function fmtStunden(minuten: number): string;
37
63
  export interface BewertbareLektion {
38
64
  severity?: string;
39
65
  source?: string;
66
+ topic?: string;
67
+ what_worked?: string;
68
+ recall_count?: number;
40
69
  [k: string]: unknown;
41
70
  }
71
+ /** Die Themen des Startvorrats — fuer Aufrufer, die nur die Namen brauchen. */
72
+ export declare const STARTER_THEMEN: ReadonlySet<string>;
42
73
  /**
43
- * Stammt die Lektion aus dem mitgelieferten Startvorrat?
74
+ * Stammt die Lektion aus fremdem Wissen statt aus eigener Arbeit?
75
+ *
76
+ * Solche Lektionen duerfen abgerufen werden und sind nuetzlich — sie zaehlen
77
+ * nur nicht als etwas, das dieser Mensch oder dieses Team gelernt hat.
78
+ *
79
+ * ── NACHTRAG 22.08.2026: der Filter kannte nur die Haelfte ──────────────────
80
+ *
81
+ * Der Schutz war seit dem 17.08. gebaut, beschrieben und begruendet — und hat
82
+ * die groesste Einzelquelle nie erwischt. Er pruefte:
83
+ *
84
+ * return lesson?.source === 'starter';
85
+ *
86
+ * `import_public_brain` schreibt aber `source: 'public_brain'` (team.ts:1610),
87
+ * und dahinter steht eine EIGENE eingebaute Liste von rund 40 Lektionen ueber
88
+ * go, docker, kubernetes, react, typescript und python — darunter
89
+ * `docker:layer-cache`, die meistabgerufene Lektion dieses Brains ueberhaupt
90
+ * (980 Abrufe, gemessen am 22.08.2026).
44
91
  *
45
- * Solche Lektionen dürfen abgerufen werden und sind nützlich — sie zählen nur
46
- * nicht als etwas, das dieser Mensch oder dieses Team gelernt hat.
92
+ * Ein Vergleich auf genau eine Zeichenkette ist blind fuer den zweiten Weg.
93
+ * Und weil `brain_hygiene` (team.ts:1247) die Zeitersparnis aus derselben
94
+ * Funktion NEU rechnet, machte die Luecke die Zahl nicht nur zu gross, sondern
95
+ * beim Aufraeumen noch groesser: 1664 h → 6445 h.
96
+ *
97
+ * ── Warum trotzdem nicht nur ueber die Namen ────────────────────────────────
98
+ *
99
+ * Eine reine Namensliste wuerde eine echte Regel brechen: Wer selbst etwas
100
+ * ueber `docker:layer-cache` lernt, hat etwas gelernt. Deshalb entscheidet
101
+ * zuerst die Herkunft. Nur wenn gar keine dasteht — `learn_from_attempts`
102
+ * setzt das Feld nicht — wird das Thema geprueft, und dann muss auch der TEXT
103
+ * wortgleich der ausgelieferte sein. Ein eigener Satz zum selben Thema zaehlt.
47
104
  */
48
105
  export declare function istStarterLektion(lesson: BewertbareLektion | null | undefined): boolean;
49
106
  /**
50
- * Minuten, die ein Abruf dieser Lektion einspart — 0 für den Startvorrat.
107
+ * Minuten, die ein Abruf dieser Lektion einspart — 0 für fremdes Wissen.
51
108
  *
52
109
  * Die Staffel (30 / 60 / 240) ist unverändert; sie stand vorher an drei
53
110
  * Stellen im Code und steht jetzt hier. `basis` erlaubt der abweichenden
@@ -55,3 +112,37 @@ export declare function istStarterLektion(lesson: BewertbareLektion | null | und
55
112
  * die Starter-Regel ein zweites Mal geschrieben werden muss.
56
113
  */
57
114
  export declare function ersparteMinuten(lesson: BewertbareLektion | null | undefined, basis?: 'recall' | 'trace'): number;
115
+ /**
116
+ * Zaehlt dieser Abruf ueberhaupt Zeit — oder ist es ein Wiedersehen?
117
+ *
118
+ * ── DER ZWEITE BEFUND VOM 22.08.2026 ────────────────────────────────────────
119
+ *
120
+ * Der Startvorrat-Filter war nur die halbe Ursache. Die andere Haelfte:
121
+ * Zeitersparnis wurde bei JEDEM Abruf erneut gutgeschrieben.
122
+ *
123
+ * `smart_recall` schreibt bis zu FUENF Lektionen pro Aufruf die volle
124
+ * Recherchezeit gut (brain.ts:1725 ff.). Die Anleitung des Servers verlangt
125
+ * einen Aufruf "BEFORE every task", und eingeblendete Lektionen kommen
126
+ * zusaetzlich bei jedem Prompt. Zehn Aufgaben in einer Sitzung konnten so
127
+ * rechnerisch zweihundert Stunden erzeugen.
128
+ *
129
+ * Gemessen: Dieses Brain ist am 01.06.2026 entstanden, 82 Tage alt, und meldete
130
+ * 1664,5 Stunden. Eine Person haette in 82 Tagen zu acht Stunden 656 Stunden
131
+ * gehabt. Die Zahl war nicht zu hoch — sie war unmoeglich.
132
+ *
133
+ * ── Die Regel, die haelt ────────────────────────────────────────────────────
134
+ *
135
+ * Die Zusage lautet "time not re-researching known fixes". Recherchiert wird
136
+ * EINMAL. Wer dieselbe Lektion zum neunhundertsten Mal sieht, spart nicht zum
137
+ * neunhundertsten Mal eine Recherche — er spart ein Nachschlagen.
138
+ *
139
+ * Also: jede Lektion zaehlt genau EINMAL, beim ersten Abruf. Damit ist die
140
+ * Zahl nach oben durch den Wissensstand begrenzt statt durch die Nutzung, sie
141
+ * kann nicht mehr schneller wachsen als gelernt wird — und das Einblenden
142
+ * treibt sie gar nicht mehr.
143
+ *
144
+ * Das ist dieselbe Fehlerklasse wie `cachly:metric-inflation-fixed` (PR #158,
145
+ * stuendliche Heartbeat-Pings blaehten die ROI-Zahlen auf). Damals wurde die
146
+ * eine Quelle abgestellt; die Bauart, die aus Wiederholung Wert macht, blieb.
147
+ */
148
+ export declare function zaehltAlsErsparnis(lesson: BewertbareLektion | null | undefined): boolean;
@@ -30,7 +30,34 @@
30
30
  * Starter-Korpus mit anderen Namen wäre er blind. Die Starter-Lektionen tragen
31
31
  * seit ihrer Einführung `source: 'starter'` im Datensatz — genau dafür. Diese
32
32
  * Datei benutzt die Markierung, nicht die Namen.
33
+ *
34
+ * ─── NACHTRAG 22.08.2026: DIE MARKIERUNG GAB ES NICHT ──────────────────────
35
+ *
36
+ * Der Absatz darüber stand fünf Tage lang so da und war falsch. Gemessen in
37
+ * der Gegenrede über die Zahl „Brain saved you ~1664h":
38
+ *
39
+ * brain_hygiene(dry_run) → "Startvorrat entwertet (Zähler auf 0): 0"
40
+ * bei 543 gescannten Lektionen.
41
+ *
42
+ * NULL. Der Filter griff bei keiner einzigen. `docker:layer-cache` — der
43
+ * Anlass dieser ganzen Datei — zählte weiterhin voll mit inzwischen 974
44
+ * Abrufen.
45
+ *
46
+ * Der Grund: Die eingebauten Lektionen kommen über `import_public_brain`
47
+ * herein und tragen dabei gar kein `source`-Feld. Die Markierung, auf die
48
+ * sich der Absatz oben verlässt, wird beim Schreiben nie gesetzt.
49
+ *
50
+ * Das ist die eigentliche Lehre, und sie ist unbequemer als die erste: Der
51
+ * Fix war gebaut, beschrieben und begründet — und hat nie gewirkt. Niemand
52
+ * hat nachgemessen, ob er greift. Fünf Tage lang stand in dieser Datei ein
53
+ * Kommentar über einen Schutz, den es nicht gab.
54
+ *
55
+ * Deshalb prüft `istStarterLektion` jetzt drei Dinge (Herkunft, Thema,
56
+ * fremder Installationsweg) — und deshalb steht darunter eine Probe, die
57
+ * nicht den Code liest, sondern eine echte Lektion durch die Funktion
58
+ * schickt. Ein Kommentar ist kein Beweis.
33
59
  */
60
+ import { STARTER_CORPUS } from './starter-corpus.js';
34
61
  /** fmtStunden macht aus Minuten eine lesbare Angabe ("21 h", "3 d 4 h"). */
35
62
  export function fmtStunden(minuten) {
36
63
  const gesamt = Math.max(0, Math.round(minuten));
@@ -44,16 +71,79 @@ export function fmtStunden(minuten) {
44
71
  return rest > 0 ? `${tage} d ${rest} h` : `${tage} d`;
45
72
  }
46
73
  /**
47
- * Stammt die Lektion aus dem mitgelieferten Startvorrat?
74
+ * Der ausgelieferte Startvorrat, nach Thema aufgeschlagen — ABGELEITET.
75
+ *
76
+ * Erster Versuch am 22.08.2026 war eine von Hand gepflegte Namensliste. Sie war
77
+ * schon beim Schreiben falsch: sieben Themen fehlten, drei standen darin, die
78
+ * es gar nicht gibt. Eine zweite Liste derselben Wahrheit laeuft auseinander,
79
+ * bevor sie fertig ist.
80
+ */
81
+ const STARTER_NACH_THEMA = new Map(STARTER_CORPUS.map((l) => [l.topic, l.what_worked]));
82
+ /** Die Themen des Startvorrats — fuer Aufrufer, die nur die Namen brauchen. */
83
+ export const STARTER_THEMEN = new Set(STARTER_NACH_THEMA.keys());
84
+ /**
85
+ * Herkuenfte, die NICHT als eigene Lernleistung zaehlen.
86
+ *
87
+ * Es gibt zwei Wege, auf denen fremdes Wissen in ein Brain kommt, und sie
88
+ * schreiben ZWEI VERSCHIEDENE Werte:
89
+ *
90
+ * brain_seed_starter (share.ts) → source: 'starter'
91
+ * import_public_brain (team.ts) → source: 'public_brain'
92
+ *
93
+ * `marketplace:` und `syndicate:` sind vorgemerkt, damit der naechste
94
+ * Einbauweg nicht wieder still durchrutscht.
95
+ */
96
+ const FREMDE_HERKUNFT = /^(starter|public_brain|marketplace:|syndicate:|import)/;
97
+ /**
98
+ * Stammt die Lektion aus fremdem Wissen statt aus eigener Arbeit?
99
+ *
100
+ * Solche Lektionen duerfen abgerufen werden und sind nuetzlich — sie zaehlen
101
+ * nur nicht als etwas, das dieser Mensch oder dieses Team gelernt hat.
102
+ *
103
+ * ── NACHTRAG 22.08.2026: der Filter kannte nur die Haelfte ──────────────────
48
104
  *
49
- * Solche Lektionen dürfen abgerufen werden und sind nützlich — sie zählen nur
50
- * nicht als etwas, das dieser Mensch oder dieses Team gelernt hat.
105
+ * Der Schutz war seit dem 17.08. gebaut, beschrieben und begruendet — und hat
106
+ * die groesste Einzelquelle nie erwischt. Er pruefte:
107
+ *
108
+ * return lesson?.source === 'starter';
109
+ *
110
+ * `import_public_brain` schreibt aber `source: 'public_brain'` (team.ts:1610),
111
+ * und dahinter steht eine EIGENE eingebaute Liste von rund 40 Lektionen ueber
112
+ * go, docker, kubernetes, react, typescript und python — darunter
113
+ * `docker:layer-cache`, die meistabgerufene Lektion dieses Brains ueberhaupt
114
+ * (980 Abrufe, gemessen am 22.08.2026).
115
+ *
116
+ * Ein Vergleich auf genau eine Zeichenkette ist blind fuer den zweiten Weg.
117
+ * Und weil `brain_hygiene` (team.ts:1247) die Zeitersparnis aus derselben
118
+ * Funktion NEU rechnet, machte die Luecke die Zahl nicht nur zu gross, sondern
119
+ * beim Aufraeumen noch groesser: 1664 h → 6445 h.
120
+ *
121
+ * ── Warum trotzdem nicht nur ueber die Namen ────────────────────────────────
122
+ *
123
+ * Eine reine Namensliste wuerde eine echte Regel brechen: Wer selbst etwas
124
+ * ueber `docker:layer-cache` lernt, hat etwas gelernt. Deshalb entscheidet
125
+ * zuerst die Herkunft. Nur wenn gar keine dasteht — `learn_from_attempts`
126
+ * setzt das Feld nicht — wird das Thema geprueft, und dann muss auch der TEXT
127
+ * wortgleich der ausgelieferte sein. Ein eigener Satz zum selben Thema zaehlt.
51
128
  */
52
129
  export function istStarterLektion(lesson) {
53
- return lesson?.source === 'starter';
130
+ if (!lesson)
131
+ return false;
132
+ if (typeof lesson.source === 'string' && lesson.source !== '') {
133
+ // Herkunft steht da: sie entscheidet, in beide Richtungen.
134
+ return FREMDE_HERKUNFT.test(lesson.source);
135
+ }
136
+ // Keine Herkunft: nur ein WORTGLEICHER Treffer aus dem Vorrat zaehlt nicht.
137
+ if (typeof lesson.topic !== 'string')
138
+ return false;
139
+ const ausgeliefert = STARTER_NACH_THEMA.get(lesson.topic);
140
+ if (ausgeliefert === undefined)
141
+ return false;
142
+ const eigener = typeof lesson.what_worked === 'string' ? lesson.what_worked : '';
143
+ return eigener.trim() === ausgeliefert.trim();
54
144
  }
55
145
  /**
56
- * Minuten, die ein Abruf dieser Lektion einspart — 0 für den Startvorrat.
146
+ * Minuten, die ein Abruf dieser Lektion einspart — 0 für fremdes Wissen.
57
147
  *
58
148
  * Die Staffel (30 / 60 / 240) ist unverändert; sie stand vorher an drei
59
149
  * Stellen im Code und steht jetzt hier. `basis` erlaubt der abweichenden
@@ -70,4 +160,44 @@ export function ersparteMinuten(lesson, basis = 'recall') {
70
160
  return basis === 'trace' ? 120 : 60;
71
161
  return basis === 'trace' ? 60 : 30;
72
162
  }
163
+ /**
164
+ * Zaehlt dieser Abruf ueberhaupt Zeit — oder ist es ein Wiedersehen?
165
+ *
166
+ * ── DER ZWEITE BEFUND VOM 22.08.2026 ────────────────────────────────────────
167
+ *
168
+ * Der Startvorrat-Filter war nur die halbe Ursache. Die andere Haelfte:
169
+ * Zeitersparnis wurde bei JEDEM Abruf erneut gutgeschrieben.
170
+ *
171
+ * `smart_recall` schreibt bis zu FUENF Lektionen pro Aufruf die volle
172
+ * Recherchezeit gut (brain.ts:1725 ff.). Die Anleitung des Servers verlangt
173
+ * einen Aufruf "BEFORE every task", und eingeblendete Lektionen kommen
174
+ * zusaetzlich bei jedem Prompt. Zehn Aufgaben in einer Sitzung konnten so
175
+ * rechnerisch zweihundert Stunden erzeugen.
176
+ *
177
+ * Gemessen: Dieses Brain ist am 01.06.2026 entstanden, 82 Tage alt, und meldete
178
+ * 1664,5 Stunden. Eine Person haette in 82 Tagen zu acht Stunden 656 Stunden
179
+ * gehabt. Die Zahl war nicht zu hoch — sie war unmoeglich.
180
+ *
181
+ * ── Die Regel, die haelt ────────────────────────────────────────────────────
182
+ *
183
+ * Die Zusage lautet "time not re-researching known fixes". Recherchiert wird
184
+ * EINMAL. Wer dieselbe Lektion zum neunhundertsten Mal sieht, spart nicht zum
185
+ * neunhundertsten Mal eine Recherche — er spart ein Nachschlagen.
186
+ *
187
+ * Also: jede Lektion zaehlt genau EINMAL, beim ersten Abruf. Damit ist die
188
+ * Zahl nach oben durch den Wissensstand begrenzt statt durch die Nutzung, sie
189
+ * kann nicht mehr schneller wachsen als gelernt wird — und das Einblenden
190
+ * treibt sie gar nicht mehr.
191
+ *
192
+ * Das ist dieselbe Fehlerklasse wie `cachly:metric-inflation-fixed` (PR #158,
193
+ * stuendliche Heartbeat-Pings blaehten die ROI-Zahlen auf). Damals wurde die
194
+ * eine Quelle abgestellt; die Bauart, die aus Wiederholung Wert macht, blieb.
195
+ */
196
+ export function zaehltAlsErsparnis(lesson) {
197
+ if (!lesson || istStarterLektion(lesson))
198
+ return false;
199
+ // recall_count ist der Stand VOR diesem Abruf. 0 heisst: das erste Mal.
200
+ const bisher = typeof lesson.recall_count === 'number' ? lesson.recall_count : 0;
201
+ return bisher <= 0;
202
+ }
73
203
  //# sourceMappingURL=wertbeitrag.js.map