cta-Block bekommt seinen Entitaets-Bezug (Auswahl + Interessens-Automatik)
Der cta-Block ist mit 252 Vorkommen der viert-haeufigste Block im Seiten-Bestand und war der einzige Conversion-Block ohne Entitaets-Bezug - der Redakteur tippte Texte ein zweites Mal ab, die als CTA-Entitaet laengst gepflegt waren.
- Katalog-Eintrag 'cta' mit paletteHidden (Sonder-Maske BLEIBT, Inline-Weg ist der Rueckfall): _cta (Auswahl echter Werte), _ctaAuto, _ctaInterest. - DREI Marker-Entscheidungen weichen vom popup-Nachbarn ab, alle am Bestand gemessen (2026-08-23, 55 Datensaetze): KEIN scopeByWebsite (_website fehlt 55/55), KEIN markUnpublished (published fehlt 55/55 - die popup-Regel prueft published !== true und haette alle 55 als 'nicht veroeffentlicht' gekennzeichnet), dafuer markForeignLanguage MIT dem neuen langField. - CatalogRefSource.langField (neu, Vorgabe 'lang'): CTAs fuehren ihre Sprache in 'locale'. Ohne den Zeiger laese foreignLanguageSuffix 'lang', saehe undefined und kennzeichnete NICHTS - waehrend der Zeichner sehr wohl filtert. Der Schalter waere lautlos wirkungslos gewesen. - block-renderer: catalog-block-editor unter der cta-Sondermaske verdrahtet. Ohne diese Zeile waere der ganze Katalog-Eintrag unsichtbar. - Beide Bestands-Waechter der Probe-Datei erweitert (Renderer-Schluessel-Gleichheit, Zeichenketten-Filter-Ausnahme) plus 6 neue cta-Proben. 64/64 gruen; Flip-Probe belegt (langField + locale-Projektion entfernt -> genau 2 rot).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newpages
cta-Block liest die gepflegte CTA-Entitaet (Marker-Weg + Interessens-Automatik)
Der cta-Block war mit 252 Vorkommen der viert-haeufigste Block im Bestand und zugleich der einzige Conversion-Block ohne Entitaets-Bezug: die Plattform pflegt CTAs seit Jahren als eigene Entitaet mit eigenem Ausliefer-Dienst (ctas.ayoune.app) und eigenem Klick-Zaehler (clt.ayoune.app), der Website-Bauer kannte sie nicht und der Redakteur tippte denselben Text ein zweites Mal.
- mapCtasToItems: ICTA -> Block-Inhalt. Strippt HTML (21 von 55 Datensaetzen tragen Markup in h1/h2), behandelt den Platzhalter '-' als leer, leitet den Sprach-Code aus 'locale' ab (NICHT 'lang' wie bei References/Faqs/Quotes - ein lang-Filter saehe undefined und liesse jede Fassung durch) und setzt die Knopf-Adresse auf den Klick-Zaehler. - resolveEntityBlocks: cta-Zweig. Ausdrueckliche Wahl (_cta) schlaegt den Auto-Zug (_ctaAuto + _ctaInterest); der Auto-Zug ist fail-closed und fragt ohne Kriterium die Datenbank gar nicht. WEDER _website- NOCH published-Filter - beide Felder fehlen bei 55/55 Datensaetzen, ein vom popup-Nachbarn abgeschriebener Filter haette jede CTA weggeschnitten. - renderBlock case 'cta': Entitaet schlaegt Inline, Sprachwahl liegt hier (der Resolver kennt die ausgelieferte Sprache nicht). Ohne passende Fassung bleibt der Block still statt auf die alten Inline-Texte zurueckzufallen. - app.ts: aMN.CTAs geladen (marketing-Modul, kein cms-Auto-Load) - ohne den Eintrag waere der ganze Zweig still wirkungslos. - app.ts: aMN.Quotes nachgetragen. NEBENBEFUND derselben Klasse: der quotes-Zweig stand seit der Zitate-Welle im Zeichner, sein Modell wurde nie geladen. Latent - quotes kommt im Seiten-Bestand 0-mal vor (Positiv-Kontrolle: 23 andere Typen).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newpages
cta-Block liest die gepflegte CTA-Entitaet (Marker-Weg + Interessens-Automatik)
Der cta-Block war mit 252 Vorkommen der viert-haeufigste Block im Bestand und zugleich der einzige Conversion-Block ohne Entitaets-Bezug: die Plattform pflegt CTAs seit Jahren als eigene Entitaet mit eigenem Ausliefer-Dienst und Klick-Zaehler, der Website-Bauer kannte sie nicht.
- mapCtasToItems: ICTA -> Block-Inhalt. Strippt HTML (21 von 55 Datensaetzen tragen Markup in h1/h2), behandelt den Platzhalter '-' als leer, leitet den Sprach-Code aus 'locale' ab (NICHT 'lang' wie bei References/Faqs/Quotes) und setzt die Knopf-Adresse auf den Klick-Zaehler clt.ayoune.app/cta/:id. - resolveEntityBlocks: cta-Zweig. Ausdrueckliche Wahl (_cta) schlaegt den Auto-Zug (_ctaAuto + _ctaInterest); der Auto-Zug ist fail-closed und fragt ohne Kriterium die Datenbank gar nicht. WEDER _website- NOCH published-Filter - beide Felder fehlen bei 55/55 Datensaetzen, ein vom popup-Nachbarn abgeschriebener Filter haette jede CTA weggeschnitten. - renderBlock case 'cta': Entitaet schlaegt Inline, Sprachwahl liegt hier (der Resolver kennt die ausgelieferte Sprache nicht). Ohne passende Fassung bleibt der Block still statt auf die alten Inline-Texte zurueckzufallen. - app.ts: aMN.CTAs geladen (marketing-Modul, kein cms-Auto-Load) - ohne den Eintrag waere der ganze Zweig still wirkungslos. - app.ts: aMN.Quotes nachgetragen. NEBENBEFUND derselben Klasse: der quotes-Zweig stand seit der Zitate-Welle im Zeichner, sein Modell wurde nie geladen. Latent - quotes kommt im Seiten-Bestand 0-mal vor (Positiv-Kontrolle: 23 andere Typen).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newayauto
`job list` + `job trigger` — Faelligkeits-Jobs von Hand starten
Ein Eintrag der worker-automatic-checks-Registrierung war bis hierher NUR startbar, indem man den Auftrag direkt in die BullMQ-Warteschlange schrieb — genau der manuelle Eingriff, den die Tooling-first-Regel ausschliessen soll. Die Route POST /jobs/:name/actions/trigger existierte, war aber von keinem Werkzeug erreichbar: `ay` weist operator-only-Module ab, MCP sperrt jede Nicht-GET-Methode in Produktion, `ayauto su` kann nur get/set auf EINEN Datensatz. Form uebernommen von `su`/`queue`: benannter Eintrag, kein Platzhalter, confirm-gated.
🔴 DER TRAGENDE BEFUND: die ausgelieferte Registrierung verschweigt `defaultData`. Gemessen am laufenden Dienst enthaelt GET /jobs die Zeichenkette null-mal, waehrend optionsSchema und queueName da sind (Positiv-Kontrolle) — `serializeRegistry` gibt das Feld schlicht nicht aus. Zugleich reicht die Ausloese-Route den Rumpf WOERTLICH als Auftragsdaten durch. Ein Hand-Start ohne Nutzlast stellt also {}; ein Verbraucher, der auf `op` besteht, weist ihn ab — und der Aufruf sieht trotzdem wie ein Erfolg aus (200 mit Auftrags-Kennung). Genau das beschreibt der Eintrag HR_BENEFIT_RECEIPTS_MONTHLY woertlich.
Deshalb liest `job trigger` `defaultData` ZUSAETZLICH aus dem Arbeitsbaum und zeigt es im Trockenlauf — ausdruecklich als Hinweis aus der QUELLE, nicht aus der ausgelieferten Registrierung. Gesendet wird es nur mit --use-local-default. Erfunden wird NIE etwas: ohne Angabe geht {} hinaus, und der Trockenlauf sagt, was das bedeutet.
Der Extrakt hat zwei Riegel, beide mit eigener Probe: Zeilen-Kommentare werden entfernt (der Quelltext WARNT an mehreren Stellen mit dem Wortlaut `defaultData: { dryRun: false }` davor, es nachzutragen) und Zeichenketten werden laengentreu maskiert (die Beschreibungen SPRECHEN ueber defaultData und op). Echt-Abgleich gegen die Registrierung: 22 Vorkommen in der Quelle, 22 vom Extrakt gefunden.
Optionen werden gegen das optionsSchema des Eintrags geprueft — ein vertippter Schluessel wird abgewiesen samt Nennung der gueltigen, statt still zu verpuffen. Ein erfundener Job-Name und ein `enabled: false`-Eintrag werden abgewiesen, ohne etwas einzureihen.
23 Proben mit Umkehr- und Positiv-Kontrollen, gruen; Bau gruen. Abnahme am laufenden Dienst: Trockenlauf zeigt den vollen Auftrag inkl. op · erfundene Option/erfundener Name je Exit 2 ohne Einreihung · echter Start von check-all-health lief DURCH (recent-runs: manual:true, active→completed, 4621 ms), die Nachbarzeile desselben Takts traegt manual:false.
OFFEN: der saubere Rueckweg ist eine Zeile in serializeRegistry (`defaultData: d.defaultData`) — sie liegt in domains/monitoring/ worker-automatic-checks, ausserhalb der beanspruchten Pfade, und ist als eigener Vorgang erfasst.
Fixhuelle
Fuss-Bereich mobil einspaltig + Menue-Schublade anklickbar
Zwei CEO-Screenshot-Befunde (tolinax.com mobil), beide in der GLOBALEN Marken-Huelle — sie galten fuer jede Mandanten-Website.
1) Der Fuss-Bereich stand mobil zweispaltig gequetscht. Die Grund-Regel repeat(auto-fit,minmax(150px,1fr)) hat keine Mobil-Fassung; am echten 396px-Sichtfeld gemessen: 2 Spalten a 158,5px, nachher 1 Spalte a 349px. Dazu Abstand unten fuer den schwebenden Chat-Knopf.
2) Die Menue-Schublade war NICHT anklickbar — und z-index war nie die Ursache. backdrop-filter auf dem Kopf macht ihn zum containing block fuer position:fixed-Kinder UND zu einem eigenen Stapel-Kontext. Die Schublade liegt IM Kopf, war also an ihm verankert statt am Sichtfeld (top:40px statt 0) und ihr z-index:200 wirkte nur innerhalb des Kopfes. elementFromPoint in ihrer Mitte lieferte ay-body. Flip-Probe: mit backdrop-filter:none top:0 und ay-nav-link.
🔴 Die erste Fassung war HALB wirksam: die Kopf-Regel im 768er-Block wird vom spaeteren Dunkel-Block ueberschrieben (gleiche Spezifitaet, Reihenfolge entscheidet). Weichzeichner ging, Durchsicht blieb — schlechter als vorher. Die geltende Fassung steht als eigener Media-Block am ENDE der Datei; eine Probe nagelt die POSITION fest, nicht nur die Anwesenheit.
⚠ Der Weichzeichner wird nicht ersatzlos genommen, sondern ueberfluessig gemacht: deckender Hintergrund aus --ay-bg, hell wie dunkel. Der Schreibtisch behaelt ihn.
⚠ Die Schublade aus dem Kopf herauszuziehen waere der andere Weg — er scheitert an .ay-header-collapse{display:contents}, das auf dem Schreibtisch Navigation und Aktionen zu direkten Flex-Kindern macht.
7 neue Proben (mit Positiv-Kontrolle und Umkehrprobe), volle Reihe 1765/1766.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixnotifications
Broadcast-Zweig trifft auch die LEERE LISTE
Der Zweig vom Vormittag war zu eng: user liegt in drei Auspraegungen vor (objectId 7.130 / array 826 / fehlend 418), und 102 der Listen sind leer. {user:null} trifft eine leere Liste nicht (418 statt 520) — die 102 waren nach dem ersten Rollout fuer NIEMANDEN mehr sichtbar. Kategorien wie bei den unadressierten, also Broadcasts.
$in statt $size:0 — dieselben 520 Treffer, aber indizierbar.
An der Sammlung gemessen liegt user in drei Auspraegungen vor: objectId 7.130, array 826, fehlend 418. Von den Listen sind 102 LEER, und {user:null} trifft eine leere Liste nicht (418 statt 520). Diese 102 waren vor dem Schnitt fuer jeden sichtbar und waeren mit dem engeren Zweig fuer NIEMANDEN mehr sichtbar gewesen.
An der Sammlung gemessen liegt `user` in drei Auspraegungen vor: objectId 7.130, array 826, fehlend 418. Von den Listen sind 102 LEER — und {user:null} trifft eine leere Liste NICHT ({user:null}=418, {$in:[null,[]]}=520). Diese 102 tragen dieselben Kategorien wie die unadressierten und waren vor dem Schnitt fuer jeden sichtbar; mit dem engeren Zweig waeren sie fuer NIEMANDEN mehr sichtbar gewesen.
$in statt $size:0 — dieselben 520 Treffer, aber indizierbar.
Empfaenger-Schnitt fuer notifications an allen vier generischen Lesewegen
Der erzwungene Mandanten-Schnitt ist bei notifications zu grob: der Verteiler legt EIN Dokument JE EMPFAENGER an, die SDUI-Tabelle zeigte also die Post der Kollegen. Neu: {$or:[{user:<ich>},{user:null}]} — eigene ODER unadressierte, nie fremde.
Der Schnitt liegt am SERVER, nicht in der basePipeline des Zustands: die reist als Teil der Aufrufer-Pipeline mit und kann weggelassen werden. Verdrahtet an allen vier Lesewegen (aggregation, export, queries x2) — ein ungeschuetzter waere die Umgehung aller anderen, und der Export gibt die Zeilen ausser Haus.
20 Proben, vier Flip-Proben (Feldname / Broadcast-Zweig / Export-Verdrahtung / Einschub im Hauptweg).
unreadNotifications + page.notifications lasen {_customerID, markread:false} — der Verteiler legt aber EIN Dokument JE EMPFAENGER an, die Glocke zeigte also die ungelesene Post der Kollegen. Neu: eigene ODER unadressierte, nie fremde.
Zweite Haelfte und ohne sie waere die erste wirkungslos: der Zwischenspeicher- Schluessel traegt jetzt userId statt customerId — sonst fuellt der erste Nutzer den Eintrag und jeder weitere bekommt 30 s lang dessen Post, ohne Fehler.
Empfaenger-Schnitt auch auf den zwei Mengen-Schreibwegen (PUT /many, updateMode many)
changeMany/updateMany bauen ihren Filter intern aus {_id} und nehmen keine zusaetzliche Bedingung entgegen — der Schnitt liegt deshalb auf einer Vorab-Lesung (filterIdsToRecipient). Fremde Kennungen fallen still heraus; trifft die Verengung nichts, wird kein leerer bulkWrite gefahren (waere ein 500).
Rest-Schnitt zum P1 notifications-nutzer-scope (2026-08-23).
Bis hierher wendete KEIN Pfad einen `level:'block'`-Override im DOM an: der Builder legt die Tests seit dem 2026-08-23 an, die Lose werden gezogen — aber jeder Besucher sah die Kontrolle, es entstanden 0 Impressionen, und nichts meldete einen Fehler.
- `blockExperiments.ts` — findet die Baustein-Region ueber das Marken-Paar, das der cms-Renderer beim Backen setzt, und ersetzt sie durch die Varianten-Behaelter. Dieselbe Mechanik wie `applyPageExperiment`, eine Ebene tiefer; der Zuweisungs-Algorithmus ist UNVERAENDERT (die drei Kopien bleiben byte-gleich). - `getBlockExperiments` — mehrere je Seite, je Baustein hoechstens eines, derselbe Mandanten-/Website-Riegel und dieselbe Haltedauer wie beim Seiten-Zwilling. - Verdrahtet in `pages.ts` UND `home.ts`. Die Startseite wird NICHT von `pages.ts` gezeichnet — wer nur eine der beiden verdrahtet, laesst die wichtigste Seite aus (dieselbe Falle wie bei W7).
⚠ Die Marken BLEIBEN im HTML stehen, der Behaelter kommt dazwischen — sonst waere die Region nach dem ersten Ersetzen weg.
⚠ Laufzeit-Definition genau EINMAL je Seite: traegt die Seite schon ein Seiten-Experiment, kommen von hier nur noch die Aufrufe (`includeRuntime:false`).
11 neue Proben, darunter die woertliche Marken-Form (Vertrag ueber zwei Repositorien) und eine Positiv-Kontrolle „ohne Experiment byte-identisch".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newwebpages
Block-Marken im gebackenen HTML + render-block-variant
Die zwei cms-Haelften der Baustein-A/B-Ausspielung.
1) `renderBlocksToHtml` legt um jeden TOP-LEVEL-Baustein ein Marken-Paar `<!--ay-block:<_id>-->…<!--/ay-block:<_id>-->`. Ohne einen stabilen Schluessel im HTML kann die Auslieferung eine Baustein-Region nicht wiederfinden — genau daran scheiterte `target.blockKey` bisher.
Bewusst HTML-KOMMENTARE statt des vorgeschlagenen `data-ay-block`-Attributs: ein Wrapper-Element aenderte das DOM (Geschwister-Selektoren, Raster) und braeche die Regressions-Zusicherung; ein Attribut am aeussersten Element setzte voraus, dass jeder Baustein GENAU EIN Wurzel-Element liefert — ueber die 47 Faelle des Zeichners ist das nicht zugesichert. Kommentare sind DOM-neutral und begrenzen die Region exakt.
Der Hex-Riegel auf der Kennung ist kein Schoenheitsfilter: ein Schluessel mit `-->` schloesse die Marke und liesse beliebiges HTML aus dem Kommentar.
2) `POST /webpages/:id/actions/render-block-variant` backt EINE Varianten- Fassung eines Bausteins zu HTML. Gebacken wird beim ANLEGEN des Experiments, nicht beim Ausliefern: die consumer-app rendert Bausteine nicht selbst, und ein Backen beim Veroeffentlichen koppelte den Experiment-Lebenszyklus an das Veroeffentlichen — wer ein Experiment startet, ohne die Seite erneut zu veroeffentlichen, haette keine Variante im HTML. Dieselbe Form, die auf der Seiten-Ebene bereits ausgebracht ist.
25 neue Proben (14 rein + Verdrahtung, 10 am Handler, alle mit Umkehrprobe).
⚠ Die Handler-Suite mockt jetzt getCustomerById/getDefaultEventConsumer/ remoteReplace: ohne sie lief EIN Fall 37 s in echte Netz-Zeitgrenzen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newpodcast
Titelbild auf der Episoden-Karte, ueber einen geteilten Bauer
Die Episoden-Karte (Mediathek + Episodenliste) trug bisher kein Bild, waehrend der Sendungs-Kopf eines hatte — die beiden Flaechen waren auseinandergelaufen.
- renderCoverBild(): die EINE Stelle, an der ein Sendungs-Titelbild entsteht. renderSendungsKopf ist darauf umgestellt (war die einzige Kopie). - PodcastWiedergabeKontext.coverBySendung: eine ZUORDNUNG sendungSlug -> Adresse, kein Einzelwert. Die Mediathek zeigt Folgen MEHRERER Sendungen (sie filtert nach sendungSlug, mediathek.ts:181/210); ein Einzelwert haette dort jeder Karte das Bild derselben Sendung gegeben. - Das Bild erscheint nur, wenn die Flaeche eine Zuordnung mitgibt. Sonst haette die Mediathek ab sofort ein Raster aus Initialen-Kacheln gezeigt, obwohl es kein einziges Bild gibt. Innerhalb einer bebilderten Flaeche traegt jede Karte den Platzhalter, damit die Zeilen nicht springen.
7 neue Proben, beide Flip-Proben beissen (Bild-Ausgabe entfernt -> 3 rot; Bauer umbenannt -> Wortgrenzen-Riegel rot). Volle Reihe 1735/1736 gruen.
Die Befuellung von coverBySendung liegt in routes/ und ist ein eigener Brief (podcast-coverbysendung-befuellen) — bis dahin zeigt keine Flaeche ein Bild.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newpage-editor
Feld "Frühere Adressen" in der Seiten-Maske
Neben dem Adress-Kürzel ein mehrwertiges Feld (Chips, dem bestehenden Schlagwort-Muster folgend): der Redakteur sieht die automatisch fortgeschriebenen Vorgänger UND kann einen falsch angehängten Eintrag entfernen — der abweichende Wert gewinnt serverseitig gegen die Automatik.
- eigene Kopie der Liste im Dialog, damit "Abbrechen" wirklich abbricht (dieselbe Falle, die der Kommentar für die Schlagwörter beschreibt) - eingetippte Adressen werden auf die gespeicherte Schreibweise gebracht (kein führender/abschließender Schrägstrich) — sonst leitet die Adresse nie um - alle vier Sprachen (de/en/it/es)
11 Proben: Schreibweise + Sprach-Vollständigkeit, je mit Positiv-Kontrolle.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixcoord
Kanon-Wert verdict extend im Brief import-media-schreibt-in-den-heimat-mandanten
Newwebpages
Adress-Historie beim Slug-Wechsel automatisch fortschreiben
Haengt beim Speichern einer GEAENDERTEN Adresse die vorige an IWebPage.formerSlugs an — die Schreiber-Haelfte der Umleitungs-Strecke, deren Leser in der consumer-app seit 2026-08-23 lebt (alte Adresse -> 301).
Bauform der Nachbarn (routerWebPagesGuard / routerPodcastCover): custom-Router VOR dem erzeugten Buendel, ergaenzt den Rumpf und faellt mit next() durch — EIN Schreibvorgang, keine zweite Wahrheit. Entscheidung rein in adressHistorie.ts.
Zwei Befunde, die den Bau bestimmt haben: - Kuration ist nur eine ABWEICHUNG vom gespeicherten Wert. Der Seiten-Editor schickt beim Speichern den ganzen geladenen Datensatz zurueck; waere jedes mitgeschickte formerSlugs schon Kuration, griffe die Automatik auf dem einzigen Weg nie, auf dem sie gebraucht wird. - PUT /webpages/many mit MEHR als einer Kennung kann die Automatik per Bauart nicht bedienen (changeMany schreibt EIN gemeinsames Update an alle Kennungen; ein Mengen-Operator wuerde von sanitizeUpdateBody entfernt). Bei genau einer Kennung bedient, darueber gewarnt und nichts geschrieben.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixdashboard
Seiten-Filter-Leiste an den Dienst haengen (OnPush-Luecke)
Der Riegel in `syncDashboardConfig()` lief die ganze Zeit korrekt — gemessen: nach dem Wechsel traegt `DashboardPageFilterService.layer` sauber `null`. Sichtbar blieb der Fremd-Stand trotzdem, weil die Leiste `ChangeDetectionStrategy.OnPush` faehrt und ihre Anzeige nur ueber GETTER auf den Dienst liest, ohne ihn zu abonnieren. Bei OnPush prueft Angular ein Kind nur bei Referenz-Aenderung einer Eingabe, bei einem eigenen Ereignis oder nach `markForCheck()`; beim Wechsel zwischen zwei Dashboards ohne gespeicherten Filter trifft nichts davon zu (`@Input() layer` ist beide Male `null`), und das `markForCheck()` am Ende von `syncDashboardConfig()` markiert die ELTERN- Komponente, was ein OnPush-Kind nicht schmutzig macht.
Deshalb waren zwei Runden lang alle Proben gruen: sie messen `filter.layer`, also den DIENST — und der war immer korrekt. Die Luecke sass zwischen Dienst und BILD.
- Leiste abonniert `pageFilter.layer$` und ruft `markForCheck()` (kein dritter Riegel: der Zustand wird nicht erneut gesetzt, nur die Anzeige daran gehaengt). - Optionen-Abruf nur bei geaenderten Filter-DEFINITIONEN, sonst laedt jede Bedienung neu. - `ngOnChanges` ENTFERNT samt seiner fuenf Proben (Abschnitt B): er konnte den Fall per Bauart nie fangen, und jeden Fall, den er wirklich abdeckte, deckt heute `syncDashboardConfig()` ab. - Neue Reihe misst das GERENDERTE Bild statt des Dienstes; beidseitige Flip-Probe (vor dem Fix 1 FAILED, danach 6/6), Umkehrprobe (Dashboard MIT eigenem Filter behaelt seinen Stand) und Gegenprobe (kein Optionen-Abruf pro Klick). 47/47 der vier betroffenen Reihen gruen, `npm run build:stage` gruen.
Fixcoord
Kanon-Werte im Brief mediathek-und-episodenkarte (verdict extend, surface fe-existing)
Fixcoding
Rueckfall-Luecke des Ereignis-Stroms benannt + Waechter fuer die stream-json-Argumente
Der Glied-B-Fix (defaultAgentArgs) trug den ganzen Rueckkanal und hatte keine einzige Probe: ein versehentliches Streichen der vier Zeichenketten faellt nirgends auf, weil der Lauf gruen bleibt und nur der Strom verschwindet.
- agentArgs.test.mjs: 9 Proben (Bestandteile einzeln, Pfad-/Endungs-Aufloesung, zwei Negativ-Kontrollen, Flip-Probe). In test:floors verdrahtet. - structuredOutputMissing(): ein Dispatch MIT eigenen args behaelt sie unveraendert und verliert dabei den Strom - das wird jetzt benannt statt stillschweigend ueberstimmt. - CLAUDE.md: der Strom-Vertrag samt empirischem Beleg gegen Claude-CLI 2.1.241.
Onboarding-Mails in vier Sprachen + zwei Reseller-Anlage-Vorlagen
Kaskade reseller-onboarding-prozesse 2026-08-23: die vier Onboarding-Vorlagen (welcome-customer, welcome-user-new-tenant, invite-user-to-tenant, invite-customer-from-reseller) tragen en_US/it_IT/es_ES als Uebersetzungs-Nachtrag (Sync-Schleife schreibt je Sprache nur fehlende Schluessel — betreiberfest). Neu: reseller-customer-created-notify (Betreiber + Prozess-Empfaenger) und reseller-customer-create-result (Abschluss an den Anlegenden), beide viersprachig. Waechter: Platzhalter-Paritaet je Uebersetzung gegen die Grundfassung (test/seed-onboarding-mail-translations.test.mjs, fing 2 echte Abweichungen).
Fixayauto
Zeilenenden von src/index.ts zurueck auf LF
Der vorige Commit hat die Datei per Python-Schreibvorgang im Textmodus von LF auf CRLF gedreht — aus 49 hinzugefuegten Zeilen wurde ein Diff ueber 10281. Der Blob trug vorher 0 CR-Bytes, danach 5165 (genau eins je Zeile). Inhalt unveraendert; gegen den Vor-Stand bleiben die 49 additiven Zeilen der su-Gruppe.
Die Falle steht in der Wurzel-CLAUDE.md: ein Python dreht unter Windows die ganze Datei.
Es gab zwei Betreiber-Kanaele und fuer `su` ihre Schnittmenge NICHT: `cli_call` (ueber `ay`) kann mutieren, erreicht `su` aber nicht (CEO-Entscheid 2026-06-20, Modul-Riegel VOR jeder Aufloesung); `api_call` (MCP) erreicht `su`, sperrt aber jede Nicht-GET-Methode in Produktion. Serverseitig ist der Zugriff offen — die Sperre lag ausschliesslich im Werkzeugkasten.
🔴 Die Regel von 2026-06-20 wird NICHT aufgeweicht: `tooling/cli` ist unangetastet und erreicht `su` weiterhin nicht (gemessen, siehe Gegenprobe). Der Zugriff wandert aus der ausgelieferten CLI in die interne, die per CEO-Regel 2026-08-10 nie ausgeliefert wird.
Form uebernommen von `ayauto queue`: benannter Datensatz, kein Platzhalter, confirm-gated. Ohne --confirm ein Trockenlauf, der NICHTS schreibt.
- `ayauto su get <entitaet> <id>` / `set <entitaet> <id> --field --value [--confirm]` - Rueckroll-Abbild VOR jedem Schreibvorgang, das festhaelt, ob das Feld vorher ueberhaupt existierte — der Rueckweg fuer ein ABWESENDES Feld ist ein $unset, nicht ein `false`; sonst bliebe ein Wert stehen, den der Bestand nie kannte. - Token aus --token / AYAUTO_API_TOKEN / angemeldeter ay-CLI ueber den GETEILTEN Lader (keine zweite Umsetzung); wandert nie in den Baum. - Schreibweg ist `PUT /<plural>` mit der Kennung im RUMPF (der erzeugte CRUD hat kein `PUT /<plural>/<id>`); verifiziert wird am DATENSATZ, nicht an der Antwort.
Verify gegen Produktion (alle fuenf Tore des Briefes): - ohne --confirm: `updatedAt` am Datensatz UNVERAENDERT (in der DB gemessen) - mit --confirm: `updatedAt` 13:45:47 -> 14:25:22, Wert wertneutral true->true - ohne Token: Exit 2 mit `NOT-READY:kein-betreiber-token — <grund>` - Gegenprobe: `ay` sagt weiterhin "Module su is operator-only and is not reachable from the customer-facing ay CLI" - Rueckroll-Abbild geschrieben, 0 Token-Vorkommen, unterscheidet abwesend von false
Gemessener Defekt (2026-08-23, marketing, zwei Pods): L4 (Override-Inhalt, 60 s) und L5 (Override-Existenz, 5 min) sind lru-cache-Instanzen IM PROZESS. Ein PUT/DELETE auf eine Override-Ebene leert nur den Pod, der die Anfrage bekommt — nach einem DELETE zeigten 8 von 12 unmittelbar folgenden Abfragen den geloeschten Override weiter, bis zu 5 Minuten lang.
Neu: cache/overrideInvalidationBus — dieselbe Bauform, die core fuer ayoune:rights-registry-updated und ayoune:translations-updated schon faehrt (Redis-pub/sub, JOB_REDIS_*, still ohne Redis). Kanal ayoune:states-override-invalidated. Verworfen: ChangeStream auf der Sammlung (deckt L5 nicht ab, braucht ein Replica-Set-Recht), Haltezeit 0, Warten im Client.
Alle VIER Schreibwege gehen jetzt ueber den einen Aufruf burstOverrideCaches: DELETE user (services/userOverrideReset) und PUT user/reseller/agency (routes/state.ts). Ein Waechter haelt fest, dass kein Schreibweg mehr nur lokal leert. Haltezeiten unveraendert.
13 neue Proben (235/235 gruen). Flip-Proben beidseitig gefahren: ohne Sender faellt Probe 8 (zwei Pods), ohne Boot-Verdrahtung Probe 13.
ioredis als DIREKTE Abhaengigkeit, exakt 5.11.1 wie core — die transitive Aufloesung unter core ist keine Zusage; dedupliziert zu einer Kopie.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01415vhKTqP7zhWwyf4xT7xn
Newhumanizer
Serien-Schnitt neben dem Je-Text-Pruefer
Der bestehende Pruefer misst 33 Muster JE TEXT und kann den geteilten Bauplan einer SERIE per Bauart nicht sehen. Gemessen: er gibt jedem der 35 maschinell erzeugten Mitarbeiter-Profile 100/100 - der neue Serien-Schnitt gibt derselben Menge 61.
Vier Achsen ueber die Menge: gleiche Eroeffnungsform, wiederkehrende rhetorische Figur (3 Figuren, je eigene Schwelle), Bekenntnis-Schluss, zu enges Wortband (Spanne/Median). Unter 3 Texten schweigt er, statt eine Zahl zu erfinden.
Jede Schwelle ist an DREI echten Mengen aus der Produktion geeicht, keine geraten: 35 maschinelle Profile (muss durchfallen, 61), 6 von Hand umgebaute Team-Texte (muss bestehen, 100), 11 echte Kundenstimmen (muss bestehen, 100). Menge C ist der Waechter gegen die eigentliche Gefahr dieses Schnitts - er darf GENRE nicht fuer Maschinen-Duktus halten.
Zwei Detektoren trennten gemessen in die FALSCHE Richtung und sind daher gestrichen bzw. umgebaut: der einraeumende Nachsatz (Maschine 0.11, Mensch 0.33) und die Schluss-Signatur-Gruppierung (Maschine 0.43, Kundenstimmen 0.73 - sie mass die Gleichfoermigkeit eines Genres).
Der Je-Text-Pruefer ist unveraendert (leerer Diff auf allen vier Bestandsdateien). 26 neue Proben inkl. Umkehr-Kontrolle je Detektor; 3 Flip-Proben gefahren, alle beissen an der erwarteten Stelle. test:humanizer 177/177, test:floors ergaenzt.
Noch nicht verdrahtet: scoreSeries hat keinen Aufrufer, runHumanize arbeitet je Dokument und kennt keine Menge.
Newpage-editor
dynamische UCD-Rolle je Baustein anbieten und anzeigen
Der Bauer bot `settings.id` bisher UEBERHAUPT NICHT an (15 Schluessel, `id` keiner davon) — ein Redakteur konnte einen Baustein also nicht einmal von Hand als dynamisch markieren, obwohl der Renderer die Kante seit 2026-08-23 traegt.
- `ucd-slots.ts`: dritte Kopie der 13 Vertrags-Namen mit Klartext-Beschriftungen; angeboten werden nur die 7, die eine DOM-Kennung brauchen. `h1`-`h6` bewusst nicht — der Klient findet sie ueber den Element-Namen. - Panel "Kennung & dynamischer Inhalt": Auswahl der Rolle PLUS Freitext-Anker. Beide schreiben auf dieselbe `settings.id`, deshalb sperrt eine gewaehlte Rolle das Anker-Feld — die Grenze "ein Baustein traegt genau eine Kennung" wird von der Oberflaeche gezeigt, nicht in einer Fussnote erklaert. - Dauerhafte Marke am Baustein auf der Leinwand (nicht erst beim Ueberfahren), nur ein Merkmal, keine eigene Aktions-Leiste.
Proben: 10 neue am gerenderten Bauteil + 7 auf der Liste; Gruppe 95/95 gruen. Vier Flip-Proben beissen (h1 in der Auswahl 4 rot, Freitext als Rolle 2 rot, Marke aus + Anker-Sperre aus 3 rot). Renderer-Probe cms-api 11/11 unveraendert.
Fixmulti-select
aufgeloeste Verweis-Werte als ausgewaehlt zeigen
Der states-Dienst liefert Verweis-Felder POPULIERT (Liste von Objekten), die Optionen tragen dagegen die Kennung als Wert. mat-select vergleicht ohne compareWith per === — Objekt gegen Zeichenkette trifft nie, also war nichts ausgewaehlt, obwohl der Wert da war. Im Browser am Feature "Anhang (HGB)" gemessen: 21 Optionen geladen, 0 als ausgewaehlt gefuehrt.
Nicht nur Anzeige: mat-select multiple schreibt beim Speichern die Werte zurueck, die es kennt — eine einzige Auswahl haette die unsichtbare Zuordnung still ersetzt.
Der Wert wird deshalb vor dem ersten Rendern auf Kennungen zurueckgefuehrt (emitEvent: false, kein dirty — die Maske speichert selbsttaetig auf valueChanges). Bewusst nicht ueber compareWith: das haette allein die Anzeige geheilt und weiter Objekte zum Server geschrieben.
Proben: 5 neue in multi-select-zeigt-gespeicherten-wert.spec.ts, davon zwei als Flip-Probe belegt (vorher rot, nachher gruen); Reihe 30/30.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newucds
Pflegeflaeche fuer dynamische Inhalte — Liste + Bearbeitungsmaske
Der Zustand marketing.ucds existierte, war aber seit 2024 deprecated und trug eine reine Selbst-Erzeugung als viewConfig: die Spalten hiessen 'desktop' und 'tablet' (die BLAETTER des h1-Unterobjekts) und zeigten garantiert nichts — weder key noch url noch topic waren sichtbar. Eine Bearbeitungsmaske fehlte vollstaendig, die Alt-Flaeche antwortete mit 404. Wer einen dynamischen Inhalt pflegen wollte, hatte plattformweit keinen Weg dorthin.
Liste: Alt-Untergrenze aus old-ui-inventory (Schluessel · Adresse · Thema · Region · PLZ), Schluessel vorn, weil er in der Anzeigen-Adresse reist. Maske: 49 Felder in sechs Abschnitten — Zuordnung, Platzhalter (var1-4 mit dem Hinweis, dass sie Ersetzungen sind, keine Anzeige-Schlitze), dann die 13 Schlitze je Geraeteklasse nebeneinander.
Kein headerKpis: die Sammlung traegt 10 Dokumente, davon 9 Pruefdaten des naechtlichen Laeufers (gemessen 2026-08-23).
Drift-Sentinel nachgezogen: 43->44 states, 34->35 Tabellen, 19->20 Edit-Spezifikationen. ⚠ Hier wandert nur die Tabellen-Zahl mit — das Bearbeitungs-Gegenstueck lebt in der anderen Datei; wer beide im Gleichschritt erwartet, sucht ein Formular, das hier per Bauart nie stand.
Proben: 6316 gruen. Der Feld-Pruefer liest alle 49 neuen Felder — per Flip belegt (erfundenes h1.gibtEsNicht -> rot mit Datei:Zeile). Umlaut-Waechter fing vier ASCII-Ersatzschreibungen, geheilt. Fundament-Frische mitgezogen (core 315.2, interfaces 453.0, models 458.0).
Newpages
Adress-Historie leitet mit 301 statt 404 (formerSlugs, Kaskade 56)
Ein Adress-Wechsel einer CMS-Seite hinterliess bis hierher eine tote Adresse: `/de/alte-adresse` antwortete 404 ohne `location`-Kopf, jeder externe Verweis und jeder Suchmaschinen-Eintrag lief ins Leere, und die aufgebaute Bewertung wanderte nicht mit. Der Fundament-Anteil (`IWebPage.formerSlugs` + Schema- Zwilling + sparse-Index `{_customerID,_website,formerSlugs}`) liegt seit Kaskade 56 vor und hatte bis jetzt keinen Leser.
- `getPageByFormerSlug` — gezielter Nachschlag, mandanten- UND website-gebunden, nur veroeffentlichte Seiten, beidseitig zwischengespeichert (auch das negative Ergebnis: eine frei waehlbare 404-Adresse darf keinen un-gecachten Datenbank-Zugriff ausloesen). Fail-soft. - `matchesFormerPageSlug` / `formerSlugRedirectTarget` — rein + exportiert, damit die Proben die ECHTE Aufloesung pruefen statt einer Kopie. - `resolveAndRender`: der Nachschlag steht VOR dem 404 und **301**, nicht 302 — nur der dauerhafte Code laesst Suchmaschinen die Bewertung uebertragen. Der bestehende Sprach-Rueckfall bleibt bewusst 302.
RANGFOLGE strukturell statt per Vergleich: der Nachschlag laeuft ausschliesslich in einem `if (!page)`-Zweig, eine Historie kann eine lebende Adresse also nicht ueberstimmen. Das ist der Unterschied zum Blog-Vorbild (`resolveBlogDocBySlug`), das alle Beitraege als Liste haelt und deshalb zwei Durchgaenge braucht.
13 Proben, alle fuenf Flip-Proben beissen je genau eine Zusage: 301->302, `if(!page)`-Klammer weg, `slug` aus der Projektion, Selbst-Schleifen-Riegel weg, Trim weg. Volle Reihe 1698/1698. Negativ-Kontrollen belegen, dass der heutige Bestand (0 von 372 Seiten mit Historie) unveraendert ausgeliefert wird.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newcodegen
Riegel gegen den Sammel-Mount eines Rumpfes, der eine Kennung braucht
`actionAddressPlacement()` sagt, WO eine Aktion hingehoert — es sagt NICHT, ob der vorhandene Rumpf dort bedienbar ist. Fuer `crm.Consumers` hat der Erzeuger sechs `both`-Aktionen bereits EINMAL an die Sammel-Adresse gemountet (Commit 5bc61ad); ihre Ruempfe lesen `req.params.id`, und Mongoose entfernt `_id: undefined` still aus dem Filter — der Zugriff traefe einen BELIEBIGEN Datensatz des Mandanten. Entdeckt wurde es von Hand, verhindert seither nur durch einen Hand-Haertungs- Marker an der Router-Datei, also durch ABWESENHEIT der Route.
Neu: `collection-safety.ts` prueft den BESTEHENDEN Rumpf vor dem Sammel-Mount. Kein `req.params.id` -> sicher. Mit kanonischem Waechter -> sicher. Datei existiert noch nicht -> sicher (die Vorlage traegt den Waechter). Sonst wird der Mount VERWEIGERT und AUSGEWIESEN (`[skip-unsafe-collection-mount]`), mit Grund UND Ausweg — ein stilles Auslassen waere dieselbe unsichtbare Mechanik wie das stille Setzen.
🔴 KOMMENTAR-BEWUSST, und das ist die teuerste Falle: `actionStreamRegenerate` (cms.Streams) erwaehnt `req.params.id` VIER MAL in seinem Kopfkommentar, um zu dokumentieren, dass es die Kennung eben NICHT von dort liest. Eine rohe Textsuche verweigert dort einen Mount, der voellig in Ordnung ist — nach dem zweiten solchen Fehlalarm schaltet jemand den Riegel ab.
FAIL-CLOSED mit ausdruecklichem Ausweg: erkannt wird nur die kanonische Waechter- Form der Vorlage; wer einen anderen Stil faehrt, setzt den Marker `✅ SAMMEL-SICHER`. Ein Irrtum laeuft damit immer in die sichere Richtung.
⚠ Beim Bau selbst getroffen: eine dynamisch gebaute Regex ueberlebt weder `node -e` noch ein Heredoc (`'if\s*\('` wurde zu `'if\s*\('`, die Regex las `ifs*(`, und der Waechter fand NIE einen Waechter). Die Waechter-Erkennung laeuft deshalb ueber einen leerraum-normalisierten Zeichenketten-Vergleich, backslash-frei.
Proben: 20 Faelle mit Umkehrproben (mit Waechter schweigt der Riegel), zwei Kommentar-Fallen samt Positiv-Kontrolle zum Strippen, Negativkontrollen (Waechter auf ANDERER bzw. LAENGERER Variable zaehlt nicht; ASCII-Ersatzform des Markers traegt nicht), sowie Proben gegen den ECHTEN Bestand und die ECHTE Vorlage.
Verify-Gate am echten Erzeuger beidseitig gefahren (`codegen routes --module crm --dry-run`): alle sechs gehaertet -> kein verweigerter Mount; einen Waechter temporaer entfernt -> Mount VERWEIGERT mit benanntem Grund; byte-genau wiederhergestellt. Reihe 4830 (+20), die 6 roten sind beidseitig als vorbestehend belegt (Fehlerlisten vor/nach identisch).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012LmDa2yAaQZtJrAss6EuZ8
Fixconsumers
entityId-Waechter fuer die sechs both-Aktionen — 501 statt beliebigem Treffer
Die sechs `availability: 'both'`-Aktionen (archive, ignore-in-merge, blacklist, add-to-list, assign-segment, export) lesen `req.params.id` und schreiben mit `{_id: entityId, _customerID}`. An der Sammel-Adresse gibt es keine Kennung, und Mongoose ENTFERNT `_id: undefined` still aus dem Filter statt zu werfen — der Zugriff traefe damit einen BELIEBIGEN Datensatz des Mandanten, ohne Fehler und ohne Protokollzeile.
Geschuetzt waren sie bis hierher nur durch die ABWESENHEIT der Sammel-Route: ein Hand-Haertungs-Marker haelt den Erzeuger davon ab, sie dort erneut zu mounten. Das war noetig, weil genau das schon EINMAL passiert ist (Commit 5bc61ad). Abwesenheit ist keine Abwehr — die Ruempfe tragen jetzt selbst den Waechter aus der Erzeuger-Vorlage (`actionStub.ts.tpl:64-82`) und antworten ehrlich 501.
Verhalten mit gueltiger Kennung ist unveraendert (reine Einfuegung, 6x16 Zeilen).
Proben: `sammelAdresseWaechter.test.ts` — je Aktion das PAAR aus Abweisung ohne Kennung (KEIN Datenbank-Zugriff, KEIN Ereignis) und UMKEHRPROBE mit Kennung (regulaerer Weg laeuft), plus zwei Negativkontrollen (leere Kennung wird ebenso abgewiesen; der Waechter kommt beim isValid-Ausstieg nicht zum Zug). 14 Faelle. Flip-Probe gefahren: Waechter aus actionConsumerArchive entfernt -> 2 rot, nach byte-genauer Wiederherstellung 14/14. Volle Reihe 1878/1878.
Der Marker-Kommentar ist nachgezogen: er behauptete, die sechs seien draussen, WEIL ihre Ruempfe die Kennung binden. Das gilt nicht mehr — sie bleiben draussen, weil ein 501-Weg kein Nutzen ist; wer die Sammel-Form braucht, implementiert sie mit Kennungen im Rumpf und aendert `availability` auf `many`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012LmDa2yAaQZtJrAss6EuZ8
Newpages
UCD-Schlitze landen am Inhalts-Element statt am Abschnitt
Der Klient tauscht dynamische Inhalte per getElementById. Eine gesetzte Block-Kennung ging bisher immer ans <section> — fuer einen UCD-Schlitz die falsche Ebene: der Tausch ersetzt dann das innerHTML des GANZEN Abschnitts samt Innen-Huelle und Knopf-Klassen.
blockIdPlacement() trennt beide Bedeutungen: ein normaler Sprungziel-Anker (#kontakt) bleibt unveraendert am Abschnitt, ein UCD-Schlitz wandert an das inhaltstragende Element — beim Knopf-Schlitz an den Knopf, sonst an die Inhalts-Huelle. h1-h6 stehen bewusst NICHT in der Liste: der Klient findet sie ueber den Element-Namen.
Bewusst KEIN neues settings-Feld: PageBlockSettingsSchema ist strict, ein Feld waere Fundament-Anteil. Getragen wird die Markierung von settings.id (Vertrag + Schema vorhanden). Grenze daraus: ein Block = hoechstens EIN dynamischer Schlitz.
Proben: 886 gruen in der pages-Reihe (11 neu). Flip gefahren — Kante entfernt: 5 rot, die beiden Rueckwaerts-Proben blieben gruen.
Fixucd
Mandanten-Riegel am dynamischen Inhalt + Schlitz-Setter nach Element-Art
Der UCD-Nachschlag fragte `UCDs.findOne({key})` ueber ALLE Mandanten hinweg. Der Schluessel reist offen in der Adresse (?ayoune_ucd=), `key` traegt keinen Eindeutigkeits-Riegel, und der zweite Zweig ({url,interest}) nimmt beide Werte vom Klienten — eine fremde Adresse genuegte fuer fremde Inhalte. Filter-Bau jetzt in tracking/ucdHelpers.ts, fail-closed (ohne _customerID kein Nachschlag, nie ein leerer Filter).
Zweiter Aufrufer ({url,interest} im Interessens-Handler) war entgegen der Brief-Annahme LIVE — vom Compiler gefangen, mit verriegelt.
idcadd.js: die Knopf-Schlitze setzten ausschliesslich .value. Der neue Seiten-Bauer zeichnet Knoepfe als <a class=ay-btn>, wo .value wirkungslos ist — ohne Fehler und ohne Warnung. ayoune_setSlot entscheidet ab jetzt nach Element-Art.
der Loeschantrag aus dem Portal bekommt einen Leser (W6, Art. 17)
Das Bewerber-Portal setzt erasureRequestedAt seit W3 und sagt im eigenen Kommentar, der Vollzug sei W6. Bis heute hatte das Feld KEINEN Leser: der Bewerber klickte, und danach geschah nie etwas.
Ein Antrag sticht die restliche Frist, aber NICHT die Schutzregeln — eine eingestellte Bewerbung und eine LAUFENDE bleiben unberuehrt (wer zuruecktreten will, nutzt withdraw; danach laeuft die Frist). Beide Faelle werden benannt gemeldet statt verschwiegen.
Antraege werden getrennt geholt und entdoppelt, damit der Bericht 'auf Antrag' von 'Frist abgelaufen' unterscheidet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newvoice
Anbieter-Wahl je Anruf statt eines festen Adapters fuer den Dienst
`stream.ts` erzeugte beim Modul-Laden EINEN `DeepgramSttProvider` fuer den ganzen Dienst; der `ICallAgent.sttProvider`-Feldwert wurde NIRGENDS gelesen. 'deepgram' war damit blosser Zufall — es war der einzige Wert, der je vorkam, nicht eine getroffene Wahl. Ein Agent mit `sttProvider: 'whisper'` bekam trotzdem Deepgram.
- Der Handler waehlt den Adapter jetzt je Anruf ueber `sttProviderFor(name)` aus dem Feldwert des Agenten. Das macht auch 'deepgram' erstmals zu einer echten Wahl. - `deps.stt` bleibt und SCHLAEGT die Wahl — das ist der Injektionspunkt der bestehenden 617 Proben, die damit unveraendert gruen bleiben. - Ohne Feldwert (und ohne Agenten am Anruf) bleibt es bei 'deepgram': die zwei heutigen Agenten verhalten sich unveraendert, keine Migration noetig. - Die Mandanten-Kennung reist bis zum Adapter durch — der Whisper-Weg loest seinen Schluessel aus dem Tresor DIESES Mandanten auf.
6 neue Proben; zwei Flip-Proben belegen, dass sie beissen (Feldwert ignorieren bzw. Mandanten-Kennung weglassen -> je genau die zugehoerige Probe faellt).
BEZUG: braucht @tolinax/ayoune-core mit `ISttOpenOptions._customerID`. Bis zu dessen Veroeffentlichung schlaegt der Bau fehl — das ist die sichere Richtung, kein stiller Schaden.
Newapplications
Recruiting-Kennzahlen + Fristen-Status an der Flaeche (W6 Reporting)
Time-to-Hire, Trichter je Stelle/Stufe, Absagequoten nach Kategorie — 13 Proben.
Gemessener Grund, warum das EIN Endpunkt ist und nicht drei Widget-Definitionen: Time-to-Hire ist die Differenz zweier Datumsfelder, der generische Widget-Motor kennt nur Akkumulatoren ueber EINEM Feld, und die Fluchttuer dataSource.pipeline liest allein der Prerender-Worker (nicht platform/states). Drei Kennzahlen ueber zwei Wege WAEREN das Parallel-System.
Flaeche: Loeschfrist + Anonymisierungs-Stempel als Spalten, Zeitraum-Filter, Schnellfilter Anonymisiert. actionsForEntity(Applications) jetzt verdrahtet — die Bedingung des frueheren Kommentars ist erfuellt, alle sechs Aktionen sind echt.
Gemessen statt gezaehlt: ein grep auf refuseUnimplementedAction liefert 11 Treffer, davon sind 8 Kommentare; die Messgroesse ist der IMPORT.
W2-Waechter mitgezogen und seine Positiv-Kontrolle auf applicants/merge umgehaengt — sonst belegten die sechs not.toContain nichts mehr.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newvoice
Whisper-Spracherkennung als echter stroemender Adapter
Der `WhisperSttProvider` war ein Platzhalter, der beim ersten Verbinden warf ("TODO post-W2") — ein Agent mit `sttProvider: 'whisper'` brach jeden Anruf ab.
ANBIETER-ENTSCHEIDUNG (gemessen, nicht stillschweigend): das rohe Whisper- Endpunkt ist Batch und kann `interim` per Bauart nicht liefern; die gehosteten Stapel-Beschleuniger (Groq/Fireworks/Replicate) puffern die volle Aeusserung; Selbstbetrieb verlangte einen eigenen Dienst samt GPU. Gewaehlt ist die stroemende Transkriptions-Betriebsart der OpenAI-Realtime-Schnittstelle — der einzige Weg, der `interim` bedient UND dessen Anbieter (`openai`) BEREITS im Zugangsdaten-Katalog des Fundaments steht.
- Ton-Format g711_ulaw: Twilios mu-law 8 kHz reist ohne Umrechnung durch. - Schluessel AUSSCHLIESSLICH aus dem Tresor des Mandanten, ausdruecklich OHNE Rueckfall auf eine Umgebungsvariable — anders als die Praezedenz `resolveElevenLabsKey`. Ein stiller Rueckfall rechnete den Anruf eines Mandanten ueber den Schluessel eines anderen ab. - `ISttOpenOptions` traegt dafuer `_customerID`/`requestingUserId` (optional, der Deepgram-Weg bleibt unberuehrt). - Fehlende Anbieter-Angaben (Guete, Zeitachse) bleiben `undefined` bzw. werden aus der Sitzungs-Uhr abgeleitet und als Naeherung kommentiert.
15 neue Proben; zwei Flip-Proben belegen, dass sie beissen (ENV-Rueckfall eingebaut -> genau die Tresor-Probe faellt; roher Rahmen statt base64 -> genau die Uebertragungs-Probe faellt), je ohne Kollateral.
Newmonitoring
Aufbewahrungs-Lauf fuer Bewerbungen (W6 DSGVO)
Ruft den hr-api-Endpunkt je Mandant (Dienst-Zeichen traegt den Mandanten, nie eine Abfrage — bei einem unumkehrbaren Loesch-Lauf die tragende Sicherung). Regel und Vollzug bleiben in hr-api, hier steht nur der Zeitgeber.
Meldet die zwei Faelle, in denen NICHT vollzogen wurde: Mandant ohne rechte-tragenden Nutzer (403 — sonst laufen seine Fristen fuer immer ab und der Waechter sieht gruen aus) und fehlgeschlagener Lauf. NICHT je abgelaufener Bewerbung — Fristen laufen ab, das ist der Normalfall.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newselfhost
eigene Abbild-Registry auf Hetzner statt Google Artifact Registry
CEO-Entscheid 2026-08-23 (Weg a) widerruft Weg a' vom 2026-08-06: eine Selbst-Hosting-Loesung darf zum Beziehen ihrer Abbilder nicht von Google Cloud abhaengen.
Neu: infrastructure/hetzner-registry-docker-images/ nach dem Muster des npm-Registry-Hosts — registry:3 + Caddy, htpasswd-Zugang, cloud-init als Quelle der Wahrheit, taegliche Sicherung, RESTORE.md.
Umgestellt: selfhost-release.mjs (Ziel jetzt zweiteilig, Quelle unveraendert GCP), selfhost-customer.mjs (htpasswd ueber SSH statt IAM-Dienstkonto), docker-compose.yml (65 Zeilen), SELFHOST-RELEASE-RUNBOOK.md neu geschrieben.
Proben 28 -> 32 gruen; Flip-Probe auf das alte Ziel-Muster faellt genau einmal. assertSafeHtpasswd weist -c ab (loeschte sonst jeden Zugang) — eine eigene Probe fing dabei, dass die erste Fassung jeden ENTZUG blockiert haette.
NICHT ausgerollt: die Anlage der kostenpflichtigen Maschine und der DNS-A-Eintrag liegen als operator-residual (high) vor.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LiqTwHE7HSdHBaWhhZ9jeZ
Newstates
Rueckstell-Weg DELETE /:state/user fuer die Nutzer-Override-Ebene
Die Nutzer-Ebene ersetzt den viewConfig vollstaendig, hatte aber keine Gegenrichtung: schon ein leerer Override ueberdeckte die Plattform-Vorgabe dauerhaft (live eingetreten bei reporting.executive, 2026-08-23).
- resetUserOverride in services/userOverrideReset (eingespritzte Abhaengigkeiten, ohne AY-Boot pruefbar), loescht erst und leert DANN beide Zwischenspeicher - Filter traegt _userID UND _customerID = Mandanten-Riegel - idempotent: zweiter Aufruf 200 mit deleted:false, leert trotzdem - 7 Proben mit Positiv-Kontrolle, Verdrahtungs-Waechter und vier Flip-Proben
Fixlogging
Betriebs-Protokoll des Arbeiters wieder sichtbar machen
Der Arbeiter war in `kubectl logs` vollstaendig stumm — null `ay.logger`-Zeilen, obwohl er arbeitete (Gegenprobe: `domain-health` fuehrt abgeschlossene Auftraege).
Gemessen sind ZWEI unabhaengige Schwellen, und beide mussten fallen:
1. `logger.level: 'info'` (Stufe 3) ist die Schwelle des WURZEL-Protokollierers und greift VOR jedem Ausgabeweg. `success` ist Stufe 5 — `Domain Workers started` und die Abschluss-Zeile des Abgleichs erreichten damit WEDER die Standardausgabe NOCH die Sammlung `ayounelogs` (dort 0 Zeilen der Stufe `success` fuer diesen Dienst, waehrend die Flotte insgesamt 83.620 fuehrt). 2. Der Ausgabeweg auf die Standardausgabe steht in Kubernetes seit dem 2026-07-31 per Vorgabe auf `error` (`resolveConsoleLevel`, Buendel 20/A) — eine bewusste Kosten-Abwaegung, kein Defekt.
Neu: `level: 'success'` + `consoleLevel: 'success'`. Die Schwelle ist eine OBERGRENZE, also faellt `verbose` (8) heraus — genau richtig, denn das ist hier die Zeile JE AUFTRAG und damit der Mengen-Treiber. Aufkommen rund 75 Zeilen je Stunde.
WIDERLEGT: die Boot-Reihenfolge ist NICHT die Ursache. Der `ay.on('ready')`-Rumpf laeuft zwar vor `initLogger`, aber der Boot-Vor-Protokollierer schreibt in diesem Fenster auf Stufe `silly` auf die Standardausgabe. Ein Verschieben nach hinten wuerde die Stille VERSTAERKEN. Kommentar in server.ts haelt das fest.
Kern UNBERUEHRT — der Befund gilt fuer jeden Dienst in Kubernetes und gehoert in einen Fundament-Platz, nicht in diesen Fix.
Newselfhost
support-center-api aus dem support-Profil entfernen (CEO-Entscheid Weg a)
Der Dienst ist seit 2026-06-16 RETIRED, seit 2026-07-08 aus dem Cluster entfernt und seine Auslieferungs-Kette seit 2026-07-25 abgeschaltet. Das Selbst-Hosting-Buendel bot ihn unter --profiles support weiter an.
Auskommentiert statt nur die Profil-Zeile gestrichen: ein Dienst ohne profiles: ist in Compose im Vorgabe-Satz und startet bei jedem up (an Compose v5.3.1 gegengeprueft); ausserdem zaehlt parseComposeImages profil-blind, das Gate 2->1 pausierte Ketten waere sonst unerfuellbar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LiqTwHE7HSdHBaWhhZ9jeZ
Newmigrate
uptime-kpis-to-dailystats — einmalige Uebertragung der Uptime-Tageszeilen
globalkpis(typ:"check-uptime-daily") -> checkdailystats, CEO-Entscheid 2026-08-23 Weg B. Rein additiv: die Quelle wird NICHT angefasst.
🔴 Der Rechenweg wird aus platform/stats-generator/dist GELADEN, nicht nachgebaut. Zwei Kopien derselben Feld-Zuordnung waeren eine Drift-Quelle, und die gefaehrliche Richtung ist hier eindeutig: die Altzeile zaehlt ERREICHBARE (`count`), der Vertrag FEHLGESCHLAGENE (`failed`) — vertauscht steht die Verfuegbarkeit auf dem Kopf. Fehlt das dist, bricht der Lauf mit einer klaren Ansage ab, statt auf eine zweite Umsetzung auszuweichen.
Ein Nachhol-Lauf des Verdichters kann das NICHT ersetzen: checkresults traegt 14 Tage Verfall, alles Aeltere existiert nur noch als Kennzahl-Zeile.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sad6An2tuxWKtd9zHcVXqs
Newkarriere
Bewerbung schreibt die Dauerform statt einer FormSubmission
Die Karriere-Strecke legt seit dieser Aenderung `Applicants` + `Applications` an, nicht mehr eine `FormSubmission` mit freiem `formdata._hrJob`. Der Uebergangs-Traeger war als solcher gebaut (W4: die zwei Entitaeten existierten damals gemessen nicht); seit Kaskade 61 gibt es sie.
- Prozess-Aufloesung nach der Kette Stelle -> Mandant -> Plattform, mit Pruefung auf `entityCollection` (ein `Tasks`-Prozess darf eine Bewerbung nie fuehren). - Anfangs-Status kommt AUS dem Prozess, nie aus einer festen Zeichenkette. - Einwilligung ist ein Pflichtfeld; der Wortlaut wird als `consentText` festgehalten und stammt aus derselben Funktion wie das Formular. - Bewerber-Dedupe ueber die kleingeschriebene Adresse (Riegel `{_customerID,email}`; das Portal sucht ebenso). - Doppel-Bewerbung: Vorpruefung UND E11000-Fang, beide unabhaengig belegt. - Der CRM-Lead entfaellt bewusst (W0 §3a: HR-Rechtekreis, nicht Kundenpflege).
Verify: tsc 0, volle Suite 1682/1682, Karriere-HTTP 24/24. Drei Flip-Proben beissen punktgenau (entityCollection, Kleinschreibung, Einwilligung); eine vierte belegt, dass der E11000-Fang allein traegt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012CLtxDVijYPDaZcJryaWjK
Fixroute
ein eigenstaendiger Plattform-Host routet auf seine Flaeche, nicht auf die Website
Ein `pwa`-Eintrag OHNE `_websiteID` hat keine Geschwister. Die Zuordnung kannte nur die Geschwister-Form und lieferte fuer ihn GENAU die eine Auffang-Regel auf die consumer-app — er bekam Zertifikat, Tor und Route, war erreichbar und zeigte die Marketing-Website unter der Anmelde-Adresse. Empirisch belegt am 2026-08-23:
Diese Form ist der Regelfall, sobald ein Wiederverkaeufer gar keine Website hat — also genau die Lage, in der Auftrag 4 (Beweis an EINEM Host) bewiesen werden soll.
- `hostRouting`: neuer Eingang `selfTarget`; ist er gesetzt, wandert **der Auffang selbst** auf die Plattform-Flaeche (bewusst keine `match`-Regel davor: der Platzhalter `*.<domaene>` steht in denselben `hosts` und fiele sonst weiter auf die Website), und `rootTarget` wird ignoriert (die Umleitung waere eine Schleife). - `platformHostGate`: `platformHostTargetFor()` gibt das ZIEL statt nur „ja"; `isEnabledPlatformHost` ruft sie — EINE Implementierung, nicht zwei. - `provisionDomain` leitet den Schluessel aus dem Tor ab und reicht ihn durch.
Rueckwaerts-Identitaet festgenagelt: ohne `selfTarget` ist jede Bestands-Form byte-gleich zu vorher (Probe ueber vier Formen), und eine `consumer`-Domaene bekommt den Schluessel per Flip-Probe nachweislich NICHT.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018KTTjQDtPqzMCcA9pm8jND
Fixpwa
Hinweistexte im Panel ueberlappten das naechste Feld
Am laufenden Bildschirm gesehen: Material reserviert mit der Vorgabe EINE Zeile fuer den Hinweis und rechnet mehrzeilige Hinweise nicht ein. Die Hinweise sind hier bewusst laenger als eine Zeile, weil sie echte Fallen benennen (ein Punkt-Pfad sperrt die Kante dauerhaft; der Joker ist nur als Ausgang zulaessig) — sie standen deshalb im naechsten Feld.
subscriptSizing="dynamic" an allen zehn Feldern + etwas Abstand.
⚠ Vierter Treffer der Backtick-Falle an einem Tag, diesmal im neuen CSS-Kommentar selbst. Der Warnhinweis stand schon zwei Zeilen darueber.
Proben 33/33. build:stage gruen.
Fixpwa
kennungsloser Einstieg — das Platzhalter-Segment traf keinen Zustand
Live gemessen (2026-08-23): der Listen-Einstieg zeigte auf `/hr/applicationprocesses/edit/vorgabe`. Die Adresse wechselte, die LISTE blieb stehen, und es erschien „State hr.applicationprocesses.edit.vorgabe nicht registriert".
Ursache: `PageService.urlToView` nimmt bei VIER Segmenten das letzte nur weg, wenn es eine ObjectId ist (`page.service.ts:1999`) — ein Platzhalter bleibt also stehen und wird Teil des gesuchten Zustands-Namens. Die Drei-Segment-Form loest unveraendert auf.
- Route traegt wieder BEIDE Kinder. Das leere heisst hier NICHT „anlegen" (ein Prozess wird uebernommen, `hideCreate` bleibt), sondern „welchen Prozess?" — die Liste kennt die Kennung der Plattform-Vorgabe nicht, der Editor loest sie ueber `GET /applicationprocesses` auf und ersetzt die Adresse. - Route-Probe auf beide Formen gedreht, Konstante umbenannt (`EINSTIEG_OHNE_KENNUNG`), Begruendung an allen drei Fundstellen.
Proben 33/33. build:stage gruen.
Fixhr
Einstieg OHNE Kennung — das Platzhalter-Segment traf keinen Zustand
Live gemessen (2026-08-23, Mandant tolinax): der Einstieg zeigte auf `/hr/applicationprocesses/edit/vorgabe`. Die Adresse wechselte, die LISTE blieb stehen, und es erschien „State hr.applicationprocesses.edit.vorgabe nicht registriert".
Ursache: `PageService.urlToView` nimmt bei VIER Segmenten das letzte nur weg, wenn es eine ObjectId ist (`page.service.ts:1999`). Ein Platzhalter bleibt also stehen und wird Teil des gesuchten Zustands-Namens. Die DREI-Segment-Form (`/hr/applicationprocesses/edit`) loest unveraendert auf; die PWA-Route traegt dafuer wieder ein leeres Kind — das heisst hier NICHT „anlegen" (der Prozess wird uebernommen, `hideCreate` bleibt), sondern „welchen Prozess?".
Reihe gruen: 41 Proben der zwei betroffenen Reihen.
Voller Modul-Lauf `codegen routes --module crm`. Der Lauf zieht DREI angesammelte Vorlagen-Fixe nach, die crm im Feldzug `codegen-suchfelder-vorlage-ohne-konsumenten-nachzug` (11 Module / 472 Handler) ausgelassen hat:
1. Freitext-Suche liest `searchableFields` statt hartkodiert name/title/subject. Fuer Consumers heisst das first_name|last_name|email|phone|company — KEIN Consumer-Dokument traegt ein `name`-Feld, `GET /consumers?q=` fand also niemanden. 2. Lese-Vorlagen klassifizieren Aufrufer-Fehler als 4xx statt pauschal 500 und protokollieren sie als `warn` statt `error`. 3. 19 Router: `PUT /many` prueft `edit` ODER `massEdit` statt `.new` (Korrektur — eine Mengen-AENDERUNG verlangte bis hierher das ANLEGE-Recht), `DELETE /many` prueft `delete` ODER `massDelete` (rein additiv).
15 Charakterisierungs-Proben umgekehrt, je mit Begruendung im Quelltext, WAS sich geaendert hat und WARUM die alte Erwartung die defekte war. Sechs Negativ-Kontrollen ergaenzt, damit der 4xx-Zweig nicht mit einem pauschalen 4xx trivial erfuellbar ist.
BEFUND, der ueber den Nachzug hinausgeht — routerConsumersCollectionActions.ts: Die Datei war hand-geschrieben OHNE Haertungs-Marker und wurde vom Lauf ueberschrieben. Uebernommen sind zwei echte Behebungen, eine dritte Aenderung ist zurueckgenommen:
- UEBERNOMMEN: die zehn `collection`-Aktionen verlangten `crm.consumers.<name>` ohne das Segment `.actions.`. `registerModuleRights` seedet ausschliesslich `crm.consumers.actions.<name>` — die alte Zeichenkette wurde NIE vergeben. Dieselbe Fehlerklasse wie die 19 Routen aus §3 des Feldzug-Handoffs. - UEBERNOMMEN: der Sammel-Router war im Eltern-Router GAR NICHT eingehaengt (routerConsumers.ts kannte nur `/:id/actions`) — die zehn Wege waren toter Code hinter einem toten Recht. Jetzt unter `/actions` eingehaengt, VOR den CRUD-Wegen. - ZURUECKGENOMMEN + gehaertet: der Lauf haengte zusaetzlich die SECHS `availability: 'both'`-Aktionen dort ein (archive, ignore-in-merge, blacklist, add-to-list, assign-segment, export). Alle sechs Ruempfe lesen `req.params.id` und schreiben mit `{_id: entityId, _customerID}`. An der Sammel-Adresse ist entityId `undefined`, und Mongoose ENTFERNT einen undefined-Wert aus dem Filter (an mongoose 8.24.4 gemessen: `{_id: undefined, _customerID: X}` castet zu `{_customerID: X}`) — der Schreibvorgang traefe einen BELIEBIGEN Datensatz des Mandanten, ohne Fehler. Haertungs-Marker gesetzt, der Waechter meldet die Datei ab jetzt statt sie zu ueberschreiben (3 -> 4 gehaertete Dateien im Modul).
Verify: 1864/1864 Proben in 106/106 Reihen (Vorher-Wert 1858/1858, +6 Negativ-Kontrollen), tsc Exit 0, zweiter und dritter Erzeuger-Lauf idempotent (0 Abweichungen, mit Positiv-Kontrolle), die zehn hand-verdrahteten Aktions-Routen in routerConsumersActions.ts unveraendert vorhanden.
Zwei Befunde vom AUSGEBRACHTEN Bildschirm, nicht vom Entwurf.
1) Joker-Kanten (from: '*') liefen quer ueber die ganze Leinwand — bei der Plattform-Vorgabe drei Linien (reject/withdraw/hold), die alles kreuzen, was dazwischen liegt. Sie haben per Bauart keinen Ausgangs-Knoten; ein kurzer Stummel links am Ziel sagt dasselbe („kommt von ueberall") und kreuzt nichts. Probe mit Gegenprobe: eine NORMALE Kante darf lang bleiben, sonst waere der Graph nicht mehr verbunden.
2) --mat-app-background-color GIBT ES NICHT im ausgelieferten Stil (am gebauten styles-*.css gemessen) — der Rueckfall #fff schlug also immer durch, und auf dem dunklen Thema standen weisse Karten auf dunklem Grund. Ersetzt durch die M3-System-Tokens, die real existieren (surface-container-high / on-surface / outline-variant / primary).
⚠ Dritter Treffer der Backtick-Falle an einem Tag: ein Backtick in einem CSS-Kommentar INNERHALB der styles-Vorlage beendet das Template-Literal, und der Uebersetzer zeigt auf eine Zeile weit dahinter. Der Warnhinweis steht jetzt in beiden Vorlagen dieser Datei.
Proben 33/33.
Fixauth
NUL-Byte aus login-brand.ts, Rueckfall-Vergleich ohne Sentinel
Der Rueckfall-Wert von `hostnameOf` fuer den eigenen Host war ein Sentinel-String, der beim Schreiben zu einem NUL-Byte wurde. Folge: git fuehrte die Quelldatei als BINAER (`Bin 0 -> 11342 bytes`) — kein Diff, keine Durchsicht, obwohl Bau und Proben gruen blieben. Gefunden beim Pflicht-Blick auf `git show --stat` nach dem Commit, nicht vom Bau.
Der Sentinel ist zugleich sachlich ersetzt: ein leerer eigener Host darf gar nicht vergleichen, sonst waere er ein Treffer fuer jeden ebenso unlesbaren Host. Statt eines Wertes, der nie trifft, jetzt eine ausdrueckliche Bedingung.
Rueckweg zur Anmeldung traegt die Marke des Hosts statt aYOUne
`redirectToLogin` schickte einen Nutzer, dessen Sitzung ablaeuft, auf `${loginUrl}/ayoune` — der Marken-Slug stand fest im Quelltext. Auf einem weiss-etikettierten Host (`app.kunde.de`) verschwand damit mitten in der Nutzung die Marke des Wiederverkaeufers, ohne sichtbaren Fehler.
Der Slug wird jetzt aus dem HOST abgeleitet, in derselben Reihenfolge wie in `clients/auth`: Zwischenspeicher → Plattform-Host → Abruf (`GET /host/:host` auf reseller-api, seit 2026-08-23 gebaut). Faellt der Abruf aus, gilt `ayoune` — fail-safe, nicht fail-closed. Die Rueckfall-Regel `login.ayoune.app/<slug>` bleibt unangetastet; es wird nichts umgeleitet und nichts ersetzt.
ZWEI gemessene Abweichungen gegenueber `clients/auth`, beide notwendig: (a) der Apex kommt aus `loginUrl`, NICHT aus `frontendUrl` — der traegt hier `pwa.ayoune.app`, also eine Flaeche UNTER dem Namen; dieselbe Ableitung haette `app.ayoune.app` zum Fremd-Host gemacht und den haeufigsten Rueckweg einen Abruf gekostet. (b) die Etiketten-Liste traegt zusaetzlich `pwa`.
Messung (Schritt 1 des Auftrags): genau EINE fest verdrahtete Fundstelle. Zwei weitere sehen so aus, sind es aber nicht (`${startupUrl}/ayoune` ist `/:product`, kein Marken-Slug), und die Lib `@tolinax/ayoune-auth-client` leitet nackt auf `loginUrl` ohne Slug — den Fall deckt der Host-Riegel der Anmelde-Oberflaeche ab.
19 Proben (`login-brand.spec.ts`) mit Flip-Probe `login.protos.ayoune.app` (Reseller-Domaene unter dem Plattform-Namen ist KEIN Plattform-Host), Byte-Gleichheit auf der Plattform-Marke und drei Negativ-Kontrollen (404 / Netzfehler / Antwort ohne slug → `ayoune`, nie `undefined`).
Fixpwa
Leer-Zustand zeigte den Anlege-Knopf trotz hideCreate + Platzhalter-Weg
Zwei Befunde am Bewerbungsprozess, beide ueber ihn hinausreichend.
1) `resolveCreateCtaLabel` las `hideCreate` NICHT — der Leer-Zustand zeigte seinen Anlege-Knopf auch dort, wo der Zustand das Anlegen ausdruecklich unterdrueckt. Die drei anderen Leser respektieren ihn laengst (`create-button-visibility.ts:59`, `primary-action.ts:41`, `base-page.component.html:245`); allein die Beschriftung nicht, weil sie nur `name_singular` brauchte. Am Bewerbungsprozess haette der Knopf einen leeren `Workflows`-Datensatz ohne Stufen erzeugt — genau den kaputten Prozess, gegen den die Server-Pruefung sieben Riegel faehrt. Gemessene Reichweite: 6 Zustaende tragen `hideCreate: true`. Flip-Probe: Riegel entfernt faerbt genau 1 Probe rot, die Gegenprobe bleibt gruen. ⚠ `AyounePageViewConfig` DEKLARIERT `hideCreate` gar nicht (TS2345 „has no properties in common") — das Feld existiert nur zur Laufzeit, `tableState()` spreizt es hinein. Daher die Index-Signatur.
2) Platzhalter-Weg `edit/vorgabe`: die Liste kennt die Kennung der Plattform-Vorgabe nicht (Aggregations-Weg blendet sie per Bauart aus), also loest der Editor sie ueber die Sammel-Liste auf und ersetzt die Adresse. Die EIGENE Kopie schlaegt dabei die Vorgabe.
Nebenbei: zwei Proben in `no-content.component.spec.ts` erwarteten seit dem 2026-08-12 ein `Create `-Praefix, das an jenem Tag ausgebaut wurde — sie waren seither dauerhaft rot (baseline nachgemessen: 2 rot ohne jede eigene Aenderung) und sind auf das gemessene Verhalten nachgezogen.
die Prozess-Flaeche war eine Sackgasse — Einstieg in den Editor
Am laufenden Programm gemessen (Mandant tolinax): die Liste zeigt „0 von 0", weil der Aggregations-Weg die Plattform-Vorgabe per Bauart ausblendet (`workflows` steht in ACKNOWLEDGED_NON_CATALOGS). Es gibt also keine Zeile zum Anklicken — und „Uebernehmen" liegt IM Editor, den man ohne Zeile nicht erreicht. Ein Mandant ohne eigene Kopie kam ueber die Oberflaeche NIE zu einer; der Leer-Zustand riet ihm woertlich zu einem POST-Aufruf.
- Listen-Aktion `link` auf `/hr/applicationprocesses/edit/vorgabe`. Der Platzhalter statt einer Kennung ist Absicht: die Flaeche KENNT die Kennung der Vorgabe nicht — derselbe Grund, aus dem `actions/adopt` auf der Sammel-Ebene liegt. Der Editor loest ihn ueber `GET /applicationprocesses` auf und ersetzt die Adresse durch die echte Kennung. - Zwei Proben inkl. Gegenprobe („der Einstieg traegt keine Kennung").
Die Konfigurations-Flaeche aus W2 war nicht bedienbar: der Formular-Renderer kennt keinen Objektlisten-Typ. Statt eines neuen Feldtyps im Fundament (Weg a) haengt jetzt ein zweck-gebauter Editor als Komponenten-Override an der Standard-Route (Weg b) — CEO-Regel 2026-08-22 plus zwei gemessene Gruende: transitions[].from/to verweisen auf die Geschwister-Liste statuses[], und kein Feldtyp des Vertrags bindet eine Auswahl an eine Liste im selben Modell.
- registerStates: hr.applicationprocesses.edit (pageType component, endpoint applicationprocesses, keine kuratierten Aktionen — sie reiten im zentralen Aktions-Slot), Zeilenklick der Liste kuratiert statt abgeleitet - Proben: 9 neue Flip-/Gegenproben, Katalog-Waechter 25 -> 26 - Reihe gruen: 52 Suiten / 1093 Proben
Newreseller
Marke je HOST aufloesen — `GET /host/:host` neben `GET /slug/:reseller`
Auftrag 3 (Teil 1 von 2) des Briefes `white-label-anmelde-host-form-b-umsetzen`.
Befund der Messung vom 2026-08-19 (Station 8): die Anmelde-Oberflaeche loest ihre Marke ausschliesslich aus dem PFAD auf (`/:reseller` -> `GET /slug/:reseller`), und `aYOUneResellers.domain` wird von NIEMANDEM als Host-Zuordnung gelesen. Auf einem eigenen Anmelde-Host (`login.kunde.de`) gibt es kein Pfad-Segment — der Besucher landet auf der aYOUne-Marke, obwohl er die Domaene seines Wiederverkaeufers aufgerufen hat.
Die neue Route ist das fehlende Gegenstueck, mit byte-gleicher Feld-Auswahl wie `/slug/:reseller`, damit die Oberflaeche beide Wege gleich behandeln kann. Oeffentlich gemountet wie `/slug` und aus demselben Grund: gefragt wird VOR jeder Anmeldung, ausgeliefert wird nur, was ohnehin auf der Anmelde-Seite steht.
🔴 An den Freischalt-Schalter gebunden (CEO-Entscheid 3): der Filter verlangt `platformHostEnabled: true`. Ohne die Bindung antwortete die Route auch fuer Wiederverkaeufer, die lediglich eine Domaene EINGETRAGEN haben — und liefe damit den beiden anderen Toren der Kette zuwider, die genau daran haengen. Der Schalter steht IM Filter, nicht in einer Pruefung danach.
⚠ 404 statt 403 bei fehlender Freigabe: die Route ist oeffentlich, ein unterscheidbarer Fehler waere eine Aufzaehlungs-Hilfe.
Die reine Regel liegt in `lib/platformHostLabels.ts`, nicht in der Route — sie ist die probenfaehige Haelfte (die Test-Konfiguration deckt `src/lib/**`) und die Haelfte, die spaeter in den Kern wandert.
🔴 DRITTE Kopie der Etiketten-Liste, bewusst benannt: dieselbe Liste steht in `domain-worker/shared/hostRouting.ts` (welcher DIENST) und in `auth/lib/platformHostOrigins.ts` (welche HERKUNFT). Drei Kopien einer Regel laufen auseinander — dieselbe Erfahrung, die `MANUALLY_MANAGED_ZONES` in den Kern gehoben hat. Der Hub ist als eigener Auftrag angemeldet.
🔴 LATENTER BEFUND, in einer Probe festgenagelt: mit `mongoose.set('strictQuery', true)` faellt `platformHostEnabled` aus dem Filter, und die Route lieferte die Marke JEDES Wiederverkaeufers mit passender Domaene aus — eine stille Weitung, kein Fehler. Heute tritt das NICHT ein, und das ist gemessen statt angenommen: Mongoose-8-Vorgabe ist `false`, und weder core noch models noch dieser Dienst setzen die Option. Die Probe faellt, sobald das jemand aendert.
Proben: neue `lib/__tests__/platformHostLabels.test.ts`, Reihe 54/54 (vorher 45). Sicherheits-Grenzen mit Umkehr- und Positiv-Kontrolle: unbekanntes Etikett, zwei Ebenen tief, `login.de` (das sonst `de` als Kandidat ergaebe).
Newdomains
Plattform-Hosts bereitstellen — `pwa` aufgenommen, aber nur hinter dem Freischalt-Schalter
Zweite Haelfte von Auftrag 1 des Briefes `white-label-anmelde-host-form-b-umsetzen`. Die Route kann einen Plattform-Host seit 2026-08-21 (`shared/hostRouting.ts`); was fehlte, war der getrennte Freischalt-Schalter aus CEO-Entscheid 3 vom 2026-08-19. Er steht seit `aYOUneResellers.platformHostEnabled` (interfaces 2026.452.0) im Vertrag, also ist die Reihenfolge des Auftrags jetzt erfuellt.
`SERVABLE_SERVICE_TYPES` traegt jetzt `pwa` — und das ist NUR zusammen mit dem neuen `shared/platformHostGate.ts` richtig. Die Liste ist fuer `pwa` ab jetzt eine VORAUSWAHL, kein Praedikat; erlaubt ist ein Host nur, wenn BEIDES gilt: 1. sein Wiederverkaeufer traegt `platformHostEnabled: true` (die Entscheidung), UND 2. sein Name liegt als bekanntes Etikett (`login.`/`app.`, genau EINE Ebene) direkt unter dessen `domain` (die Zuordnung).
Das Tor steht an VIER Stellen, und die vierte ist die massgebliche: - `startup/reconcileDomains.ts` — kein Auftrag, der ohnehin abbricht - `jobs/checkDomainHealth.ts` — sonst Dauer-Alarm alle fuenf Minuten fuer einen Zustand, der die richtige Antwort ist - `jobs/provisionDomain.ts` (Geschwister) — 🔴 ohne diese Stelle waere die Negativ-Kontrolle des Auftrags NICHT erfuellt: `resolveFeatureHosts` laedt art-blind, ein `app.kunde.de` reiste deshalb als Geschwister einer ganz normalen `consumer`-Domaene als SAN ins Zertifikat UND als Regel in die Route — ohne eigenen Auftrag und ohne Freischaltung. Genau der Befund "Tor und Zertifikat tragen einen pwa-Host BEREITS" der Messung vom 08-19. - `jobs/provisionDomain.ts` (Auftrag) — fail-closed, egal wie ein Auftrag entstanden ist (Abgleich, `verifyDomain`, Wiederholung von Hand). Ein Ausschluss nur am Abgleich waere eine Zusage mit Luecke.
Die Ladung des Datensatzes wandert dafuer VOR den docker-compose-Zweig — jener setzt `tls.provisioned` und beendet den Auftrag, ohne den `try` je zu betreten.
Fail-closed in jeder Richtung: keine Datenbank, kein Treiber, ein Wurf, ein leeres Feld ergeben die LEERE Menge und damit keinen Plattform-Host — Byte fuer Byte das Verhalten von vorher. Der schlimmste Ausgang eines Ausfalls ist, dass ein freigeschalteter Host wartet, nie dass ein nicht freigeschalteter einen bekommt.
⚠ ZWILLING, bewusst benannt: `platform/auth/src/lib/platformHostOrigins.ts` stellt dieselbe Frage fuer die Herkunfts-Erlaubnis. Zwei Kopien einer Regel, die entscheidet OB eine Domaene angefasst werden darf, laufen auseinander — dieselbe Erfahrung, die `MANUALLY_MANAGED_ZONES` in den Kern gehoben hat. Der Hub ist als eigener Auftrag angemeldet (er braucht einen Fundament-Platz, den dieser Brief nicht haelt).
Proben: neue `shared/platformHostGate.test.mjs` 35/35 (beide Richtungen am Auftrag selbst: OHNE Schalter kein Zertifikat/Tor/Route, MIT Schalter alle drei; Fail-closed bei Wurf; Sicherheits-Grenze `app.boesekunde.example` gegen `kunde.example`). `servableServiceTypes.test.mjs` mitgezogen und um eine Flip-Probe erweitert (25/25) — die Behauptung zu `pwa` wurde UMGEDREHT statt gestrichen, damit die Zwei-Stufigkeit sichtbar bleibt. Volle Reihe exit 0.
Newcoord
`foundation-slot` wird kanonischer awaiting-Wert — die Etiketten-Sackgasse schliesst
DER BEFUND: fuer "wartet auf einen freien Fundament-Platz" gab es KEINEN tragfaehigen Wert, und beide Auswege endeten im Ausschluss. - `foundation-slot` lag ausserhalb von AWAITING_ENUM -> jede Automatik uebersprang ihn fail-closed, ohne Fehler und ohne Spur. - `deferred` liegt im Enum, fehlt aber in BATCH_READY_AWAITING -> Kaskaden-Kollektor UND Grant-Waehler sind blind dafuer. Gemessen 2026-08-22: 54 blockierte Briefe warten auf den Platz, verteilt auf VIER Etiketten (32 dependency, 9 none, 9 deferred, 1 trigger). Ein Brief dokumentiert den vergeblichen Wechsel woertlich: "war `foundation-slot` - ein Wert AUSSERHALB von AWAITING_ENUM ... `deferred` ist der etablierte Wert" (knowledgebase-eigene-ki-aktionen-je-sammlung, /resync 2026-08-18). Jemand tauschte unsichtbar gegen unsichtbar.
DIE SEMANTIK KANNTE DEN WERT LAENGST: `awaitingKind()` gibt fuer ihn seit jeher die Klasse 'foundation-slot' zurueck (trigger-conditions.mjs:200) - nur das SYNTAX-Tor liess ihn nicht zu. Diese Aenderung schliesst genau diese Luecke, sie erfindet nichts Neues.
GEAENDERT (drei Listen, bewusst nicht vier): - AWAITING_ENUM (Quelle der Wahrheit) + AWAITING_ENUM_MIRROR (Spiegel) -> Wert erlaubt - BATCH_READY_AWAITING -> Kollektor und Grant-Waehler sehen ihn - NICHT AWAITING_CLAIMABLE: der Brief bleibt gesperrt, er wartet ja wirklich - 🔴 NICHT PROMOTE_RESOLVES_AWAITING: erst so gebaut, dann ZURUECKGENOMMEN. Der Drift-Waechter (coord-promote-unresolved-gate 5b) verbietet jede Erweiterung ausdruecklich, weil damit das Loch wieder aufrisse, das die awaiting-Sperre geschlossen hat (purchase-approval, 3x geclaimt / 5 Rueckgaben). Folge, im Protokoll dokumentiert: der Promote WARNT laut, dass der Wert stehen bleibt - wer den Platz gewaehrt, setzt `awaiting: none` mit.
WAECHTER-TESTS NACHGEZOGEN — und das war der heikle Teil: DREI Testreihen fuehrten `foundation-slot` als BEISPIEL fuer einen erfundenen Wert. Sie zielten auf die AD-HOC-ERFINDUNG, nicht auf das Konzept; sie sind auf `pl-grant` umgestellt (der 2026-08-19-Neufund, weiterhin ungueltig) und um Positiv-Proben ergaenzt, die die Aufnahme festnageln.
Verify: awaiting-canon-guard 17/17 · promote-unresolved-gate + awaiting-guard 25/25 · volle Reihe 4804/4810. Die 6 verbleibenden Fehlschlaege sind VORBESTEHEND — gegen den gestashten sauberen Baum gemessen, dort dieselben 6 (394/930/931/1234/1245/4200).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Drei Aktionen an der Bewerbung, alle drei mit dem AGG-/Art.-22-Riegel BAULICH statt nur im Prompt:
POST /applications/:id/actions/ai-screen -> schreibt aiScreening POST /applications/:id/actions/ai-reply-draft -> liefert nur zurueck POST /applications/:id/actions/ai-interview-questions -> liefert nur zurueck
- redactProtectedAttributes() entfernt die AGG-Traeger (Geburtsdatum, Anrede, Anschrift, Staatsangehoerigkeit, Religion, Behinderung, Lichtbild) VOR dem Modell-Aufruf. Was nicht mitreist, kann nicht gewichtet werden. - filterInadmissibleQuestions() wirft unzulaessige Gespraechsfragen aus dem ERGEBNIS und WEIST SIE AUS statt sie still zu schlucken. - Eine unlesbare Modell-Antwort wird zum Fehler, nie zu score 0 — ein Rueckfall saehe aus wie die Bewertung "ungeeignet". - Keine der drei Aktionen ruft transitionEntity oder versendet etwas; nur ai-screen schreibt, und nur das Feld aiScreening.
Alle drei Lesezugriffe (Bewerbung, Stelle, Bewerber) mandanten-gescopt einzeln. Rechte kaskadenfrei ueber additionalRights, drei getrennte.
66 neue Proben mit Negativ- UND Positiv-Kontrollen; die Art.-22-Strukturprobe ist kommentar-bewusst (der erste Wurf fiel an der eigenen Kopfzeile). Volle Reihe 1054/1054, Bau gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012CLtxDVijYPDaZcJryaWjK
CEO 2026-08-23: `JobOffers` IST Markt-BEOBACHTUNG (Job-Scraper-Zufluss), NICHT der Traeger eigener Stellenangebote. Damit ist die einzige offene Produkt-Frage dieses Briefes beantwortet und der `joboffers`-Teil faellt ENDGUELTIG aus dem Umfang — die fuenf Aktionen (send/accept/reject/counter-offer/expire) brauchen einen EIGENEN Traeger, das ist ein Folge-Brief (neue Entitaet ⇒ Fundament), nicht dieser hier.
Der Entscheid deckt sich mit dem am 2026-08-04 gemessenen Bestand, den der Brief selbst fuehrt: 22 leere Stubs, KEIN `_customerID`, und die Form (`source`/`sourceId`/`applyLink`/ `descriptionHTML`) beschreibt ein eingelesenes FREMDES Angebot.
Drei Folgeaenderungen, damit der Brief nicht auf toten Praemissen stehen bleibt: - awaiting `ceo-decision` → `none` (nichts Menschliches mehr offen) - trigger `BLOCKED-ON-CEO-DECISION` → READY samt Begruendung - claim_paths interfaces+models (Altlast, Fundament seit Kaskade 28 geliefert) → die drei echten Ziel-Repos, AUS DER REGISTRY ABGELEITET statt geraten: Vacations→hr.js, SupplierAgreements→purchase.js, Documents→crm.js; Umkehrprobe mit erfundenem Plural 0.
Danach gemessen: der Brief meldet nur noch NOT-READY:paths gegen eine LAUFENDE Sitzung (bewerbermanagement-w4 haelt domains/hr/api) — ein echter, transienter Konflikt statt einer toten Praemisse. Er loest sich, wenn jene Sitzung endet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixvalidator
Zeilenenden zurueck auf LF — und diesmal die URSACHE benannt
Der Vorgaenger-Commit drehte die Datei von LF auf CRLF und zeigte dadurch 8939 geaenderte Zeilen statt der echten 139. Zweiter Fall derselben Klasse in dieser Sitzung (der erste in domains/cms/api, drei Commits lang).
🔴 DIE URSACHE, die beim ersten Mal noch nicht benannt war: Pythons TEXT-Schreibmodus. `io.open(p, "w", encoding="utf-8")` uebersetzt auf Windows jedes `\n` beim SCHREIBEN nach `\r\n` (newline=None => os.linesep). Wer eine Datei so einliest und zurueck- schreibt, dreht ihre Zeilenenden — auch wenn er nur drei Zeichen geaendert hat.
Abhilfe fuer den naechsten Bearbeiter, in dieser Reihenfolge: 1. Zeilenenden der Datei VOR dem Edit messen: git show <ref>:<datei> | ... 2. Im BINAER-Modus lesen und schreiben (open(p,"rb") / open(p,"wb")) ODER beim Text-Schreiben newline="" mitgeben. 3. VOR dem Push git show --stat lesen: eine vierstellige Zeilenzahl bei einer kleinen Aenderung ist immer dieses Problem.
⚠ Es gibt hier KEINE .gitattributes, also keine Repo-weite Politik — jede Datei traegt ihren eigenen Stand. Diese hier ist LF (8939 Zeilen), pauschales "normalisieren" ist deshalb genauso falsch wie das Drehen.
--selftest-intake unveraendert 32 ok / 0 fail.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newvalidator
das Eintritts-Tor prueft jetzt auch den surface-Kanon
Das Tor prueft `awaiting:` gegen seinen Kanon, `fe_coverage.surface` aber nicht — obwohl beide dieselbe Wirkung haben: validation_status incomplete, und jeder Walker ueberspringt den Brief STILL. Am 2026-08-22 lagen fuenf pending-Briefe gleichzeitig mit fe-coverage-malformed-surface in toPost, darunter ein P1 — alle fuenf hatte das Tor durchgelassen.
⚠ HERKUNFT: die Haupt-Arbeit (101 Zeilen: checkIntakeSurfaceKanon, SURFACE_ENUM als EINE Quelle fuer Fehlertext und Heilungs-Hinweis, --selftest-intake) stammt aus der Sitzung intake-tor-prueft-surface-kanon-nicht-TBD. Sie ist am 2026-08-23 gestorben und hatte den Stand UNVERSIONIERT im Arbeitsbaum liegen lassen — ihr einziger Commit war der Claim. Diese Sitzung hat sie gereapt, den Stand gelesen statt ueberschrieben und zu Ende gebracht.
🔴 DIE EINE ROTE PROBE, mit der sie starb, war der Kern des Auftrags: die Einhaengung war ZU BREIT. Die geteilte Guete-Pruefung meldet einen fe_coverage-Block MIT user_facing und OHNE surface-Zeile als `surface-value-invalid` (ein leerer Wert steht nun einmal ausserhalb der Werteliste). Am Tor haette das Briefe abgewiesen, deren Autor den Block angefangen und nicht zu Ende gedacht hat — also einen INHALTLICHEN Mangel, der zum todo-author-Weg gehoert. Genau davor warnt der Auftrag in Punkt 1. Behoben durch eine Vorbedingung: es muss eine surface-Zeile MIT Wert geben, bevor ihr Wert beurteilt wird.
Dazu zwei fehlende Gegenproben ergaenzt: der GANZ fehlende fe_coverage-Block (die woertliche Negativ-Kontrolle des Auftrags) und die auskommentierte Muster-Zeile des Geruests, die `coord new-brief` in jeden frischen Brief schreibt.
VERIFY: --selftest-intake 32 ok / 0 fail. Flip-Probe Einhaengung: genau die VIER Beanstandungs-Proben fallen, keine Gegenprobe. Flip-Probe Einengung: genau die ZWEI Fehlt-Gegenproben fallen. Gegen den echten Bestand: beide offenen Faelle (briefing-absender-datenpflege, podcast-cover-w3) melden INTAKE-DEFECT MIT Heilungs-Hinweis, der die fuenf Werte, den Pflicht-Slug und das fe--Praefix nennt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixblocks
CRLF fuer routerPageActions.ts zurueck — die Normalisierung war zu breit
Dritter Anlauf an der Zeilenenden-Frage, und diesmal GEMESSEN statt pauschal:
routerPageActions.ts CRLF <- war so, ist wieder so die fuenf *.test.ts LF <- waren so, sind wieder so
Der Vorgaenger 80a05c4 hat ALLE SECHS auf LF gedreht. Fuer die fuenf Testdateien war das richtig (mein erster Commit hatte ihnen CRLF eingeschleppt), fuer routerPageActions.ts falsch — die Datei war im Repositorium schon immer CRLF, und die "Normalisierung" war dort eine 7081-Zeilen-Aenderung, die niemand wollte.
⚠ Lehre: "Zeilenenden normalisieren" ist KEINE pauschale Handlung. Ohne .gitattributes gibt es keine Repo-weite Politik; jede Datei traegt ihren eigenen Stand, und der ist am URSPRUNGS-Commit zu messen (git show <ref>:<datei>), nicht am Nachbarn zu raten.
Der Netto-Diff dieser drei Commits gegen d43a167 ist jetzt das, was er von Anfang an haette sein sollen: 241 Zeilen dazu, 19 weg, in sechs Dateien.
CI-Tor npm run test:unit: 2398/2398, 100 Reihen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixblocks
Zeilenenden zurueck auf LF + die richtige Beschreibung zum Vorgaenger
ZWEI Fehler am Vorgaenger-Commit b8656bf, beide meine:
1. DIE COMMIT-NACHRICHT IST FREMD. Sie lautet "fix(import): Menue kuriert ueber ALLE Seiten der Website" und beschreibt eine ganz andere Arbeit. Ursache: der erste Commit-Versuch wurde vom Fundament-Precheck-Hook geblockt — DER GANZE Bash-Aufruf, also auch das `cat > /tmp/cmsmsg.txt` davor. Der zweite Versuch las dieselbe Datei, die inzwischen einen fremden Stand trug. ⚠ Lehre: nach einem Hook-Block ist NICHTS aus dem Aufruf gelaufen — auch nicht die Vorbereitung. Wer danach nur den letzten Befehl wiederholt, baut auf einem Zustand auf, den er nicht hergestellt hat. Dieselbe Klasse wie das verschluckte `git add` vom 2026-08-22 (neun Dateien fielen aus dem Commit).
2. ZEILENENDEN: meine Edits schrieben CRLF in sechs Dateien, das Repositorium speichert LF (Nachbardateien 0 CRLF, core.autocrlf=false, keine .gitattributes). Der Vorgaenger-Diff zeigte deshalb 3360 Zeilen statt der echten 241/19. Hier zurueckgedreht; kuenftige Diffs und blame auf diese Dateien sind wieder lesbar.
INHALT DES VORGAENGERS (das, was in seiner Nachricht haette stehen sollen): - logos: Eintrag ohne verwertbare Bildadresse wird als Text-Chip gezeichnet, nicht als <img src="">. Gemischte Reihen tragen. - mapCustomerLogosToItems: der Filter warf bildlose Eintraege WEG — deshalb waren sie unsichtbar; ein Fix nur im Zeichner waere wirkungslos geblieben. - references: Leer-Aussage zweisprachig, nur bei Entitaets-Speisung; ein leerer INLINE-Block bleibt still. Resolver setzt __referenceItems auch bei leerer Menge. - BEWUSSTE EINENGUNG: eine ABGELEHNTE Adresse bleibt src="#" wie bei den fuenf Geschwister-Sinks (klassenweiter Vertrag, renderBlockMediaBlocks.test.ts:324).
CI-Tor npm run test:unit nach der Normalisierung: 2398/2398, 100 Reihen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fiximport
Menue kuriert ueber ALLE Seiten der Website, nicht nur den Lauf
Am Beleg-Lauf gemessen: das kurierte Hauptmenue liess ausgerechnet die Schwerpunkt-Seite /active-campaign-management weg. Sie ist von Hand gebaut und steht in keiner Quell-Sitemap, war also nie im Lauf-Bestand. Ein Menue, das nur zeigt, was der Import gerade angefasst hat, ist bei jedem zweiten Lauf ein anderes.
Neu findPages(websiteId) am Datenbank-Weg (optional — ein Adapter ohne ihn faellt auf das bisherige Verhalten zurueck statt zu scheitern) und collectCurationPages: Lauf-Bestand plus alle UEBRIGEN veroeffentlichten Seiten. Die Angaben des Laufs gewinnen, weil ihr Titel bereits gereinigt ist.
Nur veroeffentlichte Fremd-Seiten kommen dazu — ein fremder Entwurf im Hauptmenue waere ein Verweis auf eine Seite, die es oeffentlich nicht gibt. Die Seiten des Laufs sind davon ausgenommen, sie sind per Bauart Entwuerfe.
4 neue Proben inkl. Negativ-Kontrolle (Entwurf faellt weg, Positiv-Kontrolle daneben), Rechtsseiten-Zuordnung und Fehlertoleranz. 88/2027 -> 88/2031.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fixinvite
Betreiber-Anlage ist annehmbar — offene Status sind eine MENGE
Die Annahme-Seite liess ausschliesslich 'invite-sent' als offen gelten und wies 'user-created-by-admin' als not-open ab. Der Betreiber-Weg ueber superuser.ayoune.app legt aber genau mit diesem Status an (routerOnboardingActions.ts:96/105) - eine so angelegte Person konnte ihr Passwort ueber /:reseller/accept-invite?token= nie setzen. Der CEO-Entscheid 2026-08-22 (Weg a) verlangt genau das.
- INVITE_OPEN_STATUSES als Menge; INVITE_OPEN_STATUS bleibt der Einzelwert fuer den Rueckbau-Rueckfall. - Der atomare Riegel fragt {$in: INVITE_OPEN_STATUSES}. Mit dem Einzelwert haette er bei einer Betreiber-Einladung nie gegriffen: die Vorpruefung liesse sie als valid durch, hier waere sie als race-lost gescheitert - ein 410 "schon angenommen" fuer eine Einladung, die niemand angenommen hat. - Der Rueckbau stellt den URSPRUENGLICHEN Status wieder her ({new:false} liefert ihn). Vorher haette ein Fehlschlag eine Betreiber-Einladung still in eine Team-Einladung verwandelt und dem Betreiber-Weg seinen Wiedervorlage-Datensatz entzogen.
NICHT der Erzeuger auf 'invite-sent' umgestellt: der Status traegt die HERKUNFT, und der Betreiber-Weg sucht mit ihm. Eine Verschmelzung braeche jene Suche.
Die W2-Probe, die 'user-created-by-admin' auf not-open festnagelte, ist umgedreht statt geloescht - samt Negativ-Kontrolle, dass die Weitung GENAU einen Status umfasst und die Frist fuer beide gilt. Flip-Probe: 2 rot / 9 gruen. Volle Reihe 660/665 (5 uebersprungen), tsc --noEmit sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixapplications
einen Fehler mit eigenem Status nicht auf 500 ueberschreiben
Gefunden an der LIVE-Probe, nicht an den 987 gruenen Proben: ein Uebergang auf eine nicht sichtbare Kennung meldete "Error 500: Applications not found".
Ursache: `customerMongoose.findById` gibt bei Nicht-Fund kein leeres Ergebnis zurueck, sondern WIRFT (`RessourceNotFoundError`, core methods/find/findOne.js:35) — und der Fehler traegt seinen Status (404) bereits im zweiten super-Parameter. Der `if (!doc)`- Zweig ist damit unerreichbar, und der pauschale 500er im Fang-Zweig machte aus JEDEM Aufruf mit fremder oder falscher Kennung eine Dienst-Stoerung.
Der Fang-Zweig respektiert jetzt `e.status` (4xx/5xx) und protokolliert ein 4xx als `warn` statt `error` — dieselbe Klassifikation wie im Fang-Zweig von createApplication. Ein Bedienfehler weckt damit niemanden in der Ueberwachung.
⚠ Die Klasse ist breiter als dieser Weg — eine Parallel-Sitzung arbeitet an derselben Sache im Kern (core-updateone-wickelt-404-in-500). Hier ist der Verbraucher behoben.
Proben 988/988. Flip-Probe: Status ignorieren faerbt genau 1 rot.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixcoding
Rueckkanal zur Plattform — Kennung im Rumpf, Ausfaelle sichtbar, schlankes Terminal-Fenster
Nach dem ersten erfolgreichen PWA->DC-Dispatch (CEO-Test 2026-08-22) lief der Auftrag am Geraet bis Exit 0, waehrend das Sitzungs-Dokument unveraendert auf 'dispatching' stand. Drei Glieder, alle am Quelltext vermessen.
A) Der Rueckschreiber baute PUT /codeagentsessions/<id>. Diesen Weg gibt es an der Entitaet nicht: der erzeugte Router fuehrt nur put("/") und put("/many"), sein Abschluss get("*") ist nur fuer Lese-Wege. Der Aufruf traf keine Route und endete in "Not Found" - geschluckt, also von "laeuft" ununterscheidbar. Jetzt der Vertragsweg mit _id im Rumpf.
Warum die Messung in 546803a den Aufrufer nicht sah: Adresse und Verb stehen in verschiedenen Funktionen (Adresse in writeBackToServer, method:'PUT' erst in flushWriteBack, das sie als Parameter bekommt). Eine Suche nach "PUT nahe einem Pfad-Muster" kann diese Form per Bauart nicht finden.
B) Der Ereignis-Strom hat KEINEN statisch auffindbaren Defekt - die Kette ist Ende-zu-Ende verdrahtet und die Aufnahme-Route ist eingehaengt. Der Grund fuer null Ereignisse war nicht ermittelbar, weil jeder Aussteig wortlos war. Die drei stillen Aussteige melden sich jetzt einmal je Sitzung, damit der naechste Lauf die Antwort liefert statt wieder zu schweigen.
C) Das Terminal-Fenster bekam kein preload, also gab es dort kein window.electronAPI; der Renderer-Dienst stieg in listen() sofort aus, der Fokus-Kanal wurde nie gehoert und das Fenster blieb auf der Standard-Route - eine zweite volle Anwendung. Die Whitelist trug den Kanal die ganze Zeit (preload.ts:52) und der Hoerer existierte auch; nur die Bruecke fehlte. Zusaetzlich laedt das Fenster die Sitzung jetzt direkt ueber die Adresse (?terminal=1), damit die schlanke Huelle vor dem ersten Bild feststeht.
Ausserdem: ein non-2xx-Rueckschreiben protokolliert benannt und meldet nach dem zweiten Fehlversuch einmalig ans Geraet - "schreibt nicht" und "schreibt unsichtbar" waren drei Monate lang ununterscheidbar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixhr
die drei Bewerbungs-Flaechen aus W1 fehlten im Modul-Raster
Gemessen am AUSGEBRACHTEN Modul-Zustand: 17 Verweise, keiner auf `hr.applications`, `hr.applications-board` oder `hr.applicants`. Die Flaechen waren gebaut, geseedet und in der PWA gerendert — aber aus der Oberflaeche NICHT auffindbar: diese Liste ist die einzige Auswahl des Rasters "ALLE FUNKTIONEN" (resolveNavigationLinks serverseitig, base-page.component.ts im Klienten). Wer eine Flaeche registriert und sie hier vergisst, baut einen Tiefverweis, den niemand findet — dieselbe Klasse wie pm.onboarding (CEO-Entscheid 2026-08-22).
Ergaenzt in fachlicher Reihenfolge: Bewerbungen, Bewerbungs-Pipeline, Bewerber (vor dem W2-Verweis "Bewerbungsprozesse").
Neue Probe haelt alle VIER fest, mit Positiv-Kontrolle gegen eine leere Liste. Flip-Probe: einen Verweis entfernt faerbt genau 1 rot. Proben 987/987.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixapplications
Ablehnungen sagen im Rumpf denselben Status wie auf der Leitung
Am laufenden Dienst gemessen (2026-08-22): GET /applicationprocesses/<erfunden> antwortete mit HTTP 404 und `meta.code: 500`. Die ay-CLI liest den Rumpf und meldete deshalb "Error 500" fuer einen Bedienfehler — in der Ueberwachung eine Stoerung.
Ursache: der HTTP-Status kommt aus `res.locals.status` (returnResponse), der `meta.code` aber aus `err.status || 500` (defaultAPIAnswerError) bzw. einem HARTKODIERTEN 400 (defaultAPIAnswerRequestFailed). Beide lesen `res.locals` nicht. Damit kam auch die ganze GUARD_STATUS-Abbildung der Motor-Ablehnungen (403/409/422) als 400 beim Aufrufer an — der Kern von W2 war im Rumpf unsichtbar.
Neue Naht `lib/antwortMitStatus.ts` haengt den Status an den Fehler, damit `err.status` greift; alle Ablehnungen der drei W2-Wege laufen darueber.
⚠ Die zwei Antwort-Bauer in `platform/core` sind NICHT angefasst — sie liegen ausserhalb der claim_paths, und eine Aenderung dort traefe ~330 Verbraucher. Die Codegen-Vorlage traegt denselben Widerspruch (actionJobOfferSend).
⚠ Der strukturelle Waechter allein reichte NICHT: eine Flip-Probe, die der Naht das Anhaengen wegnimmt, liess ihn gruen — er prueft, DASS sie benutzt wird, nicht DASS sie wirkt. Deshalb zusaetzlich `antwortMitStatus.test.ts` (7 Faelle inkl. Negativ-Kontrolle am rohen Bauer). Mit ihr beisst die Flip-Probe: 6 rot.
Proben 986/986 (49 Reihen, vorher 979/48).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixapplications
NUL-Byte aus dem Pruefer entfernen — git fuehrte die Datei als binaer
Der Dubletten-Schluessel trug ein literales NUL als Trenner. Folge: git stuft die Datei als binaer ein und zeigt keinen Diff mehr — eine Codeueberpruefung saehe Aenderungen daran nie. Gefunden am "Bin 0 -> 8496 bytes" im Commit-Statistik-Block, nicht an einem Fehlschlag: Bau und Proben waren die ganze Zeit gruen.
Ersetzt durch eine verschachtelte Karte (Map<from, Set<event>>) statt eines zusammengesetzten Schluessels — damit entfaellt die Trenner-Frage ganz. Ein Escape half nicht: es wurde auf dem Schreibweg erneut zu einem echten NUL.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixapplications
die neun neuen W2-Dateien nachreichen — sie fielen aus dem Vorgaenger-Commit
130e28a committete mit , und das nimmt UNVERSIONIERTE Dateien nicht auf: die zwoelf geaenderten Dateien gingen mit, die neun neuen nicht. Der lokale Bau blieb gruen (er sieht den Arbeitsbaum), die Kette #174 fiel mit neun — genau den neun fehlenden Dateien.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newapplications
der Bewerbungsprozess laeuft — Anlege-Naht, fuenf echte Statuswechsel, Konfigurations-Flaeche
Bewerbermanagement W2. W1 hat die Flaechen und den Prozess-Seed gebaut, aber die Strecke war nicht bedienbar: alle sechs Aktions-Ruempfe lehnten mit 422 ab, und `POST /applications` konnte gar nicht gelingen — das Schema fuehrt drei Pflichtfelder (`_workflow`, `currentStatus`, `statusSince`), die kein Schreibweg setzte.
- Anlege-Naht `custom/applications/routerApplicationCreate`: loest den geltenden Prozess ueber die dreistufige Kette auf (Stelle -> Mandanten-Kopie -> Plattform-Vorgabe), setzt die drei Felder und loest die Eingangsbestaetigung ueber den Orchestrator aus. Verwirft `_workflow`/`currentStatus`/`statusSince` AUS DEM RUMPF. - Fuenf Aktionen echt ueber `core/lib/workflow/transitionEntity` (transition/reject/send-offer/hire/withdraw); `anonymize` bleibt ehrlich abgelehnt (W6). - Mandanten-Riegel: der Motor laedt roh (`model.findById`), die Naht laedt VORHER ueber `customerMongoose` — sonst waere die Bewerbung eines fremden Mandanten schaltbar. - `statusSince` + `stageHistory` werden nachgeschrieben (der Motor schreibt die Namen des Piloten `ITask`, `strict` verwirft sie hier still). - Konfigurations-Route `custom/applicationprocesses` (Liste/Lesen/Aendern/Uebernehmen) mit einem Pruefer, der sieben Graph-Fehler abfaengt, die der Motor NICHT abfaengt. - SDUI: Flaeche `hr.applicationprocesses` + Modul-Verweis; Status-Aktionen am Bearbeitungs-Zustand verdrahtet (Hausregel "erst befuellen, dann verdrahten" erfuellt). - Rechte `hr.applicationprocesses.view|edit` kaskadenfrei ueber `additionalRights`.
Proben 978/978 (48 Reihen, vorher 954/47). Drei Flip-Proben, je genau 1 rot, mit nachgewiesener Ersetzung: Mandanten-Riegel, Punkt-Pfad-Riegel, Aktions-Verdrahtung.
Es gibt KEINE `ApplicationProcesses`-Entitaet und soll keine geben — das Fundament sagt es woertlich (`IApplication`). Der Prozess IST ein `Workflows`-Dokument.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixcoord
zwei Briefe hingen an einem Aufloeser, der ihre Abhaengigkeit per Bauart nie finden kann
BEFUND (systemisch, aber klein): der Pruefer loest `DEPENDS-ON <slug>` gegen SITZUNGS- Kennungen in sessions/done/ auf. Wird ein Brief innerhalb einer FUNDAMENT-KASKADE geliefert, wandert er nach queue/done/, ohne je eine eigene Sitzung getragen zu haben — gemessen 0 Sitzungen fuer beide Ziele, bei status:done. Ein stamm-basierter Aufloeser findet das nie, und der abhaengige Brief meldet fuer immer `trigger:waiting`. GROESSE der Klasse gemessen (nicht geschaetzt): 36 blockierte Briefe tragen ein DEPENDS-ON, 6 davon haben ein Ziel in queue/done/, und genau 2 davon haben keine Sitzung. Diese zwei.
Beide Abhaengigkeiten stattdessen EMPIRISCH am installierten Fundament belegt (@tolinax/ayoune-interfaces 2026.451.0), je mit Positiv- und Umkehrprobe: - presse-portale: `PressReleases` in data/modelsAndRights/marketing.js (1 Treffer; Positiv-Kontrolle `Partners` 2; Umkehrprobe erfundener Name 0). Geliefert von Kaskade 64. - white-label: IDomain.d.ts fuehrt `serviceType` (Z.66) UND `rootTarget` (Z.94) (Positiv-Kontrolle 51 Feldzeilen; Umkehrprobe 0). Der Auslöser fragte ohnehin nach PUBLIKATION, nicht nach Brief-Status — also so gemessen.
⚠ Beim Schreiben in die dokumentierte Zitat-Falle gelaufen und wieder heraus: das woertliche Zitat des alten Auslösers wurde vom Pruefer als AKTIVER Auslöser gelesen, der Brief blieb `waiting`. Genau das haelt `backup-pending-haenger-und-erster-lauf-beleg` in seinem eigenen trigger fest. Marker-Schreibweise deshalb vermieden; danach beide READY.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixcoord
zwei weitere Fundament-Praemissen richtiggestellt
paymenttransactions-consumerid — Auslöser UEBERHOLT, und der Brief widerlegt ihn selbst. Er verlangte "einen FUNDAMENT-PLATZ (Einzelschreiber auf interfaces + models)", waehrend sein eigener STOPP-Block auf Zeile 53 vier positive Quelltext-Belege fuehrt, dass die Fundament-Haelfte seit Kaskade 28 geliefert ist (IPaymentTransaction._consumerID:48 · IConsumer.recipients:848 · PaymentTransactions.ts:29 + Index :37 · Consumers.ts:996). Offen sind nur die zwei Verbraucher-Teile: die Schreib-Seite von `_consumerID` und der Reiter "Empfaenger", der weiterhin `Notifications` zeigt. Beides ohne Fundament-Platz. claim_paths BEWUSST nicht ersetzt — der Rumpf benennt fuer die zwei offenen Haelften keine eindeutigen Ziel-Repos; ein Ersatz waere geraten. Als erster Schritt im Lauf zu setzen.
oauth-app-kontingente-bau — Schwelle NACHGEMESSEN statt geglaubt (rein lesend, Prod): `oauthclients` = 1, `oauthtokens` = 0. Der Auslöser verlangt BEIDE > 0, er ist NICHT erfuellt — der Brief liegt zu Recht. Gegenueber der 08-03-Messung (0/0) hat sich die eine Haelfte bewegt. Nebenbefund festgehalten, NICHT behoben: claim_paths fuehrt platform/core bei `foundation: false`; der Widerspruch ist latent, solange die Schwelle sperrt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixcoord
zwei Briefe mit ueberholtem Fundament-Blocker richtiggestellt
callagent-drei-tote-felder — EINGEHAENGT. Sein Auslöser nannte "foundation-batch-cascade-31" als Halter von platform/interfaces; der Bestand steht bei 68. Vor allem aber braucht der Brief gar keinen Platz: sein eigener Defer-Grund sagt "Nach dem ABBILDEN-Entscheid gibt es KEINEN interfaces/models-Anteil — die Arbeit liegt in drei anderen Repos", und Rumpf-Zeile 84 benennt sie woertlich (communication-server / mobile-app / domains/ai/api). claim_paths entsprechend gesetzt — vorher standen dort interfaces+models, was ihn bei JEDER laufenden Fundament-Sitzung mit NOT-READY:paths kollidieren liess (4x zurueckgelegt). Danach gemessen: nur noch ein ECHTER, transienter Konflikt mit einer aktiven Sitzung, den der Pruefer selbst als verengbar ausweist. Der Teilbefund `sttProvider ist nicht abbildbar` bleibt ausdruecklich offen.
rang2-restposten-zustandsfelder — ZWEITE Korrektur am selben Tag, sie nimmt meine erste zurueck. Der Commit 158ae945cd hob ihn auf `dependency` mit der Begruendung "reiner Platz-Warter"; das war SACHLICH FALSCH. Gemessen: die Fundament-Haelfte ist geliefert (3 von 4 Entitaeten), der Rest braucht keinen Platz. Offen ist eine PRODUKT-Entscheidung, die der Brief selbst benennt: `joboffers` wurde am Bestand gestoppt (22 leere Stubs, kein `_customerID`) — gehoeren die fuenf Aktionen ueberhaupt an diese Entitaet? Daher `ceo-decision`, damit /interview ihn ueberhaupt sieht. Die Fundament-Pfade blieben dort BEWUSST stehen: der Brief benennt kein einziges Nicht-Fundament-Repo, ein Ersatz waere geraten gewesen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixcoord
sieben Briefe beanspruchten Fundament-Pfade, deren Arbeit laengst geliefert ist
Jeder der sieben traegt `foundation: false` UND sagt im eigenen Rumpf, dass sein Fundament-Anteil erledigt ist ("Der Fundament-Teil dieses Briefes ist GELIEFERT", "KEIN Fundament-Diff", "FUNDAMENT-ANTEIL GELIEFERT"). Stehen geblieben war nur der `claim_paths`-Eintrag auf platform/interfaces|models|core.
DER SCHADEN ist der in CLAUDE.md (Kaskade 68) beschriebene: ein `foundation:false`-Brief, der einen Fundament-Pfad weiter beansprucht, faellt beim Promote mit `NOT-READY:paths:<sitzung>:<pfad>`, sobald irgendeine Fundament-Sitzung laeuft — und die laufen rund fuenfmal taeglich. Er wird eingesammelt, kollidiert, wird zurueckgelegt, wiederholt. Gemessen: von sieben geprueften Briefen dieser Klasse sind ALLE SIEBEN mehrfach zurueckgelegt (2-4x).
Eine Stichprobe zusaetzlich am Quelltext belegt statt am Brieftext geglaubt: sdui-aktions-schiene verlangt ein Verb-Feld an `actionDef` — es steht seit laengerem in platform/core/src/lib/api/registerModuleStates.ts:899 und wird :914 verwendet.
WIRKUNG GEMESSEN: drei der sieben melden nach der Verengung `READY` (dashboard-geteilter-seitenfilter, stackdump-restmenge, statusseite-tages-rollup); vorher keiner. Alle sieben behalten nicht-leere claim_paths.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixroutes
Codegen-Nachzug hub — Mengen-Wege pruefen ab jetzt das Aenderungs-Recht
Der Ausstoss eines Erzeuger-Laufs vom 2026-08-22T05:13Z lag 121 Dateien gross unversioniert im Arbeitsbaum. Gepruefte Entscheidung: uebernommen, mit EINER benannten Korrektur.
BELEG, dass der Ausstoss reproduzierbar ist: Arbeitsbaum-Stand gesichert, auf HEAD zurueckgesetzt, voller Modul-Lauf `codegen routes --module hub` neu gefahren, danach byte-verglichen — deckungsgleich (sha1 ueber alle 171 .ts identisch), mit Positiv-Kontrolle gegen HEAD (163 Unterschiede, der Vergleich greift also wirklich).
WAS SICH FACHLICH AENDERT — 11 Router (chats, examresults, exams, faqs, glossaries, hubquestions, knowledgebases, polls, quotes, savedreplies, wikipages):
PUT /many vorher extendForNewRights(BASE) -> verlangte `hub.<e>.new` nachher extendForMassEditOrEdit() -> `hub.<e>.edit` ODER `hub.<e>.massEdit` DELETE /many vorher extendForDeleteRights(BASE) -> verlangte `hub.<e>.delete` nachher extendForMassDeleteOrDelete() -> `hub.<e>.delete` ODER `hub.<e>.massDelete`
Der PUT-Fall ist eine Korrektur, kein Zusatz: eine Mengen-AENDERUNG verlangte bis hierher das ANLEGE-Recht. Wer `new` haelt, aber weder `edit` noch `massEdit`, verliert den Mengen-Weg — er haette ihn nie haben duerfen. Wer `edit` haelt, aber nicht `new`, gewinnt ihn. Der DELETE-Fall ist rein additiv (any-of): niemand verliert, `massDelete`-Halter gewinnen.
Dazu die 4xx-Klassifikation aus der Vorlage (ValidationError/CastError enden als 400 + warn statt 500 + error) in allen create/update/delete/get-Handlern.
DIE EINE KORREKTUR AM AUSSTOSS: der Modul-Lauf entfernt in routerHubQuestions.ts still die von Hand gesetzte Montage `use('/:id/comments', routerHubQuestionsComments)` samt Einfuhr — acht Kommentar-/Reaktions-/Stimmen-Routen waeren danach ueber HTTP unerreichbar gewesen. Wiederhergestellt und mit dem vorgeschriebenen Zeile-1-Haertungsmarker versehen, damit der naechste Lauf sie meldet statt sie zu entfernen. Gegenprobe am Waechter: `--probe` meldete vorher 0 gehaertete Dateien, jetzt 1, mit dem Vermerk "NUR ueber den Marker" — die zwei Bestands-Achsen (verlorene Deklaration / verlorener Hand-Import) sind fuer eine verlorene `use()`-Montage strukturell blind.
Verify: `npm run build` gruen; Testreihe 217/217 in 16 Suiten gruen. Kontrolle vorher gefahren: mit dem rohen Ausstoss fielen 2 Proben in `hubquestionsRouterMounting.test.ts`, bei HEAD waren dieselben 5/5 gruen — die Regression war echt und ist geschlossen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixonboarding
Betreiber-Verdrahtung zurueckgenommen — die Annahme-Seite weist den Status ab
Der Katalog-Eintrag aus 7281a71 haette eine Mail verschickt, deren Verweis auf eine Seite fuehrt, die ihn ABWEIST — also genau die Fehlerklasse, mit der der CEO-Entscheid Weg (b) verworfen hat ("toter Verweis"), nur eine Ebene weiter.
Gemessen: `platform/auth/src/lib/inviteState.ts:32` fuehrt `INVITE_OPEN_STATUS = 'invite-sent'` als "der EINZIGE Status, aus dem heraus eine Einladung angenommen werden darf"; Zeile 70 weist jeden anderen als `not-open` ab. Der Betreiber-Weg schreibt `user-created-by-admin`. W2 hat den Fall gesehen und den Wert in einer eigenen Probe FESTGENAGELT (`platform/auth/test/inviteState.test.ts:44`) — eine getroffene Entscheidung der Nachbarstrecke, kein Versehen. Der Entscheid nahm an, W2 trage die Annahme; er sagt selbst "die Annahme-Seite muss stehen, BEVOR diese Mail sinnvoll verschickt wird".
Die VORARBEIT bleibt und ist geprueft: Empfaenger-Rueckfall auf `evt_data.email` (der Betreiber-Weg kennt kein `inviteEmail`) und Handelnden-Rueckfall auf `aYOUneInvites.sourceUser` (das Ereignis meldet keinen `sourceUserId`; ohne ihn begaenne der Betreff mit einer Luecke — am Bestand belegt: 2 von 2 Betreiber- Einladungen tragen `sourceUser`).
Zwei Proben lesen jetzt die BLOCKER-Zeilen im Quelltext: wird eine geaendert, faellt die Probe und zwingt zur erneuten Messung, statt dass jemand den Eintrag blind setzt. Umkehrproben: Vorarbeit ausgehebelt -> 2 rot, auth-Zeile geweitet -> 1 rot. Volle Reihe 864/864.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newcoord
Selbst-Hosting-Restprofile als vier Wellen geschnitten
Vier Wellen-Briefe (crm+marketing / content+ecommerce / Betrieb / Fachmodule) plus ein CEO-Entscheid-Brief zum stillgelegten support-center-api im Compose.
Faktenlage neu gemessen statt aus der Vorlage vom 08-05 uebernommen: 19 statt 17 Profile, core 11 statt 10, 66 statt 65 Abbilder, 19,37 GB gemessen statt ~21 GB hochgerechnet, Namens-Luecken bereits geschlossen (66/66).
Der Ausloeser des Ueberbau-Briefes war NICHT eingetreten (drei Betreiber- Residuals seit 16 Tagen offen). Die neuen Briefe verankern ihr DEPENDS-ON deshalb auf dem Residual statt auf dem Brief, der es hinterlassen hat — Befoerderungs-Trockenlauf weist alle vier mit Namen des Blockers ab.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newonboarding
Betreiber-Anlage verschickt die Einladungs-Mail (CEO-Entscheid Weg (a))
`user.created-by-admin` zeigt auf dieselbe Vorlage und Huelle wie die Team-Einladung (`invite-user-to-tenant` / `forgotpass`) — beide fuehren eine Person OHNE Passwort ueber einen Token zum Passwort-Setzen.
Der Katalog-Eintrag allein haette NICHT getragen: das Ereignis meldet dieselben Daten unter teilweise anderen Namen (`email` statt `inviteEmail`) und laesst den Handelnden ganz aus (kein `sourceUserId`). Ohne Rueckfall haette der Betreff mit einer Luecke begonnen. Der Handelnde wird deshalb vom Einladungs-Datensatz gelesen (`aYOUneInvites.sourceUser`) — nur dann, wenn das Ereignis ihn nicht mitschickt.
Die bestehende Negativ-Kontrolle benutzte ausgerechnet `created-by-admin` als "fremdes Ereignis" und waere durch die Verdrahtung gebrochen — sie nennt jetzt `password-changed`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixdeps
Gantt-Bibliothek auf 2026.3.4 und die drei Angular-Pakete aus dem Lock nehmen
Die Bibliothek deklariert ihre Angular-Peers ab 2026.3.4 als optional, der Express-SSR-Dienst zog sie bis dahin mit, ohne sie je zu laden. Gemessen: 985 -> 982 Lock-Eintraege, 25 MB weniger im Abbild, npm ci gruen (958 Pakete), tsc-Bau gruen gegen einen Baum ohne jedes Angular-Paket.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixcoord
sechs reine Fundament-Platz-Warter aus der Etiketten-Sackgasse befreit
`deferred` steht im AWAITING_ENUM, fehlt aber in BATCH_READY_AWAITING (coord-admin.mjs:6716) — die sechs Briefe waren damit fuer den Kaskaden- Kollektor UND den Grant-Waehler dauerhaft unsichtbar, obwohl ihr trigger READY sagt und einzig der Fundament-Platz fehlt.
Fuer Fundament-Platz-Warter gab es nur zwei Etiketten, und BEIDE fuehren in den Ausschluss: `foundation-slot` liegt ausserhalb des Enums (fail-closed), `deferred` faellt aus BATCH_READY_AWAITING. Ein Brief dokumentiert den vergeblichen Wechsel woertlich (knowledgebase-eigene-ki-aktionen-je-sammlung, /resync 2026-08-18). Gehoben auf `dependency` — der Wert, der bei 32 Geschwistern nachweislich traegt.
Wirkung gemessen (foundation-batch --dry-run): 5 der 6 sind jetzt als [core]/[field] IM Kollektor. Positiv-Kontrolle haelt: der echt geparkte devopsalerts-entity-retire-candidate bleibt [awaiting-human] EXCLUDED, das Tor ist also nicht pauschal geoeffnet.
Der sechste (rang2-restposten-zustandsfelder-fundament) bleibt draussen und legt einen eigenen Befund frei: er beansprucht platform/interfaces+models, traegt aber `foundation: false` — 11 Briefe liegen so, davon 7 mehrfach zurueckgelegt. Nicht in diesem Commit behoben (Koordinations-Entscheid).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fix
Angular-Peers als optional deklarieren, damit sie nicht in framework-freie Verbraucher wandern
Der Zweit-Einstiegspunkt /core ist framework-frei, das Paket deklarierte seine Angular-Peers aber ohne peerDependenciesMeta — npm installierte sie deshalb in jeden Verbraucher. Gemessen an einem leeren Testpaket: 3 Angular-Eintraege plus rxjs vorher, 0 danach.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixcoord
Kaskade 65 stand als tote Next-in-line in der Fundament-Reihe
`foundation-batch-cascade-65-fields` trug seit dem CEO-Einfalten in Kaskade 66 (2026-08-21) ein `superseded_by`, aber weiter `status: blocked`. `foundation-advance` liest den STATUS, nicht das Feld — der Brief meldete sich damit als "Next-in-line (P1, 8-14 h)", obwohl sein Inhalt in 66 geliefert ist.
- status blocked -> superseded (Vorbild: Kaskade 67, die es korrekt traegt) - echter Next-in-line jetzt: core-updateone-wickelt-404-in-500 (P1)
Gefunden durch CEO-Rueckfrage im /resync-Tick, nicht durch einen Waechter.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixcoord
drei Briefe trugen `awaiting: pl-grant` — ein Wert ausserhalb des Kanons
`pl-grant` steht NICHT im kanonischen Vorrat (`ceo-decision | ceo-verification | user-input | operator-action | hardware | dependency | deferred | dedicated-session | none`). Der Pruefer sagt dazu woertlich: „ein Wert ausserhalb macht ihn fuer JEDE Automatik unsichtbar" — die drei Briefe waren also weder claimbar noch befoerderbar.
🔴 ZWEI DAVON SIND MEINE, aus derselben Sitzung (Iteration 5, 2026-08-22): ich hatte den Wert aus dem BESTAND abgeschrieben (`grep` zeigte ihn an einem vorhandenen Brief), statt den Kanon zu pruefen. Genau die Fehlerklasse „vom Bestand abschreiben statt die Regel lesen" — der vorhandene Brief trug denselben Fehler.
Korrigiert auf `awaiting: none`, und das ist gemessen, nicht geraten: der Fundament-Grant laeuft ueber `foundation: true` plus den Grant-Mechanismus, NICHT ueber `awaiting`. Die Verteilung der `foundation: true`-Briefe in `blocked/` bestaetigt es — 26x `dependency`, 11x `none`, 11x `deferred`, 1x `trigger`; kein einziger nutzt `awaiting` als Grant-Tor.
Wirkung belegt: `ensure-worker-prompt` meldete fuer beide vorher `awaiting-not-in-enum … nichts angefasst`, danach `already complete (no-op)`.
⚠ NICHT MITKORRIGIERT, bewusst: drei weitere Briefe tragen `awaiting: trigger` (ebenfalls nicht im Kanon). Dort ist die richtige Ersetzung NICHT eindeutig — `deferred` und `dependency` bedeuten Verschiedenes, und die Wahl aendert die Einreihung. Das gehoert gelesen, nicht geraten: `codingjobs-lebenszyklus-routen-und-cli-stilllegen`, `codingjobs-strukturell-entfernen`, `deployment-health-severity-wirkung-nachmessen`.
Fixpipeline-red-head-audit
„Kette aus" ist nicht „Kette rot" — Ursache statt Vermutung
Der Melder schrieb fuer JEDEN fehlenden Publish „die Kette meldet gruen, geliefert wird nichts". Bei `ayoune-google-ads-client` meldete gar keine Kette etwas (`pipelines_config` 404, 0 Laeufe). Der Satz beschrieb also einen anderen Zustand als den vorliegenden — und schickte den Folge-Auftrag in die falsche Richtung: er suchte den Fehler im `|| echo` des Publish-Schritts, waehrend der Schritt nie ausgefuehrt worden war. ⚠ Diese Vermutung stammte aus MEINEM eigenen Brief; sie war nie gemessen.
Ueber zehn Bibliotheken gemessen: 5 mit eingeschalteter Kette, 4 OHNE Kette und trotzdem veroeffentlicht (von Hand), 1 ohne Kette und ohne Paket. Die Haelfte hat gar keine laufende Kette — und das ist offenbar in Ordnung.
Getrennt wird jetzt die URSACHE, nicht die Schwere: die Befund-Klasse bleibt `paket-fehlt` (Ausstiegs-Vertrag unveraendert, beide Faelle faerben rot), dazu kommt je Fund ein Feld `chain` und ein eigener Satz. Die Klassen-UEBERSCHRIFT ist jetzt ursachen-NEUTRAL — der alte Satz steht nur noch dort, wo er zutrifft.
🔴 DREI BEFUNDE UEBER DAS EIGENE WERKZEUG, alle beim Verify aufgefallen:
1. `--registry` war fuer SCOPED Pakete WIRKUNGSLOS. Gemessen: `npm view @tolinax/ayoune-http --registry https://registry.npmjs.org` liefert `2026.1.0`, weil `npm config get @tolinax:registry` auf `registry.ayoune.app` zeigt und eine scope-spezifische Registry ueber `--registry` GEWINNT. Gegenprobe im selben Lauf: `npm view express --registry …` greift. Der Schalter war also fuer genau die Pakete blind, fuer die dieser Waechter gebaut ist — und haette jeden Gegen-Lauf gegen ein anderes Register still verfaelscht. Behoben ueber `npmViewArgs`, das den Scope aus dem Paketnamen ableitet und `--@scope:registry=` mitgibt (3 Proben).
2. Die 404-ZWEIDEUTIGKEIT: Bitbucket antwortet auf `pipelines_config` mit 404, wenn die Kette nie eingeschaltet wurde UND wenn es das Repositorium gar nicht gibt. Aus dem 404 allein folgt „nie eingeschaltet" NICHT. Geloest OHNE dritten Abruf: der Publish-Durchgang laeuft per Bauart nur ueber lokal existierende Verzeichnisse, die Existenz ist dort belegt und wird durchgereicht. ⚠ Ein erster Anlauf fragte sie per API nach — und machte aus einem korrekten `kette-nie-gelaufen` ein `unbestimmt`, sobald jene Abfrage aus einem anderen Grund als 404 fehlschlug. Eine Probe, die scheitern KANN, darf eine belegte Tatsache nicht ueberschreiben.
3. Der Status-Regex stand als `/API (d{3}) /` statt `\d{3}` in der Datei (der Backslash ging beim Einsetzen ueber ein Heredoc verloren). Er traf damit NIE, `configStatus` blieb null, und JEDER Befund fiel auf `unbestimmt` — die ganze Nachschaerfung war still ausgehebelt, ohne dass eine Probe rot wurde. Die vorhandenen Proben pruefen die reine Klassifikation; der Regex lag im netzabhaengigen Abruf. Jetzt als reine `statusFromError` herausgezogen und mit 3 Proben festgenagelt. LEHRE: eine Zeichenketten-Auswertung gehoert in eine reine Funktion, sonst ist sie per Bauart ungeprueft.
VERIFY, live gegen Bitbucket (nicht per Fixture): - `ayoune-google-ads-client` -> `kette-nie-gelaufen` (404 + size=0, unabhaengig gemessen) - `ayoune-email-blocks` -> `kette-laeuft` (enabled=true, size=9) - chainCounts zaehlt beide getrennt: {nie-gelaufen 1, laeuft 1, unbestimmt 0} - Negativ-Kontrolle mit ungueltigen Zugangsdaten: der Melder laeuft durch, meldet den Registry-Befund und behauptet KEINE Ursache (`unbestimmt`), Ausstieg unveraendert.
Proben +21 (I Ursachen-Klassen · J Registry-Schalter · K 404-Zweideutigkeit · L Status-Regex), 84/84 in dieser Datei gruen. Volle Paket-Reihe 4797/8 — dieselben acht Bestandsfehler wie vor der Aenderung (4776/8 in Iteration 2 gemessen), also +21 Tests, +21 pass, fail unveraendert: keine Regression.
🟢 NEBENBEFUND: `ayoune-google-ads-client` ist inzwischen VEROEFFENTLICHT — der Brief, den dieser Waechter am 2026-08-22 erzeugt hat, ist abgearbeitet worden. Der Live-Beleg oben laeuft deshalb bewusst
Fixalarme
Alarm-Stufe aus dem Verhaeltnis ableiten, nicht aus der Absolutzahl
`checkHealth.ts:205` lautete `severity: unavailable > 1 ? 'critical' : 'warning'`. Die Stufe hing damit an der ABSOLUTZAHL nicht verfuegbarer Instanzen statt am Verhaeltnis zur Sollzahl - in beide Richtungen falsch, beides an echten Alarmen gemessen (Fenster 2026-08-15..22, Sammlung `alerts`):
- Falsches critical: 636 von 688 kritischen `deployment_failed` trugen `availableReplicas > 0`, liefen also weiter. Spitzenreiter sind drei Ein-Instanz-Laeufer, bei denen "0 verfuegbar" der Normalzustand jedes Neustarts ist (368 + 149 + 54 Alarme). - Verpasster Totalausfall: ein Ein-Instanz-Dienst, der ganz ausfaellt, zeigt `unavailable = 1` und war damit nur `warning` - niedriger eingestuft als das harmlose Neuausrollen des Nachbarn.
Neu: `helpers/deploymentOutage.ts` haelt das Urteil EINMAL (rein, AY-frei) - `outageScope` (full/partial/none) plus der aus `syncCluster` hierher gezogene `deploymentHealthStatus`. Beide Regeln standen an getrennten Stellen und sind genau deshalb auseinandergelaufen.
Karenz gegen Ausroll-Rauschen ueber den BESTEHENDEN Aufschlag des geteilten Schreibers (`upsertAlert.escalateAfterMs`), kein neuer Mechanismus: ein Totalausfall startet als `warning` und wird `critical`, sobald er die Karenz (Vorgabe 10 min, `DEPLOYMENT_OUTAGE_CRITICAL_MINUTES`) ununterbrochen steht. Gemessen trennt das sauber: von 1.960 Alarmen mit `availableReplicas = 0` standen 1.934 (98,7 %) genau EINEN Takt, 25 laenger als drei Stunden, genau einer dazwischen.
Zwei Praemissen des Auftrags korrigiert: - `syncCluster.ts:81` (readyReplicas === 0) und `availableReplicas === 0` sind NICHT dieselbe Regel - 430 Alarme ueber 33 Dienste tragen ready=1 bei available=0. Fuer die Stufe gilt `availableReplicas`, weil `escalationGate.decideEscalation` genau dieses Feld fuer sein FULL OUTAGE liest. - Die vorgeschlagene Regel ALLEIN haette die Lage verschlimmert: sie macht aus 1.908 heutigen Warnungen kritische Alarme (Ein-Instanz-Neustart). Erst die Karenz dreht das um. `status.updatedReplicas` geprueft und nicht gebaut - ohne Zusatznutzen neben der Karenz.
Proben: `test/deploymentOutage.test.ts`, die vier Faelle des Verify-Gates plus Karenz, Zahl-Lesung und Verhaltensgleichheit des Gesundheitszustands. Die abgeloeste Regel steht als `legacySeverity` daneben, damit der rote Gegenpart im selben Lauf entsteht. Flip-Probe gefahren: mit der alten Regel an derselben Stelle fallen 11 Proben, die uebrigen 355 bleiben gruen. `npm test` 366/366.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixnode
Spreizung zaehlt nur Pods derselben Fassung (matchLabelKeys, 0.1.10)
GEMESSEN, nicht abgeleitet: nach der Ausbringung von 0.1.9 landeten BEIDE Repliken von ayoune-updates-develop-node auf demselben Knoten (fp7m), waehrend heartbeats im selben Lauf sauber auf zwei Knoten verteilte. Gegenprobe: das Loeschen einer der beiden updates-Repliken liess den Ersatz-Pod auf einem ANDEREN Knoten (fny8) starten - der Mechanismus greift also, nur nicht waehrend eines Rollouts.
Ursache: der labelSelector der Spreizung (name/instance/app) trifft auch die Pods der ALTEN Fassung. Waehrend eines Rollouts zaehlen die mit, die Schiefe wird gegen den falschen Bestand gerechnet, und mit dem weichen ScheduleAnyway kann die Wertung dann zugunsten eines einzelnen Knotens ausfallen.
matchLabelKeys: [pod-template-hash] beschraenkt die Zaehlung auf dieselbe Fassung (stabil seit K8s 1.30, Cluster faehrt 1.35). Umkehrprobe gefahren: der API-Server liefert das Feld im Objekt zurueck, verwirft es also nicht still.
Ein-Replikat-Render bleibt gegen 0.1.7 identisch (ausser dem Fassungs-Etikett); leere matchLabelKeys-Liste laesst das Feld ersatzlos entfallen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newtasks
Code-Agent-Aktion aus der Registry ziehen (Pin auf interfaces 2026.452.0)
Der Pin-Bump zieht den neuen entityActions-Eintrag `dispatch-code-agent` nach — ohne ihn rendert kein Knopf, egal was publiziert ist (actionsForEntity liest die INSTALLIERTE Registry). Wirksam wird er an den zwei Aufgaben-Tabellen (registerStates.ts:483/589) und in der Rechte-Ableitung.
An der Detail-Ansicht wird der generische Eintrag BEWUSST herausgefiltert: dort stehen seit CEO 2026-07-08 zwei handgebaute Knoepfe auf derselben Route, die die Laufzeit-Wahl (bodyData dc/k8s) und einen Bestaetigungs-Dialog tragen — entityActions kann beides nicht ausdruecken (actionsForEntity setzt bodyData fest). Ohne den Filter stuenden dort drei Knoepfe auf einer Route, davon einer namenlos.
Lock-Beleg vor dem Push: interfaces/models/core je genau EINE Aufloesung, alle gleich dem publizierten Stand. 726/726 Proben gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Anlass: 2026-08-22T18:19Z raeumte ein ScaleDown des Cluster-Autoscalers BEIDE Repliken von ayoune-states-develop-node zugleich (0/2, rund 14 Minuten). Das Chart bot dem Autoscaler nichts an, was ihn bremst - im ganzen Baum existierte genau EIN PodDisruptionBudget, und der gehoert dem Istio-Ingressgateway.
- templates/pdb.yaml: PDB, gerendert NUR bei replicaCount > 1, minAvailable: 1, Selektor = die drei POD-Etiketten (name/instance/app). Ein echter bool in podDisruptionBudget.enabled schlaegt die Automatik; maxUnavailable schlaegt minAvailable. - deployment.yaml: topologySpreadConstraints - Roh-Durchreichung (Bauform wie affinity) plus Automatik ab replicaCount > 1 mit chart-gerendertem labelSelector. whenUnsatisfiable: ScheduleAnyway, bewusst weich. - values.yaml: podDisruptionBudget / topologySpreadConstraints / topologySpread.
Fuer die 227 Ein-Replikat-Dienste aendert sich NICHTS: helm template 0.1.7 gegen 0.1.8 ist identisch ausser dem helm.sh/chart-Etikett (das jeder Chart-Bump traegt); bei replicaCount 0 und 1 wird weder PDB noch Spread gerendert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixtasks
waitingOnOthers auf den Kanon-Wert blocked abbilden
Outlooks `waitingOnOthers` wurde auf `waiting` abgebildet — kein Wert des Kanons `PmStatus`. CEO-Entscheid 2026-08-22 (via /interview), Weg (a): `blocked`.
Gemessen mit Positiv-Kontrolle: der Normalisierungs-Haken `TASK_LIKE_STATUS_MAP` fuehrt sieben Eintraege, KEINER heisst `waiting` — die Annahme des Auftrags, der Haken fange den Wert ohnehin ab, ist falsch. Diese Zeile ist der einzige Riegel.
Waechter-Test nagelt JEDEN ausgegebenen Status gegen den Kanon fest (nicht nur den einen Fix); Flip-Probe: mit dem alten Wert 93/94 rot, mit dem Fix 94/94.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newpm
Aufgaben-Aktion dispatch-code-agent in modelsAndRights deklarieren
Die Route POST /tasks/:id/actions/dispatch-code-agent existiert handgebaut seit 2026.28.0, war aber ohne Registry-Eintrag fuer Nutzer unerreichbar — actionsForEntity baut ohne Deklaration keinen Knopf. Rein additiv: modelsAndRights bleibt bei 843 Eintraegen, null neue plural-Zeilen.
Bewusst OHNE emits (die custom-Route gewinnt per Mount-Reihenfolge, ein deklariertes Ereignis haette keinen Erzeuger), ohne defaultOnScan (ein Barcode-Scan darf nie ungefragt eine Code-Agent-Sitzung starten) und ohne right (die Ableitung pm.tasks.actions.dispatch-code-agent traegt).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newpipeline-red-head-audit
zweiter Durchgang für VERÖFFENTLICHUNGS-Ketten — „Kette grün, geliefert wird nichts"
Der Wächter beantwortete bisher eine Frage: „Ist der letzte Lauf rot?" Ein Repositorium, das nicht ausbringt sondern VERÖFFENTLICHT, fällt durch dieses Raster per Bauart — es hat nie ein Deployment. `ayoune-vscode-copilot` war deshalb vier Tage lang nicht abrufbar, während jedes neue cloud-init es zog und leer ausging, ohne dass irgendwo ein Fehler entstand.
🔴 WARUM EIN ZWEITER DURCHGANG UND KEINE WEITERE KLASSE IN classifyRedHead: der bestehende Weg beginnt mit selectRedHead, und bei einem GRÜNEN Kopf ist dort Schluss (reason: 'kopf-nicht-rot'). Die Klasse `nie-ausgebracht` entsteht also erst NACH einem roten Kopf. Der teure Fall ist aber genau der andere.
Vier reine, prüfbare Funktionen (netzfrei testbar): classifyPipelineKind ausbringung | veroeffentlichung | keine — KOMMENTAR-BEWUSST, sonst macht eine Zeile, die "kein gke-deploy" DOKUMENTIERT, aus einer Bibliothek einen Dienst (dieselbe Falle hat die Helm-Zahl zuletzt am 2026-08-20 um acht überhöht). derivePackageName Kette > Publish-Verzeichnis > Wurzel-Name MIT Scope. derivePublishDir cd <dir> && npm publish classifyPublishState paket-fehlt (BEFUND) · paket-unbestimmt · registry-unbestimmt · paket-vorhanden · null (kein Veröffentlichungs-Repo).
🔴 DIE REIHENFOLGE IN derivePackageName IST DER KERN, und der Anlassfall belegt sie: ayoune-vscode-copilot trägt in seiner Wurzel-package.json den Namen `ayoune-copilot` OHNE Scope — veröffentlicht wird @tolinax/ayoune-vscode-copilot aus npm-dist/. Wer blind den Wurzel-Namen nimmt, fragt nach einem nie veröffentlichten Namen und meldet einen Defekt, den es nicht gibt. Ein Name ohne Scope wird deshalb NICHT geraten, sondern als `paket-unbestimmt` benannt.
Ehrlichkeits-Regel wie bei classifyRedHead: registryHas unterscheidet 404 vom Netzfehler AM AUSGABETEXT, nicht am Ausstiegs-Code (npm view endet bei JEDEM Fehler != 0). Ein unerreichbares Register ist `registry-unbestimmt`, nie ein Befund.
Damit automatisiert der Wächter die Regel aus feedback_pipeline-silent-no-publish ("Tags pushed != Lib published — ALWAYS empirisch mit npm view verifizieren"), die bisher Handarbeit war und genau deshalb ausblieb.
🟢 ERSTER SCHARFER FLOTTEN-LAUF FAND SOFORT EINEN UNBEKANNTEN FALL: ayoune-google-ads-client (domains/ads/lib-google-ads) — Kette grün, Paket 404, letzter Commit 2026-06-19. Ursache dort ist eine Fehler-Unterdrückung IN der Kette (npm publish || echo "Version already published, skipping"), die jeden Fehlschlag als Erfolg meldet. 0 Verbraucher, also heute latent; eigener Brief, nicht hier behoben.
🟢 Und die Zahl trägt den Ausstiegs-Code: 46 von 47 Veröffentlichungs-Repositorien sind grün. Der Wächter steht NICHT ab Tag eins auf rot — die Lärm-Regel des Kopf-Kommentars bleibt gewahrt. Ab jetzt färben ZWEI Klassen rot.
🔴 NEBENBEFUND, beim Bauen gefunden und mitbehoben: slugForDir erkannte eine .git/config mit ANMELDE-TEIL nicht (https://x-...-token-auth:<token>@bitbucket.org/...). Ein so geklontes Repositorium fiel aus JEDEM Lauf heraus — weder auf roten Kopf noch auf sein Paket geprüft, still unter `skipped` verbucht. Gemessen: 379 Ketten, 363 erkannt; von den 16 übrigen haben 15 gar keine .git/config (dort ist `skipped` richtig), genau EINES scheiterte an der Adress-Form — ayoune-search-lib, und das ist eine Veröffentlichungs-Kette. Ausgelagert als reine slugFromGitConfig, sechs Proben.
Proben: +32 (F reine Klassifikation · G Ende-zu-Ende im echten Skript-Lauf · H Kennung), 63/63 in dieser Datei grün. Flip-Probe, Negativ- und Positiv-Kontrolle je vorhanden; die bestehenden Proben A-E bleiben unberührt und netzfrei (Fixture ohne __publish überspringt den Durchgang). Volle Paket-Reihe 4776 Tests, 8 Fehlschläge — dieselben acht, namentlich identisch, auch OHNE diese Änderung gemessen (git-stash-Gegenprobe): Bestandsfehler des Pakets, keine Regression.
Newcoord
Walker prueft gegen queue/done + Aufraeumer meldet Fertig-Doppel
VORFALL 2026-08-22: `podcast-takt-zeit-je-objekt` fuhr 17:33:35Z ein sauberes `coord done` (8596553cf2). 17:40:32Z claimte ein Walker denselben Brief erneut aus toPost — auf dem AKTUELLEN Stand (merge-base --is-ancestor: ja). Ergebnis: derselbe Slug in ZWEI Ordnern getrackt.
- Guard (a1) `already-done` vor dem O_EXCL-Lock, direkt nach `pending` — die zurueckgekehrte Kopie traegt `status: pending`, der Vorgaenger-Guard faengt sie also nicht. Benannter Grund `copy-in-queue/done`, nie stilles false. - `coord reap` meldet FERTIG-DOPPEL (Slug in done UND toPost/blocked), nur gemeldet, nie bewegt — die Ursache ist ungemessen, und eine getrackte Datei wegen Namensgleichheit zu entfernen ist die riskantere Richtung.
GEMESSEN statt angenommen (der Brief verlangte es ausdruecklich): der bestehende `done-zombie`-Zaehler stand zu Recht auf 0 — `classifyZombieBrief` laeuft ausschliesslich ueber queue/active, der Doppel-Eintrag lag in toPost. Anderes Quellverzeichnis, kein schwaecheres Signal.
`active` ist bei FERTIG-DOPPEL BEWUSST ausgenommen: waehrend eines laufenden `coord done` liegt derselbe Slug kurz in active UND done.
Proben: 10 neu, ins test:coord-Fach VERDRAHTET (1066 -> 1076, Fehlschlaege unveraendert 5 = Bestand, beidseitig gegengeprueft). Flip-Probe beidseitig. Flottenweite Zaehlung 0 auf beiden Achsen, zweifach gemessen.
Fixpm-states
zwei Waechter-Befunde aus Kette #219 — Umlaute in der Beschriftung + Drift-Sentinel
Der Vor-Commit `8c7d7ed` brachte die Kette zu Fall, und beide Waechter hatten recht:
1. `germanUmlautHygiene` — die Kurzbeschreibung des neuen Zustands trug „pruefen" und „uebernehmen" statt „prüfen"/„übernehmen". Der Waechter liest die einfach-gequoteten Zeichenketten der Seeder (Kommentare vorher entfernt) gegen die geteilte Stamm-Liste `@tolinax/ayoune-umlaut-stems`. 2. `registerStates.test.ts` — der Drift-Sentinel steht per Bauart auf einer festen Zahl; ein neuer Zustand IST die beabsichtigte Abweichung. 89 -> 90, mit der Herleitung in der Zeile wie bei allen Vorgaengern.
🔴 Die Lehre, nicht der Fix: `npm run build` war gruen und hat NICHTS davon gesehen. Beide Waechter haengen an `npm test`, und genau der wurde vor dem Push nicht gefahren. Die repo-eigene Testreihe ist vor dem Push Pflicht, nicht der Bau.
Der Umlaut-Waechter ist damit an der Sache selbst als beissend belegt — er hat den Fehler in Kette #219 real gefangen, nicht in einer gestellten Probe.
`ayoune-pm-onboarding-assistant` registrieren — mit LEEREM Eingangs-Vertrag
Zweiter Andockweg des Assistenten „Bilde deine Software ab" (Muster `ayoune-hub-question-thread`): Zustand + Route sind der eine Weg, diese Registrierung der andere — sie laesst die Komponente kuenftig als Reiter eines Detail-Verbunds haengen. Fuer den EINSTIEG ist sie wirkungslos; den liefert der Zustand `pm.onboarding` plus die bestehende Route `/pm/onboarding`.
Der Eingangs-Vertrag ist leer, und das ist gemessen statt gewaehlt: die Komponente traegt KEIN einziges `@Input()` (anders als die beiden Nachbarn) — sie holt Rechte ueber `PageService` und Daten ueber `ApiService`. Der Vorgabe-Mapper in `inputsFor()` wuerde ihr `{consumer, consumerId, pageData, id}` zuweisen, und `ComponentRef.setInput` auf einen nicht deklarierten Eingang meldet in Angular 21.1.4 `NG0303` (`_debug_node-chunk.mjs:8437`) — in Produktion sogar STILL, also unbemerkt. Der leere Mapper ist deshalb Pflicht, nicht Kosmetik; der im Auftrag vorgeschlagene Drei-Feld-Mapper waere ein Defekt gewesen.
Zwei Proben dazu, mit Negativ-Kontrolle (ohne eigenen Mapper kaeme der generische Vier-Schluessel-Abzug). Flip-Probe gefahren: mit dem Drei-Feld-Mapper faellt genau die eine Probe, die Kontrolle bleibt gruen. 13/13 gruen.
Newpm-states
Zustand `pm.onboarding` macht den Assistenten „Software abbilden" auffindbar
Der Assistent ist seit dem 2026-08-22 gebaut, geprueft und ausgebracht (clients/mobile-app 9002f976, Route `/pm/onboarding`) — aber nur ueber den Tiefverweis erreichbar. CEO-Entscheid vom selben Tag, Weg (a): ein Platzhalter-Zustand plus kuratierter Verweis im Modul-Raster.
Die Bauform ist `accounting.ledgerimport` / `accounting.anhang`, NICHT der im Auftrag genannte HR-Assistent — gemessen: `base-page.component.html` rendert bei `pageType:'component'` ausschliesslich einen `<router-outlet>`, der Rumpf kommt also von der Angular-Route. `platform/states` leitet die Adresse deterministisch aus dem Titel ab (`moduleOverviewLinks.ts:154`), `pm.onboarding` → `/pm/onboarding`, und genau dort haengt die bestehende Route. Deshalb BEWUSST kein `componentSelector`: der wird nur von `sub-view.component.ts:230` gelesen, also fuer einen Reiter innerhalb eines Detail-Verbunds — auf oberster Ebene liest ihn niemand, und ihn zu setzen behauptete eine Wirkung, die es nicht gibt.
Wozu dann der Zustand? `resolveNavigationLinks` verwirft einen kuratierten Verweis ohne Ziel-Zustand STILL (`moduleOverviewLinks.ts:348`). Ohne diesen Eintrag bleibt der Assistent unauffindbar.
`_right: 'pm.modules'` ist ein BESTEHENDES Recht — dasselbe, das `scan-repository` verlangt und das der Baustein selbst prueft; ein aus dem Titel abgeleitetes `pm.onboarding` gibt es als Recht nicht und der Zustand waere fuer alle unsichtbar.
Belegt am Bau (nicht behauptet): `getStates()` liefert den Zustand mit `pageType:'component'` und `pwaLink:/pm/onboarding`, der kuratierte Verweis steht an Position 13 von 30 direkt hinter „Komponenten"; Umkehrprobe mit einem erfundenen Ziel liefert nichts.
Newscheduler
CHECK_PODCAST_EPISODES wird Faelligkeits-Tick alle 10 Minuten
CEO 2026-08-22, woertlich: 'Was ist wenn ein anderer Kunde das Briefing ODER DEN PODCAST erst um 9 haben will?' Dieselbe Korrektur wie bei CHECK_TENANT_BRIEFINGS (4b076c9) und aus demselben Satz. Muster CHECK_EXPORTS: haeufiger Tick ohne Uhrzeit, Faelligkeits-Pruefung je Objekt.
15 7 * * * Europe/Berlin -> */10 * * * * (ohne tz — ein Zehn-Minuten-Tick ist zeitzonen-frei; eine tz hier waere eine Behauptung ohne Wirkung und suggerierte, die Uhrzeit laege wieder im Cron).
Die VORGABE bleibt 07:15 Europe/Berlin und liegt jetzt in worker-automatic-checks-v2/src/lib/podcastEpisodeCore.ts. Sie ist bewusst NICHT die 07:00 des Briefings: der abgeloeste Cron stand nach den Briefings, weil beide dasselbe Artikel-Fenster lesen und die Folge den Stoff des Morgens tragen soll. Bestands-Sendungen verhalten sich damit exakt wie vorher.
5 neue Registry-Proben (Tick-Vertrag, keine tz, Beschreibung nennt sendTime/ sendDays, Vorgabe 07:15, attempts 1, kein Mandant im Namen). Reihe 70/70 (vorher 65).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UVmnsay4L5Rw8MvnFMVqmj
Newpodcast
Sendezeit je Sendung statt festem 07:15-Cron
CEO 2026-08-22, woertlich: 'Was ist wenn ein anderer Kunde das Briefing ODER DEN PODCAST erst um 9 haben will? Der Scheduler sollte nur checken welche in der Zukunft liegen wie bei anderen Themen auf der Platform auch, siehe Exporte und Reporte.' Dieselbe Korrektur wie bei den Briefings am selben Tag.
GETEILT, NICHT KOPIERT: die Faelligkeits-Logik liegt jetzt in lib/sendSchedule.ts (rein, uhrlos, ohne DB-Zugriff). tenantBriefing.ts importiert sie und re-exportiert unter seinen bisherigen Namen weiter, also ohne Aufrufer-Nachzug; der Vorgabewert ist Parameter geworden, weil Briefing 07:00 und Podcast 07:15 traegt.
PARITAET IST DER TRAGENDE TEIL: eine Sendung ohne Zeit-Felder faellt auf 07:15 Europe/Berlin und ihre schedule-Tage zurueck — exakt das Verhalten des abgeloesten Cron. Die 07:15 sind kein Schoenheitsfehler: der alte Cron stand bewusst NACH den Briefings (07:00), weil beide dasselbe Artikel-Fenster lesen.
12 neue Proben, davon 5 Paritaets-Proben. Flip-Probe: Vorgabe testweise auf 07:00 gezogen -> genau diese 5 fallen, nichts anderes. Volle Reihe 489/489 (vorher 477).
Zwei info-Zeilen auf debug: der Takt laeuft jetzt 144-mal am Tag statt einmal.
OFFEN und gemessen: sendTime/sendDays/timezone stehen WEDER im Vertrag IPodcast NOCH im Podcasts-Schema (models 2026.456.0). Gelesen werden sie ueber .lean(), wie die Datei schon autoPublish und curation liest; GESCHRIEBEN werden koennen sie noch nicht (strict verwirft sie lautlos). Der Lese-Weg steht bereit, die Eingabe fehlt — Fundament-Glied als eigener Brief podcast-sendezeit-felder-am-vertrag.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UVmnsay4L5Rw8MvnFMVqmj
Fixplayground
offenen Weiterleiter haerten — Sperrliste, Rate-, Zeit- und Groessen-Deckel
Gemessener Ausgangszustand (2026-08-22): POST /api/playground/proxy war OHNE Anmeldung nutzbar, OHNE Rate-Begrenzung, OHNE Zeitlimit und OHNE Groessen-Deckel, und liess JEDE .ayoune.app-Unterdomaene zu — su-api (Superuser/godfather, laut Wurzel-CLAUDE.md ausdruecklich nie aus Kunden-Clients), credentials-api (Zugangs-Tresor) und registry (private Paket-Registry) eingeschlossen. Der Weiterleiter verbarg dabei die Herkunfts-Adresse des Aufrufers hinter der Pod-Adresse.
Der Riegel liegt in der REINEN Funktion checkPlaygroundTarget (kein Express, kein Netz, kein Zustand) — nur so ist er ohne laufenden Dienst pruefbar: - Sperrliste privilegierter Wirte, ausgewertet VOR der Nachsilben-Pruefung (sonst faellt su-api durch das -api-Muster wieder herein) - Anmeldedaten in der Adresse abgewiesen (verschiebt, was ein Mensch LIEST, ohne zu aendern, wohin die Anfrage GEHT) - ausdruecklicher Port abgewiesen (sonst Port-Abtaster fuer alles unter der Nachsilbe); der Regel-Port bleibt erlaubt - Rate-Begrenzung 20/min je Herkunft ueber das vorhandene checkRateLimit - Zeitlimit 10 s und Antwort-Deckel 512 KiB; redirect manual, weil eine Umleitung aus einem erlaubten Wirt einen gesperrten machen koennte, NACHDEM die Pruefung gelaufen ist
Verify: npm run build Ende 0 / 0 TS-Fehler; neue Reihe 19/19; volle Reihe des Repositoriums 1632/1632, 0 Fehler. Die Reihe traegt drei FLIP-PROBEN, die die alte Regel woertlich nachbauen und belegen, dass sie genau diese Faelle durchliess — samt Gegenkontrolle, dass das regulaere Ziel crm-api.ayoune.app bei BEIDEN Regeln durchgeht, der Playground also nicht gebrochen ist.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T2uHkCzw3A1XWKSCf6c7JF
Newtasks
type-Normalisierung (Stufe 2) + zwei nachgetragene status/priority-Luecken
CEO-Entscheid 2026-08-21 (E10). Neue Karte TASK_TYPE_MAP faengt die zwei gemessenen Schreibweisen-Dubletten am SCHREIBWEG ab: 'Code-Agent' -> 'CodeAgent', 'Task' -> 'Aufgabe'. Registriert an `Tasks` neben status/priority.
WARUM DER HAKEN UND NICHT DIE UNION: gemessen 2026-08-22 schreiben die realen Erzeuger ueber `any`/`string`-typisierte Objektliterale (`tooling/cli` dispatcher.ts:365 schreibt bis heute 'Code-Agent'; `platform/middleware-worker` handleBitBucket.ts:66) — eine geschlossene TypeScript-Union faengt davon NICHTS, dieser Haken alles. Er ist damit der tragende Riegel des Auftrags, nicht der Vertrag.
NICHT in der Karte: 'CodeAgent-Todo'. Am Bestand gemessen ein EINZELPUNKT INNERHALB einer Sitzung (origin `code-agent-hook:<id>`), nicht die Sitzungs-Aufgabe (origin `code-agent-hook`) — der lebende Erzeuger sucht ihn idempotent ueber genau diesen Wert.
ZWEI GEMESSENE LUECKEN NACHGETRAGEN (dieselbe Schreibstelle, middleware-worker, ausgerollt): `Erledigt` -> done und `Hoch` -> high waren der Karte UNBEKANNT und liefen damit unbehelligt in den Bestand. Wirkt zugleich fuer Projects/FeatureRequests, die dieselbe Karte teilen. Der Erzeuger liegt ausserhalb der Anspruchspfade — die Karte ist der Hebel ohne Fremd-Edit.
`waiting` (worker-outlook) bleibt BEWUSST unzugeordnet: es gibt kein offensichtliches Kanon-Ziel, und raten waere schlimmer als offenlassen. Liegt als Rest-Schnitt-Brief.
19 neue Proben, davon vier gegen das ECHTE Tasks-Schema (die Attrappen-Tests belegen die Mechanik, nicht die Verdrahtung). Flip-Probe: Haken-Zeile entfernt -> genau der eine Verdrahtungs-Test faellt, alles andere bleibt gruen. Volle Reihe 707/707.
interfaces-Caret auf ^2026.451.0 (Fundament-interne Kette bleibt Caret).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UVmnsay4L5Rw8MvnFMVqmj
CEO-Entscheid 2026-08-21 (decisions.log 20:22:43Z, LOCKED, E10). Der Kanon ist aus dem GEMESSENEN Bestand abgeleitet (42.521 Aufgaben, nach dem Dublettenlauf `ayauto migrate task-status-type-kanon`): elf Werte, `ITask.type` traegt jetzt `PmTaskTypeValue` statt `string`.
BEWUSST OFFEN gelassen (`| (string & {})`), wie `status`: das Schliess-Gate des Auftrags ist NICHT erfuellt. Gemessen schreiben die realen Erzeuger ueber `any`/`string`-typisierte Objektliterale (`tooling/cli` dispatcher.ts:365, `platform/middleware-worker` handleBitBucket.ts:66) — eine geschlossene Union faengt davon NICHTS. Der tragende Riegel ist der Normalisierungs-Haken in `platform/models`, nicht dieser Typ.
BEWUSST NICHT sprach-normalisiert: `type:'Bug'` traegt die plattformweite Errors-as-Tasks-Konvention (24.468 Dokumente). Eine Umbenennung auf `bug` waere ein Flotten-Eingriff mit Leser-Implikationen, kein Vertrags-Detail — Produkt-Entscheid, CEO.
`CodeAgent` und `CodeAgent-Todo` sind ZWEI Sachverhalte, keine Dublette (Sitzungs-Aufgabe gegen Einzelpunkt innerhalb einer Sitzung) — beide im Kanon, kein Merge.
Flip-Probe beidseitig gefahren: offen -> erfundener Wert geht durch; testweise geschlossen -> TS2322 auf `type` UND `status`, waehrend die Kanon-Positiv-Kontrolle still bleibt. Der Vertrag haengt also wirklich am Feld.
Nicht-brechend: PmTaskTypeValue ist von jedem string zuweisbar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UVmnsay4L5Rw8MvnFMVqmj
Newonboarding
user.invite-sent wird zu einer echten Einladungs-Mail
Der Einladungs-Weg endete nach dem aYOUneInvites-Datensatz: die Vorlage onboarding/invite-user-to-tenant liegt seit Monaten global in mails, aber eine Suche nach ihrem Namen ueber die ganze Flotte fand NUR den Seed — kein Verbraucher. Die eingeladene Person erfuhr nie von ihrer Einladung.
dispatchOnboardingMail haengt als fuenfter Zweig in defaultTriggers, ist also eine Plattform-Regel, die ein Mandant nicht loeschen kann (gleiche Begruendung wie bei den drei Geschwistern). Versand ueber den consumer-losen sendSecurityEmail-Weg, weil eine eingeladene Person noch keinen Consumers- Datensatz hat. Adresse auf PASSWORD_RESET_FRONTEND_URL mit Marken-Segment, nicht auf authHost — das ist die API, nicht die Oberflaeche.
zwoelf Nav-Beschriftungen und den Menue-Titel in den globalen Seed
Die Seitenleiste und die Strg+K-Palette zeigten rohe Uebersetzungs-Schluessel. Gemessen am ausgelieferten Sprachpaket (GET /locale/{de,en,it,es}.json), nicht am Quelltext: die PWA verweist auf 37 nav-Beschriftungen, zwoelf davon fehlten in ALLEN vier Sprachen. Der Befund nannte sechs; die restlichen sechs sind dieselbe Familie und standen roh im selben Bild.
Die deutschen und englischen Texte sind aus dem bereits sichtbaren Client-Rueckfall uebernommen (admin-nav-i18n.ts, me-sections-i18n.ts), nicht neu gewaehlt: der Rueckfall setzt nur Schluessel, die das geladene Paket nicht kennt, also gewinnt ab diesem Seed der Server. Eigene Worte haetten die Beschriftung beim Ausrollen still geaendert.
Dazu ein Waechter, der die verwendeten Schluessel aus den beiden Definitions-Dateien der PWA LIEST statt sie abzuschreiben, und sie gegen die Vereinigung aller Seed-Quellen haelt. Flip-Probe gefahren: ein entfernter Schluessel macht ihn rot.
Schluesselzahl im Kopf neu gemessen: 290 -> 303.
Newmigrate
task-status-type-kanon — Bestand von tasks.status/.type auf den Kanon
Bestands-Nachtrag zu CEO-Entscheid 2026-08-21 (E7+E10). Zwei getrennte Achsen:
status 'Nicht begonnen'->open, 'Abgeschlossen'/'Erledigt'->done, ... — die Paare stehen bereits in TASK_LIKE_STATUS_MAP, der Schreib-Haken deckt aber nur NEUE Schreibvorgaenge; dieser Lauf holt den Bestand nach. type NUR Schreibweisen-Dubletten: 'Code-Agent'->'CodeAgent', 'Task'->'Aufgabe'.
NICHT zusammengefuehrt wird 'CodeAgent-Todo': am Bestand gemessen ist das ein EINZELPUNKT INNERHALB einer Sitzung (origin code-agent-hook:<id>), nicht die Sitzungs-Aufgabe (origin code-agent-hook), und der lebende Erzeuger sucht ihn idempotent ueber genau diesen Wert.
Die Sprachmischung (Bug/Aufgabe/...) wird NICHT aufgeloest — Produkt-Entscheid, CEO.
Trocken als Vorgabe. --apply zieht VORHER ein vollstaendiges Rueckroll-Abbild und schreibt ohne dieses nicht (harter Abbruch, kein catch).
Gefahren 2026-08-22 gegen Produktion: 15 (status) + 302 (type) geschrieben, Zweitlauf 0 Kandidaten, Kreuzprobe 42521 == 42521 auf beiden Achsen. Abbilder mitversioniert unter .claude/reports/status-typ-kanon/.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UVmnsay4L5Rw8MvnFMVqmj
Fixonboarding
create-with-welcome legt username an — unique-Index ohne sparse liess jede Anlage in E11000 laufen (500)
Ursache am Bestand gemessen: ayouneusers traegt username_1 als unique OHNE sparse/partialFilterExpression, und der EINE erlaubte Platz ohne Wert ist seit dem 2026-07-12 belegt (1 von 200 Zeilen). Die Route legte ohne username an, also scheiterte JEDE Anlage — zweimal 500 ohne Datensatz, gemessen 2026-08-22.
Neu: deriveUsername leitet deterministisch ab (Adresse, sonst Adresse+Mandant, sonst zaehlend) und scheitert laut statt still. Zusaetzlich antwortet die Route auf einen Schluessel-Konflikt jetzt mit 409 statt 500. 8 Proben inkl. zwei Negativ-Kontrollen.
Newrag
hide-internal-doc-chunks in den rag-backfill-Verteiler haengen
Der Lauf `hideInternalDocChunks` war seit 0bafeff gebaut und getestet, aber nicht verdrahtet: der Auftragsname war dem Verteiler unbekannt, und der Rueckfall am Ende haette ihn still durch `backfillChunkLanguage` ersetzt -- also einen falschen Lauf ohne Fehlermeldung geliefert.
- src/index.ts: Einfuhr + Verteiler-Zweig VOR dem Rueckfall, Kopf-Kommentar der Auftrags-Liste um die Signatur ergaenzt. - package.json: `hideInternalDocChunks.core.test.mjs` in die Testkette.
Verify: Bau gruen; Testkette gruen und die neue Reihe belegt in der Ausgabe (`hideInternalDocChunks.core: 56 passed, 0 failed`). Flip-Probe gegen das kompilierte dist/index.js gefahren: mit dem Zweig HINTER dem Rueckfall landet `hide-internal-doc-chunks` in `backfillChunkLanguage` -- die Reihenfolge ist damit gemessen, nicht behauptet. Der Uebersetzer faengt es NICHT (der Zweig ist zwar unerreichbar, bricht den Bau aber nicht).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013DW1XRFQDrDUNPyT6mdAoB
Newcoord
Einzelschreiber-Riegel unterscheidet Reservierung von Schreiber (CEO 2026-08-22)
Der Fundament-Riegel sperrte bis dahin gleich hart, egal ob ein Brief in toPost LIEGT (Reservierung, niemand arbeitet) oder eine Sitzung AKTIV schreibt — und der ERSTE gefundene Halter gewann, unabhaengig von Prioritaet und sogar davon, ob er ueberhaupt dispatchbar war.
GEMESSENER SCHADEN (2026-08-22, DREI Faelle an EINEM Tag): - Ein P3 hielt den models-Platz vor einer P1-Kaskade mit drei Gliedern. - Ein P2 hielt den interfaces-Platz SECHS STUNDEN vor einem P1 — und war dabei selbst nicht dispatchbar (NOT-READY:fe-coverage-missing). Ein Brief, den niemand claimen KANN, blockierte einen, den jemand claimen soll. - Ein weiterer P3 rueckte sofort nach, als der erste geraeumt war. Alle drei mussten von Hand per demote --force weggeraeumt werden.
DIE REGEL: - active / active-session sperren IMMER — dort schreibt jemand; Prioritaet darf einen laufenden Schreiber nie verdraengen (Einzelschreiber-Garantie). - toPost sperrt nur gegen gleich- oder hoeherrangige Anwaerter — eine Reservierung weicht einem STRIKT dringlicheren Vorgang (gleicher Rang sperrt weiter, sonst entstuende ein Wettlauf zweier claimbarer Briefe). - FAIL-CLOSED: fehlt eine der beiden Prioritaeten oder ist sie unlesbar, haelt die Sperre — ein Tippfehler darf nie zum Freifahrtschein werden. - Rueckwaerts-vertraeglich: ohne uebergebene Prioritaet gilt das alte Verhalten.
VERIFIKATION: - 17 neue Proben, im test:coord-Fach namentlich verdrahtet. - Flip-Probe: Ausnahme neutralisiert → 4 von 17 rot, wiederhergestellt 17/17. - Ende-zu-Ende am ECHTEN Fall: status-und-typ-kanon (P1) war vom P2-Halter REFUSED, nach dem Fix "would promote". - Bestands-Reihe coord-admin 257/259 — die 2 roten sind VORBESTEHEND, beidseitig belegt per git stash (identisch rot am unveraenderten HEAD; byte-genaue Wiederherstellung per Pruefsummen-Vergleich). Beide ohne Bezug zum Riegel.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017TzYJQKJ6eGLzeZXmLMMxX
Fixpm
lesbare Beschriftungen — primary war als Text auf dunklem Grund blind
Sicht-Kritik am LIVE ausgebrachten Stand (pwa.ayoune.app, dunkles Thema), nicht am Entwurf: zwei Stellen faerbten Text in `--mat-sys-primary` und waren damit die am schlechtesten lesbaren der ganzen Seite.
- Der AKTIVE Schritt der Schiene traf es am haertesten — ausgerechnet seine Beschriftung war unlesbar, waehrend die Ziffer daneben (primary als FLAECHE mit `on-primary` darauf) einwandfrei stand. Die Betonung traegt jetzt Rahmen und Ziffer, der Text hat volle Textfarbe. - „Alle"/„Keines" sind Neben-Griffe: Sekundaerfarbe plus Unterstreichung, beim Ueberfahren volle Textfarbe.
Lehre: in einem dunklen M3-Thema ist `primary` als Flaeche gedacht, nicht als Textfarbe auf dunklem Grund.
Verify: `npm run build:stage` Exit 0 inkl. Schrift-Check.
Newi18n
Fehlertexte fuer die Firmen-Seite in Endkunden-Sprache
Sechs Schluessel fuer den neuen Fehler-Zweig der PWA-Firmen-Seite (`clients/mobile-app/.../company.component.ts`): eine Ueberschrift, die Zusicherung dass nichts verloren ist, und vier nach Ursache unterschiedene Meldungen (offline / keine Berechtigung / Dienst startet / Dienst antwortet nicht).
Anlass: die Seite zeigte bei einem fehlgeschlagenen Ladeaufruf ein leeres Formular ohne jeden Hinweis. Ein Rohtext der Gegenstelle taugt dafuer nicht — er lautete woertlich „Cannot read properties of undefined (reading 'findOne')".
Alle vier Sprachen (de/en/it/es) wie im Bestand, alphabetisch eingeordnet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixpm
Assistent nennt die richtige Grenze und sagt, WAS er liest
Zwei Befunde aus dem Live-Durchlauf gegen echte Repositorien (pwa.ayoune.app, Mandant tolinax) — beide gefunden, weil die Fläche wirklich gefahren wurde:
1. Der Abschneide-Hinweis schob JEDE Grenze auf die Tiefe und meldete am Wurzel-Repositorium woertlich "0 Eintraege unterhalb von Ebene 3" — der Leser war dort an der SEITEN-Grenze. Ein Hinweis, der die falsche Ursache nennt, ist schlimmer als keiner: er schickt den Leser die Tiefe erhoehen, wo die Seitenzahl das Problem ist. Jetzt getrennt benannt. 2. Filtert die Suche die gewaehlte Karte aus der Sicht, blieb "Struktur lesen" scharf, ohne dass irgendwo stand, WAS gelesen wird. Die Fussleiste nennt das gewaehlte Repositorium jetzt beim Namen.
`IEmployee.gender` stand im Vertrag und erreichte den Prompt-Bau nie — er las ausschliesslich `appearance`. Drei von sechs erzeugten Portraets trugen daraufhin das falsche Geschlecht ("Tobias Kern" als Frau, CEO-Sichtbefund 2026-08-22). Der Fehler war STILL: kein Fehlschlag, kein Protokolleintrag, nur ein falsches Bild.
- `PortraitSubject.gender` (`m|w|d|x`, 1:1 der Vertrag) + Satz im Prompt - `d`/`x` behaupten bewusst NICHTS — aus ihnen folgt kein Bild-Merkmal - fehlt das Feld, wird aus `appearance` abgeleitet (de+en, WORTGRENZEN: "woman" enthaelt "man"); nennt der Text beide, wird nichts abgeleitet - schweigen beide Quellen: `PortraitGenderUnknownError` statt Ratewurf, im Router als 422 mit echter Meldung statt 502 "bitte erneut versuchen" - der Aufrufer reicht `employee.gender` aus der Akte durch (die fehlende Zeile) - `buildStylizePrompt` bleibt BEWUSST ohne Geschlechts-Satz: beim Angleichen fuehrt das Foto, ein widersprechendes Feld erzwaenge den Gesichts-Tausch, den PORTRAIT_IDENTITY_CLAUSE verbietet - `portraitSeed` unberuehrt — die Szenen-Wahl darf sich nicht verschieben, wenn jemand ein Geschlecht nachtraegt
Proben: 915/915 gruen (13 neue, mit Umkehr- und Negativ-Kontrollen). Gegenprobe am gebauten dist fuer m / w / leer / leer+Freitext / d / Angleichung.
lokalen Schreibweg (applied$) an die Kopfleiste koppeln
Der Socket-Ruf des Servers war der einzige Abonnent der Kiosk-Einstellungen — `KioskSettingsService.applied$` (die HTTP-Antwort des eigenen Schreibvorgangs) hatte flottenweit null Abonnenten. Der Socket-Ruf ist aber best-effort: der Server schluckt jeden Fehler (config-api lib/kioskDeviceSettings.ts:169-173) und antwortet trotzdem mit 200. Ist der Melder nicht erreichbar oder das Geraet gerade nicht im Raum controlpanel:<id>, blieben Kopfleiste, Ton-Politik und Stumm-Riegel bis zum naechsten Neuladen auf dem alten Stand.
- applied$ abonniert, mit feedPanel=false (das Panel kennt den Wert aus seiner eigenen save()-Antwort; ein Rueckfluss ueber external$ waere eine zweite Zuweisung desselben Wertes) - 9 Proben: Kopplungswirkung, Panel-Rueckfluss-Schalter, Fern-Weg unveraendert, Geraet ohne Alarm-Anzeige, leerer Satz, plus die Bibliotheks-Kettenhaelfte gegen die echt installierte Lib (voller Server-Satz, Weg-Trennung, Fehlerfall) - angular.json: die neue Spec in die explizite include-Liste (ohne den Eintrag wird eine Spec-Datei hier NIE eingesammelt und laeuft still gar nicht mit)
Verify: Bau gruen (Application bundle generation complete), 188/188 Proben gruen, Flip-Probe beisst (Schalter ausgehebelt -> genau die Schalter-Probe rot).
Newpm
Assistent "Bilde deine Software ab" — Oberflaeche (W5, Weg b)
Statischer PWA-Baustein unter `pm/onboarding`, CEO-Entscheid 2026-08-22 Weg (b). Der Motor (scan-repository / apply-proposal, pm-api `a6b805b`) bleibt unangetastet.
Drei Schritte, und die Reihenfolge traegt eine Aussage: verbinden -> pruefen -> uebernehmen. Erst der dritte schreibt.
- Der BELEG steht sichtbar an jeder Zeile (die Manifest-Datei, die den Vorschlag traegt), nicht in einem Tooltip. Was nicht eindeutig war, steht als OFFEN gleichberechtigt daneben statt in einer Fussnote — ein Vorschlag, der seine Luecken verschweigt, sieht vollstaendig aus und ist es nicht. - Zuordnungen sind korrigierbar; ein abgewaehltes Modul zieht seine Zuordnungen mit, sonst zeigte die Oberflaeche eine Zuordnung, die der Server als OFFEN behandelt. Eine Komponente ohne geltendes Modul reist OHNE `moduleKey`. - Rechte prueft der Baustein SELBST (`pm.modules` / `pm.modules.edit`), weil kein SDUI-Zustand die Sichtbarkeit uebernimmt — ueber den treuen Spiegel `holdsRight`, also dieselbe Glob-Semantik wie `checkRights` im Kern. - Benannte Fehler statt leerem Vorschlag: ein fehlender Vault-Zugang wird als solcher gezeigt, nicht als leere Struktur.
Verify: `npm run build:stage` (das Gate, das master faehrt) Exit 0 inkl. Schrift-Check BESTANDEN; 16 Proben gruen; Flip-Probe am Rechte-Riegel faellt mit genau 2 roten Reihen und ist zurueckgebaut.
Fixcodegen
unbefuellter Aktions-Rumpf verweigert laut (422) statt still 200
Bis hierher verwarf ein Rumpf mit leerer ALLOWED_FIELDS jedes Rumpf-Feld, schrieb nur noch `_id` und antwortete 200 mit dem unveraenderten Dokument — fuer den Aufrufer von Erfolg nicht zu unterscheiden. Genau dieses Bild hat den Etikettendruck der Lagerplaetze sieben Wochen lang stillgelegt.
Der leere Vorgabewert bleibt RICHTIG (fail-closed; der Erzeuger kann die Felder einer Aktion gar nicht kennen). Geaendert ist, was der Rumpf damit tut.
- 422, nicht 501: 501 hiesse „nicht auf diesem Wirt" und trifft nicht zu; die Aktion IST zustaendig, sie hat nur keinen Rumpf. Vorbild: die dreifach im Bestand umgesetzte `refuseUnimplementedAction` (hr/config). - Der Sammel-Zweig behaelt sein 501 — dort ist die ADRESSE unbedienbar. Reihenfolge test-gepinnt: 501 greift VOR 422. - Kein Schalter, kein Opt-out: sobald ein Feld opt-in ist, faellt der Zweig weg und der Pfad laeuft unveraendert. - Die Kopf-Prosa beider Varianten nachgezogen; die emits-Variante sagt ausdruecklich, dass auch KEIN Ereignis mehr feuert (die Verweigerung greift vor dem Ereignis-Block).
Belege: echter Erzeuger-Lauf gegen cms/WebPages in einem Wegwerf-Ziel, der erzeugte Rumpf gelesen UND uebersetzt (npm run build Exit 0), danach voll zurueckgenommen (Arbeitsbaum sauber, Aktions-Verdrahtung 1 -> 2 -> 1). 11 neue Proben inkl. Kontrollfluss-Nachbildung und Flip (ohne die Wache 6 rot). Volle Reihe 4709/4717 — die 8 roten sind vorbestehend und beidseitig belegt.
⚠ first-write-wins: wirkt NUR auf kuenftige Stuempfe, die 146 Bestands- Fundstellen bleiben unveraendert. Der Melder zaehlt einen verweigernden Rumpf weiter — gemessen und richtig: er misst Funktionslosigkeit, nicht Stille.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newbriefings
Quelle "Entwicklung" am bestehenden Briefing-Motor
CEO-Wunsch 2026-08-22: taegliches Entwicklungs-Briefing, konfigurierbar wie der Podcast — "andocken und erweitern, nicht neu bauen".
Es ist eine QUELLE, kein zweiter Motor: derselbe Laeufer, dieselbe Verdichtung, dasselbe Gedaechtnis, dasselbe Konsistenz-Tor, dieselbe Audio-Kette, dieselbe Mail, derselbe Zeitplan (CHECK_TENANT_BRIEFINGS, werktags 07:00). Ein reines Entwicklungs-Briefing ist ein Automations-Objekt mit sources: ["development"] — kein neuer Zeitplan, keine zweite Pflegestelle, kein Codegen.
- developmentSource.ts REIN (kein ay, kein Mongoose, kein Netz): Umfang-Stufen, Filter, Rangfolge, Entdopplung, Rohstoff-Saeuberung, Endkunden-Regeln. - tenantBriefing.ts Quellen-Weiche in Aufforderung, Hoerstueck, Betreff, Kopf, Fuss und Gedaechtnis-Strang. - tenantBriefings.ts loadDevelopment() + Verdrahtung; nicht aktive Quellen werden gar nicht erst abgefragt.
Nicht-Brechen an SECHS Literalen festgenagelt (Betreff, Prompt-Kopf, Fuss "Internes Briefing", Audio-Anfang, Strang "tenant-briefing:intern", Vorgabe- Quellen). Beide LIVE-Objekte (Xamine, tolinax) tragen kein sources-Feld und verhalten sich unveraendert. 439/439 Proben gruen.
DREI Praemissen des Auftrags gemessen widerlegt: - Die Modul-Achse ist LEER: _module 30/5.665 (0,5 %), _component 2 (0,04 %), _repository zu 93 % EIN Wert. Gefiltert wird ueber commit.repo (100 %) und scope (98 %). Ein Filter auf _module haette lautlos alles weggeschnitten. - Releases haengt (juengster publishedAt 17.08., fuenf Tage alt) — Zusatz-Achse, nie Traeger. Zeit-Achse ist commit.date, nicht createdAt (der Nachfuell-Laeufer schrieb Jahrgaenge 2023/2024 an einem Tag). - Tasks.completedAt existiert NICHT (0 von 1.897) — es heisst doneAt (878). Erste Messung war eine falsche Null, gefangen von der Positiv-Kontrolle.
Zwei Fehler, die erst die Verdrahtung sichtbar machte: - Das Konsistenz-Tor bekam den Entwicklungs-Stoff nicht und haette JEDEN Entwicklungs-Punkt als "erfunden" gemeldet: Urteil rot, Korrektur wirkungslos, Versand nie. - Der Probelauf gegen echten Stoff sagte "keine Neuigkeiten" UND "Grundlage waren 25 Aenderungen" in derselben Folge — Stoff ohne Verdichtung wird jetzt als Ausfall benannt statt als Nachrichtenlage.
Probelauf gegen 31 echte Eintraege vom 21.08.: 25 ausgewaehlt, 13 Repositorien, Aufforderung 19.889 Zeichen, Mail 9.917 Zeichen, sechs Riegel gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014XVq1MjzHpUt7bUtRzJTny
Newpage-editor
Pflege-Flaeche fuer die Entitaets-Bloecke — Auswahl, Automatik, Filter
Der Renderer bedient elf Entitaets-Blocktypen; der Editor bot die Felder fuer drei an. Die uebrigen waren ueber die Schnittstelle voll bedienbar und ueber die Oberflaeche gar nicht.
🔴 Die elf zerfallen in DREI Klassen, nicht in eine — der Brief nahm eine an: • Im Katalog (references/partners/portfolio/jobs): Felder ergaenzt. `portfolio` hatte KEINEN Auto-Schalter, `jobs` trug null Entitaets-Schluessel, obwohl der Eintrag seit dem 2026-08-21 im Katalog steht und damit versorgt aussah. • Mit Sonder-Maske (gallery/faq/logos/testimonial/pricing): ein Katalog-Eintrag allein waere hier TOT — `block-renderer` hat fuer sie eigene @case-Zweige, der Katalog-Editor laeuft nur im @default. Sie bekommen den Katalog-Editor jetzt ZUSAETZLICH unter die Sonder-Maske; der Inline-Weg bleibt unangetastet. • Im Editor gar nicht vorhanden (awards/quotes): weder Kachel noch Maske — sie waren nicht bloss unpflegbar, sie waren nicht EINFUEGBAR.
Neu: `entity-picker-field` — kuratierte Auswahl MIT Reihenfolge. Ein `mat-select multiple` traegt das strukturell nicht (es liefert Options- statt Wahl-Reihenfolge), und bei der Galerie IST die Reihenfolge die Kuration; der Renderer haelt sie ausdruecklich ein. Verschieben per Ziehen, wie es dieser Ordner schon tut (team/stats/navigation).
Gemessen statt angenommen: die Entitaeten liegen NICHT alle im cms-Modul (References/Partners/CustomerLogos/Ratings/Awards/Portfolios → marketing, Faqs/Quotes → hub, HRJobs → hr, Images → cms, PricingTables → config). `hub` und `hr` kannte der Host-Aufloeser nicht — ein falscher Rechner liefert eine LEERE Auswahl, ohne dass etwas fehlschlaegt.
Der Kriterien-Riegel ist jetzt in der Oberflaeche sichtbar: `_imagesAuto` und `_quotesAuto` ziehen OHNE Kriterium NICHTS (Absicht — 1.303 Bilder bzw. 48.428 importierte Fremdzitate bei je einem Mandanten). Am Renderer gemessen sind das die einzigen zwei mit Riegel; `partners` etwa zieht auch ohne, dort waere die Warnung falsch.
Verify: Bau gruen, Typpruefung gruen, 50/50 Proben. Flip-Proben BEIDSEITIG — verdrehter Feldname, entfernter Riegel-Hinweis und falscher Rechner werden je rot gemeldet (4 Proben), ebenso eine zerstoerte Wahl-Reihenfolge; danach zurueckgerollt und wieder gruen. Die Galerie-Sonder-Maske ist unveraendert.
⚠ Der Ende-zu-Ende-Beleg am laufenden Editor (Feld setzen → speichern → ausliefern) steht aus — siehe Handoff.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015yTiSkb8SH9siwzkjmMDHT
Newrag
deterministische Bestands-Korrektur der Sprach-Beschriftung von collection-Abschnitten
Der Schreibweg liest die Sprache seit 1a32d05 ab statt sie zu raten — es kommt also nichts Falsches mehr dazu. Dieser Lauf raeumt den Altbestand: er setzt `metadata.language` aus dem Ursprungs-Datensatz neu und UEBERSCHREIBT dabei. `backfillChunkLanguage` fragt `$exists:false` und traegt nur Fehlendes nach — ein solcher Lauf liesse genau den Schaden stehen (84,5 % Widerspruch zum `Locale:` des Datensatzes).
Ausloesen: rag-backfill / "relabel-collection-language" { apply?, batchSize?, maxBatches?, startAfterId?, sourceIds?, maxUnsetRatio? } Ohne `apply` ein reiner Trockenlauf.
Gemessen (Produktion, rein lesend, 2026-08-22): - Text-Weg gegen Dokument-Weg entschieden, nicht geraten: 3 Fenster, 2 Quellen, 6.000 Abschnitte — 5.977/5.977 Uebereinstimmung, 0 Widersprueche; 23 ohne `Locale:` im Text (geteilte Datensaetze), die das Dokument trotzdem aufloest. Also: Dokument ist die Quelle, Text nur Rueckfall. - Auf den zwei Riesenquellen (96,6 % des Bestandes) ueber 60.000 Abschnitte: 0 unset, 0 skip, 93,1 % korrigiert oder gefuellt.
Zwei Befunde, die im Auftrag nicht standen: - `metadata.documentId` ist eine ZEICHENKETTE, `_sourceDocument` eine ObjectId. Der im Auftrag genannte Mengen-Lookup ueber `documentId` liefert typ-bedingt null Treffer OHNE Fehler (0 von 2.000 gegen 2.000 von 2.000) — der Lauf haette daraus "keine Sprache" gelesen und auf 14,6 Mio Dokumenten geloescht. - Die Entfern-Regel gilt nicht ueberall: die Loesch-Menge sind die prosa-nahen Mandanten-Modelle (WikiPages Median 2.204 Zeichen, Contents 2.989), wo franc die richtige Mechanik ist und kein `Locale:` gerendert wird. Deshalb im Betrieb ueber `sourceIds` beschraenken; die Loesch-Bremse (maxUnsetRatio, Vorgabe 5 %) bricht einen unbeschraenkten Lauf ab, BEVOR er schreibt.
Entscheidung und Schleife liegen boot-frei im Kern, damit Trockenlauf und Ernstfall dieselbe Schleife fahren. 69 Proben, in `npm test` aufgenommen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixrag
internal-Riegel im laufenden Abgleich — interne Seiten wurden endkunden-sichtbar
`incrementalSync` schrieb `consumerVisible: source.consumerVisible || false`, waehrend sein Zwilling `structuredDataChunker.ts:125` seit dem 2026-05-26 (`758d69e`, "wiki-internal leak fix") den Riegel `&& doc.internal !== true` traegt. Der Abgleich ist der LAUFENDE Schreiber (alle 30/60/120 min je Quelle): eine interne Wiki-Seite, die nach der Erst-Indizierung bearbeitet wurde, lief durch den Weg ohne Riegel und wurde dabei endkunden-sichtbar.
Gemessen (Produktion, rein lesend, 2026-08-22): 18 von 18 Wiki-Seiten mit `internal: true` haben endkunden-sichtbare Abschnitte — 46 aktive Abschnitte ueber zwei Mandanten, kein einziger korrekt verborgen. Alle 18 Seiten sind NACH der Haertung des Zwillings entstanden (aelteste 2026-07-22), die Ursache ist also dieser Weg und nicht Alt-Bestand. Direkter Beleg: Seite 6a82e86536b34963d60a2b78 traegt `internal: true` seit 11:53:54Z, ihr Abschnitt wurde am 2026-08-18T12:30:04Z neu geschrieben — sichtbar.
Zweiter Zwilling geprueft: die Objekt-Literale sind sonst feldgleich. Die Abweichung bei `_customerID` ist begruendet (globale Quellen erben den Stempel des Rebuilds, incrementalSync.ts:51-70), also kein zweiter Befund.
⚠ Der Bestand heilt hierdurch NICHT: beide Zwillinge ueberspringen Abschnitte mit unveraendertem `textHash`, und der Schluessel enthaelt `consumerVisible` nicht — eine reine Flag-Aenderung loest also weder hier noch im Rebuild ein Neuschreiben aus. Die 46 brauchen einen eigenen Korrektur-Lauf; Abbild liegt unter .claude/reports/rueckroll-abbilder/.
Verify: Flip-Probe gegen den gebauten Ausdruck beidseitig (internal:true unter sichtbarer Quelle true→false), zwei Negativ-Kontrollen unveraendert (ohne `internal` bleibt sichtbar; Quelle consumerVisible:false bleibt unsichtbar), 6/6 gruen; Ausdruck jetzt identisch mit dem Zwilling; Regression 29/29.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015yTiSkb8SH9siwzkjmMDHT
Newpages
Galerie-Block speist aus der Mediathek Images (W9 Glied 2)
CEO-Entscheid 2026-08-22 Weg (a): der `gallery`-Block bekommt eine Entitaets-Quelle aus dem LEBENDEN Bild-Bestand statt aus der Leer-Entitaet `Collections` (0 Dokumente ueber alle 90 Mandanten, 0 Verbraucher, kein W0-Kanon). Kein Fundament-Anteil, kein neuer Blocktyp, die Blocktyp-Zahl bleibt unveraendert.
Zwei Wege wie bei references/portfolio/quotes: - ausdrueckliche Auswahl `content._images[]`, Reihenfolge der Redaktion - Auto-Zug `content._imagesAuto` mit Kriterium `_imageTags` / `_imageCategory`
IImage traegt KEINEN W0-Kanon (kein published/_websites/sortOrder, am Vertrag UND am Schema gemessen, Positiv-Kontrolle IReference.published). Daraus folgen die zwei quotes-Vertraege: die ausdrueckliche Auswahl filtert nicht, der Auto-Zug verlangt ein Kriterium und fragt ohne eines die Datenbank gar nicht erst. Der Grund ist hier schaerfer als bei den Zitaten: images ist die MEDIATHEK - bei EINEM Mandanten 1.303 Bilder; ein kriterienloser Zug haette die halbe Mediathek auf eine oeffentliche Website gestellt, ohne dass etwas fehlschlaegt.
Gemessene Achsen (Prod, 3.384 Bilder): tags 459 · category 0 · _contentCategoryID 1. tags ist die einzige lebende Gruppierungs-Achse; category bleibt als Filter angeboten (das Feld existiert), _contentCategoryID bewusst nicht.
Alternativtexte nach der bindenden S8-Semantik (altTranslations[locale] vor alt), inkl. der Mongoose-MAP-Form: auf einem hydrierten Dokument liefert doc.altTranslations['en'] sonst still undefined und der Besucher laese die Vorgabe-Sprache, obwohl die Uebersetzung vorliegt (357 Bilder tragen sie).
Nicht-Brechen: ein Block ohne Entitaets-Bezug laeuft durch den unveraenderten Inline-Zweig - byte-identisch, als Literal festgenagelt, mit Umkehrprobe.
28 neue Proben, alle vier Flip-Proben beissen (Kriterium-Riegel, Map-Form, Inline-Ausgabe, Reihenfolge). Voller Lauf des Repositoriums: 101 Suiten / 2391 Proben.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014XVq1MjzHpUt7bUtRzJTny
Fixcoord
foundation-freshness hint no longer suggests --pin-exact for the 6 transitional shared libs
The freshness hint at coord claim treats any non-exact pin as "kein exakter Pin" and recommends --pin-exact for it — correct for normal consumers, but wrong for the six shared libraries (ayoune-theming, ayoune-device-shell, comm-sidebar, notifications, permission-editor, communicator) that CLAUDE.md's transitional override explicitly keeps on ranges until the deployment-plan bump_dependency step carries them along. Following the hint there creates a second Foundation resolution at their consumers (measured 2026-08-02, 11 affected pairs).
TRANSITIONAL_RANGE_LIBS excludes exactly those six from the hint; everything else is unaffected (flip-probe-verified, gegenprobe for a 7th unlisted lib).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Der fehlende Baustein war gemessen: die Plattform liest Repositorien, Zweige und seit dem 2026-08-13 Commits — aber es gab plattformweit KEINEN Leser der Verzeichnis-STRUKTUR. Ohne ihn hat ein Onboarding-Assistent nichts, woraus er vorschlagen koennte.
- repoStructure.ts Baum-Leser fuer bitbucket/github/gitlab auf EINE flache Form; Klient injizierbar (Proben ohne Netz); Zugang ausschliesslich aus dem Credential-Vault des Mandanten. - proposeStructure.compute.ts REIN: Baum -> Vorschlag. Schliesst nur auf BELEGE (Manifest-Datei), nie auf Ordnernamen — sonst haette das Verify-Gate "echtes fremdes Repositorium" strukturell nie bestanden. Kein Sprachmodell: die Eingabe ist Struktur. - applyStructure.compute.ts REIN: Bestaetigung + Bestand -> Schreib-Plan. Wiederholbar (zweiter Lauf legt nichts doppelt an), ergaenzt nur und ueberschreibt nie. - actions/ POST /modules/actions/scan-repository (lesend, schreibt NICHTS) und /apply-proposal (schreibt nur Bestaetigtes). Keine neuen Rechte: pm.modules bzw. pm.modules.edit.
Dogfooding am eigenen Bestand: Bauform korrekt erkannt, 37 Module / 183 Komponenten vorgeschlagen, 250 Punkte ausdruecklich OFFEN statt geraten.
Dabei einen echten Defekt gefunden und behoben: der Tiefen-Deckel verschluckte `domains/finance` lautlos (Manifest eine Ebene tiefer) — weder vorgeschlagen noch als offen gemeldet. `droppedByDepth` meldet die Grenze jetzt; Regression gepinnt.
38 neue Proben, volle Reihe 726/726 gruen.
Fixcourses
Wiederholungs-Versuche deckeln und benennen
Am laufenden Dienst gefunden, nicht am Reissbrett: nach dem Abschluss eines Durchgangs fiel der Stand bei der naechsten Antwort kommentarlos von 2/2 auf 1/2 zurueck. Ursache ist ein NEUER Ergebnis-Datensatz — richtig im Ergebnis (Exams.retries sieht Wiederholungen vor), aber unangekuendigt und ohne Deckel.
Drei Maengel behoben:
1. `retries` wird geprueft. Die Lesart ist ausdruecklich festgeschrieben: retries sind die WIEDERHOLUNGEN, der erste Durchgang zaehlt nicht dazu (retries:2 = drei Durchgaenge). Der Vertrag sagt es nicht, und beide Lesarten sind vertretbar - wer sie anders liest, deckelt um eins daneben. Aufgebraucht -> 409 versuche-aufgebraucht.
2. Der NEUESTE Versuch bestimmt den Stand. Liste und Detail sortierten nicht; bei mehreren Ergebnis-Datensaetzen war unbestimmt, welcher den Stand liefert - die Uebersicht zeigte je nach Fund-Reihenfolge der Datenbank mal den alten, mal den neuen.
3. Die Antwort meldet `neuerVersuch`, damit die Oberflaeche den Ruecksprung erklaeren kann statt ihn geschehen zu lassen.
Dabei von der Testreihe eine Null-Falle gefangen: Number(null) ist 0, nicht NaN - ein nicht gesetztes `retries` haette den Deckel still auf EINEN Durchgang gesenkt statt auf den Vorgabewert zu fallen.
69 Tests, Flip-Probe auf den Deckel gefahren.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newi18n
Kiosk-Sprachschluessel der Einarbeitung (kiosk-w5, Punkt 17)
31 Schluessel fuer die Einarbeitungs-Flaeche plus drei fuer die vertieften Produktdaten (Merkmale, Varianten), je de+en — wie der Terminal-Katalog es im Dateikopf verlangt.
Festgehalten und ausdruecklich NICHT nachgezogen: `terminal.nav.personal` fehlt in diesem Seed (die Selbstbedienung wurde nur im gebuendelten Terminal-Katalog gepflegt). Das ist ein fremder Auftrag; der Kommentar haelt die Luecke fest, damit sie der naechste Leser nicht fuer Absicht haelt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newlearning
Einarbeitung am Arbeitsplatz + vertiefte Produktdaten (kiosk-w5)
Punkt 17 — Einarbeitung: Neue Flaeche /learning + /learning/:id. Zugewiesene Kurse mit Stand, Pflichtaufgaben mit Frist und Quittierung, Lektions-Ablauf mit Frage, Rueckmeldung und Erklaerung.
Rollenneutral und deshalb OHNE roleGuard — die Einarbeitung ist kein eigener Arbeitsplatz-Typ, sondern ein Bereich, den jede Rolle tragen kann. Der Sidebar-Eintrag erscheint trotzdem nur, wenn wirklich etwas zugewiesen ist (Tote-Flagge-Regel wie Service-Desk und Selbstbedienung).
Der Dienst schickt KEINEN Besitz-Filter mit — die hr-api leitet den Besitzer aus dem Anmeldezeichen ab (/courses/own). Die Bewertung macht der Server; diese Seite kennt die Loesung gar nicht.
`learning` in TERMINAL_TOP_ROUTES — ohne den Eintrag haelt der Serial-Parser das erste Pfad-Segment fuer eine Geraete-Seriennummer und der roleGuard wartet auf eine Rollen-Aufloesung, die nie kommt.
Punkt 16 — Produktdaten: Die Detailseite hatte Stammdaten, Masse, Bild und Lagerstand bereits. Ergaenzt sind die MERKMALE — 570 Produkte tragen gepflegte Attribute, die Seite zeigte davon nichts — sowie die Varianten-Karte. Die Lagerort-Zeile verlinkt jetzt auf /warehouse/storage/:id, statt den Ort ein zweites Mal auszubreiten (verlinken statt nachbauen).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newcourses
eigene Einarbeitung am Arbeitsplatz (kiosk-w5, Punkt 17)
Zugewiesene Kurse, Lektionen, Fragen und Pflichtaufgaben mit Frist — ausschliesslich die EIGENEN. Der Besitzer kommt aus dem Anmeldezeichen, nie aus dem Aufruf: der Arbeitsplatz ist ein geteiltes Werkstatt-Geraet.
GET /courses/own GET /courses/own/:id POST /courses/:id/actions/answer-own GET /mandatorytasks/own POST /mandatorytasks/:id/actions/acknowledge-own
Kein eigener Fortschritts-Traeger: geschrieben wird `ExamResults` (Modul hub, sichtbar unter hub.examresults). Wer am Kiosk eine Lektion abschliesst, hat sie abgeschlossen — nicht „am Kiosk abgeschlossen".
Die Bewertung macht der Server. `answers[].correct` wird entfernt, bevor eine Frage das Geraet erreicht; die Oberflaeche kennt die Loesung nicht.
Zwei Zuweisungs-Wege (Team + Pflichtaufgabe), weil der Bestand zwei fuehrt — ohne beide waere eine leere Liste mehrdeutig. Ohne offenen Weg entsteht KEIN Filter (sonst lieferte er alle Kurse des Mandanten).
Vier eigene Rechte ueber additionalRights, kaskadenfrei — bewusst nicht `hr.courses`/`hr.mandatorytasks`: das sind die Rechte der Personalabteilung auf ALLE Datensaetze.
Teams/Exams/ExamResults zu modelsToLoad (fallen durch den hr-Filter); alle drei am installierten Paket geprueft, Umkehrprobe negativ.
60 Tests, drei Flip-Proben gefahren (Null-Weg-Riegel, Loesungs-Leck, Mengen-Gleichheit) — alle beissen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Perfgantt
Ladepfad folgt der Dimension, Zeitfenster statt pauschalem Deckel
Gemessen am groessten Projekt der Plattform (aYOUne, 4206 Aufgaben): der Endpunkt braucht 6,2 s, davon 4,4 s allein fuers Laden — auch fuer nur-ausfuehrung, das nur Sitzungen und Auftraege zeigt und dafuer 4206 Aufgaben und 5022 Stories VOLLSTAENDIG holte, um von ihnen nichts als die Kennungen zu benutzen. Der Deckel LEVEL_LIMIT war nicht die Ursache.
- loadCascade nimmt Ebenen und Zeitfenster entgegen; ohne Angabe unveraendert - neededSources und dateWindowFilter als reine, gepruefte Helfer in ganttTypes - Ebenen, die nur als Anker gebraucht werden, kommen als _id statt 15 Feldern - das Fenster greift NUR an den Blatt-Ebenen; Zeilen ohne Termin fallen NIE heraus, und Sitzungen folgen ihren Ankern von selbst - LEVEL_LIMIT bleibt letzte Sicherung
Die Invariante ist als Probe festgenagelt: fuer jede Dimension baut die beschnittene Kaskade knotengleich zur vollen, mit Negativ-Kontrolle.
Befund nebenbei: 5022 von 5022 Stories haben KEINEN Termin und reissen den 4000er-Deckel schon heute — die Kappung war also laengst still wirksam. Eine reine Zeitraum-Filterung haette sie alle entfernt und 4190 der 4206 Aufgaben ihre Eltern genommen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newaudit
Arbeitsliste — wo gearbeitet wird und das Modul fehlt
Der Melder repo-platform-drift kannte die Sitzungs-Achse nicht. Er meldete 221 Repositorien ohne Modul-Zuordnung, ohne zu sagen, in welchem davon ueberhaupt gearbeitet wird. Ergaenzt wird genau diese Achse — kein zweiter Ableiter: der Vorschlag stammt aus dem BESTEHENDEN deriveModule.
- Er schlaegt vor, er setzt nicht. Ein Mensch entscheidet je Zeile. - 'Kein Vorschlag' ist ein Ergebnis mit Grund, kein leeres Feld: der Ableiter greift nur unter domains/, ausserhalb ist es eine Entscheidung. - Die zweite Haelfte traegt den wichtigsten Fall: eine Plattform-Zeile ohne Gegenstueck im Inventar. Die Grundgesamtheit 'verbunden' schliesst das Wurzel-Repositorium aus, an dem 5.318 von 5.331 Sitzungen haengen. - Eine nicht gemessene Sitzungs-Achse meldet das ausdruecklich, statt eine leere Liste zu zeigen (falsche Null).
23 neue Proben, fuenf Flip-Proben beissen (8/1/2/4/1).
Vier CEO-Befunde der Runde vom 2026-08-22 (Bildbelege 5a, 8b-8d).
5a -- Herkunft anklickbar. GEMESSEN, statt die Kette zu erfinden: nur `model`/`modelName` benennt eine Entitaet; domain/group/nameSpace/appName sind Ordnungs-Begriffe des Erzeugers und zeigen auf nichts Oeffenbares. Und es bleibt beim Verweis auf die ENTITAET: der Zustand traegt keine Dokument-Kennung -- `entityId` sieht wie eine aus, ist aber ein Slug (an 307 k8deployments-Zustaenden in der Produktion gemessen). Als AUSGABE, nicht als Router-Aufruf: die Bibliothek bleibt router- und netzfrei.
🔴 Die verlangte Kette Cluster -> Projekt -> Modul -> Komponente -> Feature ist NICHT gebaut, weil die Kanten fehlen: IK8Deployment traegt _project, _cluster und _repository, aber weder _module noch _component noch _feature (gegen interfaces 2026.447.1 gemessen, Positiv-Kontrolle greift). Befund in der Uebergabe.
8b -- Knopf-Kontrast. Der Befund war keine schlechte Farbwahl, sondern eine Bauform: der Grund war ein 22-Prozent-Schleier UEBER dem Untergrund, die Schrift ein fester heller Ton. Ein durchscheinender Grund kann seinen Kontrast per Bauart nicht kennen. Jetzt deckend in Marken-Farben mit weisser Schrift; Kontrast gerechnet und mit Flip- UND Umkehrprobe belegt (alt auf hell faellt durch, alt auf dunkel bestand -- daher fiel es nie auf).
8c -- Spracheingabe. Die bestehende Diktier-Direktive aus @tolinax/ayoune-speech angeschlossen, nichts gebaut: Umschalten statt Halten, Text erscheint waehrend des Sprechens. Optionale peer-Abhaengigkeit -- von vier Verbrauchern nutzen nur terminal und control-panel das Aufgaben-Detail, und beide fuehren das Paket schon.
8d -- Verlaufslinie. Die Auftrags-Annahme „die Kurve gibt es schon, im Zustands-Detail" traegt nicht: jene laedt sich selbst ueber eine stateId aus statelogs, und eine Aufgabe hat keine. Wiederverwendet ist stattdessen der Sparkline-Helfer des Fehler-Centers DIESER Bibliothek, samt seinem Erzeuger-Muster (der Wirt liefert dort seit jeher stats.sparkline).
5 neue Proben zu 5a + 6 zum Kontrast, je mit Positiv-Kontrolle und Umkehrprobe, flip-geprueft. Volle Reihe 299/299.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newworkspace-create
der Tagespreis steht in der Groessen-Zeile
Die Auswahl-Zeile nennt jetzt den Kundenpreis je Tag, wo der Dienst einen liefert — zwischen Rechner-Groesse und Abschalt-Frist. Betrag und Waehrung kommen aus `GET /workspaces/templates` (`dailyPrice`), formatiert wird mit `Intl.NumberFormat` nach der Sprache der Oberflaeche (de `1,30 € pro Tag`, en `€1.30 per day`) — NIE als roher Fliesskomma-Wert.
- Ohne Betrag bleibt die Zeile unveraendert: kein Platzhalter, kein Strich, keine 0,00 € (ein Nullbetrag saehe aus wie kostenlos). - Die Notiz unter der Auswahl folgt dem GEMELDETEN Preis-Zustand des Dienstes (`pricing: live|unavailable`) statt eines eingefrorenen Satzes; sie behauptete bisher, es gebe keine Preisangabe. - Das Einheiten-Wort ist als Zwei-Sprachen-Karte im Baustein — benannte Abweichung: diese Flaeche haelt keine i18n-Datei, ein Schluessel ohne Server-Seed rendert roh. Begruendung steht an der Karte.
Proben: 20 gruen (Karma, ChromeHeadless), darunter die KETTE Dienst-Antwort → `enumValues`-Label, die Negativ-Kontrolle ohne Betrag und die Umkehrprobe `preiseLive` folgt dem Feld, nicht den Zeilen.
⚠ Karma meldet Exit 0 AUCH bei roten Faellen — der Beleg ist die TOTAL-Zeile. 🔴 `Intl` setzt ein GESCHUETZTES Leerzeichen (U+00A0) vor das Waehrungszeichen; eine Erwartung mit gewoehnlichem Leerzeichen faellt mit einer Meldung, in der beide Zeichenketten identisch AUSSEHEN (hier 3-mal passiert).
Newworkspaces
Tagespreis der Groessen-Leiter — live aus der Anbieter-Preisliste
Die Route meldete das Literal `pricing: 'unavailable'`. Sie leitet den Kunden- Tagespreis jetzt LIVE aus `GET /v1/pricing` ab — derselben Ressource, derselben Formel (min(Std x 24, Monat / 30)) und demselben Aufschlag (x2, CEO 2026-08-10), aus denen der Abrechnungs-Lauf seine Betraege bildet. Wizard und Rechnung koennen damit per Bauart nicht auseinanderlaufen (CEO-Entscheid 2026-08-22, Wege A1+B1).
- lib/templatePricing.ts — reine Ableitung, Zwilling von hetznerBilling.ts (gespiegelt statt importiert, mit Begruendung); Waehrung wird GELESEN, nicht angenommen; location aus der Vorlage statt hartkodiert. - lib/hetznerPricingSource.ts — Zugang aus dem Vault (CEO 2026-08-15), ausdruecklich OHNE Umgebungs-Rueckfall: ein fremdes Konto zeigte einen falschen Listenpreis. Vorhalt 15 min. Wirft per Bauart nie. - Ein Fehlschlag reisst die Groessen-Leiter NICHT mit — dann bleibt es bei pricing: 'unavailable', der ehrlichen Richtung. - costAggregator bleibt bewusst tot: befuellen hiesse eine ZWEITE, eingefrorene Preis-Quelle neben der live entstehenden Rechnung.
Proben: 23 neu (Formel, Waehrung, Standort, Vault-Feld, die drei Betraege, Negativ-Kontrolle ohne Preisliste, Groesse ohne Zeile). Flip-Proben beidseitig: Aufschlag 2->1 und min->max machen je 3 Faelle rot, rueckstandsfrei zurueck.
Newcoding
Modul-Zuordnung und Wissens-Kontext im Job-Kontext
Ein Coding-Job kannte sein Repositorium, aber nicht das Modul dahinter - und konnte einen Doku-Vorschlag deshalb nicht verorten. performDispatch ist der EINE geteilte Ausliefer-Kern hinter allen vier Eingaengen; der Block haengt dort, nicht in einem zweiten Weg.
- Die Kette wird GELESEN (Repository._module/._component), nicht aus dem Pfad abgeleitet - der Ableiter bleibt im Melder repo-platform-drift. - Der Wissens-Kontext kommt aus GET /{modules,components}/:id/knowledge (pm-api), kein zweiter Aufloeser, kein zweites Retrieval. - Die Luecke wird BENANNT statt weggefiltert; gemessen tragen 5.318 von 5.331 Sitzungen ein Repositorium ohne Zuordnung. - Mandanten-Grenze: _customerID-gebundener Lesezugriff, Token des ausloesenden Benutzers, bewusst ohne Zwischenspeicher. - IRepository fuehrt KEIN name-Feld (gemessen 0 von 200) - Anzeige ueber friendlyName > repoFullName > repoSlug, per Umkehrprobe festgenagelt.
Drei CEO-Befunde der Runde vom 2026-08-22, zwei davon aus EINER Ursache.
1) Stumm-Knoepfe antworteten „until fehlt" und haben nie eine Stummschaltung angelegt. GEMESSEN sass der Fehler auf der CLIENT-Seite: der bibliothekseigene Anbieter schickte eine DAUER (`minutes`), der Vermittler verlangt einen ZEITPUNKT (`until`, buildSnoozeDoc in @tolinax/ayoune-core). Die einzige andere Bindung (clients/desktop-client) rechnet seit jeher korrekt um -- ein „Fix" am Vermittler haette sie mitgerissen und eine zweite Drift erzeugt.
2) Weisser Grund im Dunkeln UND Fremd-Rahmen im Hellen sind EIN Fehler: dem Akustik-Fenster (seit 2026-08-21) fehlte sein [popover]-Ruecksetz-Block, also schlug das Browser-Blatt durch (`background-color: Canvas`, `border: solid`). Der Block existierte fuer die fuenf aelteren Top-Layer-Flaechen.
3) Quittieren war rot und teilte sich die Zeile mit den drei Stumm-Knoepfen. Jetzt eigene Zeile, volle Breite, 60px hoch, Farbe aus --brand-primary. Der rote Rahmen der Karte BLEIBT -- er zeichnet den Alarm, nicht die Handlung.
Karte, Innentexte und Stumm-Knoepfe ziehen gegen --brand-background/--brand-text (bisherige Werte als Rueckfall), damit ein heller Mandanten-Anstrich traegt.
Proben: 8 zum Rumpf-Vertrag + 10 zum Top-Layer (als REGEL ueber alle Flaechen aus dem Bauplan, nicht als Einzelfall), je mit Positiv-Kontrolle und Umkehrprobe, beide flip-geprueft. Volle Reihe gruen.
Nicht enthalten: der vorgelesene Satz (CEO „Alerts created by System"). Er entsteht in domains/config/worker-notifications/src/lib/alertTts.ts:101 aus subject+body -- ausserhalb der beanspruchten Pfade dieses Auftrags.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newkiosk-keyboard
fokussiertes Feld wird freigescrollt (CEO-Bildbeleg 8a)
Die Auftrags-Praemisse war widerlegt: die Bibliothek meldet ihre Hoehe SEIT JEHER (CSS-Variable --gkb-h + .gkb-open, `hoehenEffekt`), und terminal wie control-panel verbrauchen sie. Die Ausgangs-Messung suchte nach `keyboardHeight|scrollIntoView| offset` -- englische Namen fuer eine deutsch benannte Mechanik -- und lieferte eine falsche Null.
Was wirklich fehlte, ist die zweite Haelfte: freischeuchen, wenn niemand Platz reserviert. Die Variable hilft nur einer Flaeche, die sie liest; ein Feld, das eine BIBLIOTHEK rendert (Bildbeleg 8a: Kommentarfeld im Aufgaben-Detail von ayoune-kiosk-dashboard), liegt ausserhalb jedes Wirt-Layouts.
- keyboard-inset.util.ts: reine, browserlose Regeln -- scrollDeltaForField (nie weiter als Platz ueber dem Feld), isScrollContainer, reportedKeyboardHeight - Komponente scrollt den naechsten scrollbaren Vorfahr (Dialog!), sonst das Fenster - Neues oeffentliches Signal `hoehe` neben der CSS-Variablen - 13 neue Proben (62 gesamt) inkl. Positiv-Kontrolle, Umkehr- und Flip-Probe
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixportal-invite
Token nur noch gehasht in Consumers.verificationCode
Der von send-portal-invite geminteten Einladungs-Token wurde im KLARTEXT in die Consumer-Zeile geschrieben. Wer die Datenbank lesen kann, konnte damit jeden offenen Einladungs-Link uebernehmen (Passwort setzen = Konto- Uebernahme). Dieselbe Fehlerklasse wie storeevent-persistiert-rohen-reset- token (behoben 2026-08-20).
Neu: hashPortalInviteToken = sha256(token) hex; nur der Digest geht in die Zeile. Der Roh-Token verlaesst diesen Prozess ausschliesslich in setPasswordUrl, also in der Mail.
🔴 AUSBRINGUNGS-REIHENFOLGE, nicht optional: clients/consumer-app MUSS ZUERST live sein. Sein Leser ist beidseitig (Hash zuerst, Roh-Wert als Uebergangs-Rueckfall). Geht dieses Repositorium zuerst raus, schreibt es Hashes, waehrend der alte Leser nur den Klartext kennt — jede neue Einladung waere unaufloesbar, ohne Fehlermeldung.
⚠ ZWILLING: dieselbe Funktion liegt in clients/consumer-app/src/routes/data-fetcher.ts, genau wie mintPortalInviteToken schon vorher. Laufen sie auseinander, findet der Nachschlag einfach nichts — kein Fehler, kein Protokolleintrag. Beide Proben-Reihen nageln denselben fest verdrahteten Digest fest; das ist die einzige Stelle, an der eine Abweichung auffaellt.
Die zwei bestehenden Proben, die verificationCode auf /^pi-/ pruefen, sind auf den neuen Vertrag umgestellt — sie hielten das alte, unsichere Verhalten fest und wurden von dieser Aenderung erwartungsgemaess rot (der Diff zeigte den Digest an der Stelle des pi-Tokens). Fuenf neue Proben dazu. 22/22 gruen, tsc --noEmit Exit 0.
⚠ Scoped Commit: der Arbeitsbaum trug zugleich 227 fremde Dateien aus einem laufenden Codegen-Lauf. Sie sind NICHT Teil dieses Commits.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixportal
Einladungs-Token nur noch gehasht persistieren (Leser beidseitig)
Der einmal verwendbare Portal-Einladungs-/Reset-Token lag im KLARTEXT in Consumers.verificationCode. Wer die Datenbank lesen kann, konnte damit jeden offenen Einladungs-Link uebernehmen — Passwort setzen ist Konto-Uebernahme. Dieselbe Fehlerklasse wie storeevent-persistiert-rohen-reset-token (behoben 2026-08-20); die Nutzer-Ebene (platform/auth password.ts) hasht laengst.
Neu: hashPortalInviteToken = sha256(token) hex. In der Zeile liegt nur noch der Digest; der Roh-Token verlaesst den Prozess ausschliesslich in der Mail-Adresse.
Reihenfolge ist tragend — der LESER kommt zuerst und ist beidseitig: findConsumerByPortalInviteToken fragt ZUERST den Hash und faellt danach auf den Roh-Wert zurueck, damit vor dem Umbau geminteten Einladungen einloesbar bleiben. Der Rueckfall traegt ein Ablaufdatum im Kommentar (ENTFERNEN AB 2026-08-29 = Umbau-Tag + PORTAL_INVITE_TTL_MS); danach kann kein Roh-Token mehr gueltig sein, weil inspectPortalInviteToken vorher abweist.
Bewusst OHNE pi-Praefix am Hash: ein reiner Hex-Digest kann per Bauart nicht mit pi- beginnen, der Praefix bleibt damit die Trennung zum Telegram- Anmelde-Wert in DERSELBEN Spalte. Gemessen 2026-08-22: 158 Zeilen mit verificationCode, davon 2 mit pi-; die uebrigen 156 gehoeren Telegram und werden nicht angefasst. Von den 2 war genau EINE noch innerhalb der Lebensdauer (gesetzt 07:30Z), die andere 24 Tage alt und damit laengst abgewiesen.
Kein Salz: der Token traegt 24 zufaellige Bytes, ein Woerterbuch-Angriff ist gegenstandslos — und ein Salz machte den Gleichheits-Nachschlag unmoeglich, den dieser Weg braucht.
portal-auth.ts ist NICHT angefasst: es ruft nur mint + set + find und bekommt den Roh-Token weiterhin fuer die Mail-Adresse. Die Schichtung stimmt damit von selbst.
8 neue Proben mit AY-Attrappe, die den WEG pruefen (welcher Wert im Filter stand, in welcher Reihenfolge gefragt wurde) — nicht nur das Ergebnis. Zwei Flip-Proben gefahren: Schreiber zurueck auf Klartext => 1 rot, Lookup-Reihenfolge umgedreht => 2 rot, zurueck => 15/15. Volle Reihe 1574 gruen. Der fest verdrahtete Digest in der Probe ist derselbe wie in crm-api — laufen die Zwillinge auseinander, wird eine Einladung unaufloesbar, OHNE dass ein Fehler entsteht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixuser-menu
Suchfeld im Mandanten-Wechsel bekommt den Fokus (CEO-Bildbeleg 4)
Der Dialog wurde mit autoFocus:false geoeffnet (brand-switcher.component.ts) -- deshalb blieb das Suchfeld am Kiosk leer, bis man hineintippte. Gemessen lag die Ursache an der OEFFNUNGSSTELLE, nicht wie vermutet in der Dialog-Vorlage.
- brand-switcher: autoFocus 'first-tabbable' (beachtet [cdkFocusInitial]) - customer-switcher-dialog: Suchfeld traegt [cdkFocusInitial] - Der Fortschritts-Dialog behaelt autoFocus:false (Spinner unter disableClose) - Neue Testreihe (4 Proben) inkl. Umkehrprobe + Positiv-Kontrolle; flip-geprueft
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixcredentials
Alt-Bestands-Messung las die falsche Antwort-Form — und schwieg dabei
Der Vorab-Riegel von vault-change-password griff NICHT. Gefunden hat es erst die Live-Probe: der Wechsel ging trotz eines vorhandenen Alt-Zugangs raus und wurde vom SERVER mit 409 gestoppt — die Zusage "stoppt, bevor gesendet wird" war damit gebrochen.
Ursache, am laufenden Dienst gemessen: die Antwort ist { payload: { data: [...] }, meta } — `payload` ist ein OBJEKT, kein Feld. Der Code las `res.payload ?? res.data` als Liste, bekam ein Objekt, und `Array.isArray(...) ? ... : []` machte daraus STILL eine leere Liste. Eine leere Liste heisst hier "keine Alt-Bestaende", also: senden.
Zwei Fehler, nicht einer — und der zweite ist der teurere: (a) falscher Pfad in die Antwort; (b) eine unerwartete Form wurde als "nichts vorhanden" gelesen statt als "nicht feststellbar". Genau die Fehlerklasse, gegen die dieser Riegel steht.
Behoben: der gemessene Pfad zuerst, danach zwei ausdrueckliche Rueckfaelle — und FAIL-CLOSED. Ist das Ergebnis keine Liste, bricht der Befehl ab, statt weiterzumachen. Ebenso, wenn die Liste die Obergrenze (500) erreicht und damit abgeschnitten sein koennte; `meta` traegt keine Gesamtzahl, mit der sich das anders pruefen liesse.
Belegt: dieselbe Live-Probe stoppt jetzt VOR dem Senden, nennt den Zugang namentlich (69fe8996e1772fc374ef1c86 "Rustdesk Public Key") und endet mit Exit 2 statt mit dem Server-409. Drei neue Proben halten die gemessene Form, die unerwartete Form und den Obergrenzen-Fall fest; die bestehenden Proben sind auf die ECHTE Form umgestellt — sie hatten meine falsche Annahme gespiegelt und deshalb gruen gemeldet.
Dazu: die Phrasen-Abfrage nimmt die Phrase nicht-interaktiv ueber die STANDARDEINGABE (nie ueber ein Flag — das stuende in der Prozessliste), je eine Zeile pro Abfrage. Der Leser wird je Befehlsaufruf erzeugt statt im Modul gehalten; als Modul-Zustand teilten sich zwei Aufrufe im selben Prozess einen bereits geleerten Vorrat.
23 Proben in dieser Datei; volle Reihe 1545 gruen / 3 uebersprungen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Gemessen 2026-08-22: unter domains/coding gibt es NULL Aufrufer eines Wissens-Retrievals. Coding-/Workspace-Agenten hatten Code-RAG, aber keinen Weg zu einer kuratierten Bibliothek — ein Auftrag "baue nach den Prinzipien der Ueberzeugungspsychologie" traf ein Modell ohne Quellen.
Das Werkzeug ruft ausschliesslich den fertigen, LLM-freien Endpunkt POST /rag/retrieve auf. KEIN zweiter Retriever: Bibliotheks-Aufloeser und Mandanten-Sichtbarkeit bleiben in ai-api.
Die Bibliothek ist Pflicht und ist die Mandanten-Grenze, kein Filter. Ohne sie verweigert das Werkzeug den Aufruf, statt eine sinnlose Anfrage zu schicken — sonst lernt der Agent, dass "ohne Bibliothek" eine gueltige Form waere. Eine fremde Bibliothek antwortet 404 und wird als 404 durchgereicht; ein 403 wuerde verraten, dass sie existiert.
Die Urheberrechts-Auflage steht in der Werkzeug-Beschreibung, woertlich aus dem Systemprompt der 21 bereits ausgestatteten Agenten: anwenden statt zitieren, harte Grenze 15 Woerter Originalwortlaut, Werk/Autor/Seite als Beleg erwuenscht.
E2E gegen Produktion belegt: librarySlug=verkaufspsychologie liefert 200 mit Cialdini-Treffern (Influence, S. 224/157/187/223, Abschnitt SOCIAL PROOF), eine erfundene Bibliothek liefert 404 und null Treffer.
Der E2E-Lauf hat dabei einen Fehler in der eigenen Beschreibung gefunden: das Feld heisst `page`, nicht `pageNumber`, und `bibliographic` liegt unter `source`. Ein Agent haette nach `pageNumber` gesucht und ohne Seite zitiert. Beschreibung korrigiert und per Probe festgenagelt.
Proben 181/181; 7 neue mit zwei Umkehrkontrollen. Vier Flip-Proben belegen, dass sie beissen: Endpunkt umgebogen, Bibliotheks-Zwang entfernt, Urheberrechts-Grenze gestrichen, falscher Feldname behauptet — je genau eine Probe faellt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newalerts
Schweregrad je Quelle — Rueckfall-Kette und Entity-Dropout sind Warnungen
CEO-Befund 2026-08-22: "Rueckfall-Kette disarmed [oxy/...]" und "Entity dropout [oxy/...]" loesten den akustischen Alarm samt Voll-Overlay aus, obwohl sie Warnungen sind. Gemessen: 309 Dokumente, alle severity:'critical'.
Die Ausnahme sitzt im Klassifizierer, nicht am Erzeuger: das Erzeuger-Paket ist eine Bibliothek mit 14 Crawler-Verbrauchern (15 Releases gegen 1), und Zeile 81 ist ohnehin der Ort, an dem der Schweregrad entschieden wird.
Die Eskalations-Leiter wird fuer eine herabgestufte Quelle NICHT durchgereicht, und das ist tragend statt Kosmetik: der geteilte Schreiber hebt eine stehende Meldung nach escalateAfterMs auf critical und stuft sie danach nie wieder herab (core upsertAlert.ts:278-287). Gemessen — crawler-fallback-readiness schickt eine 30-Minuten-Leiter, waehrend seine Meldungen bis zu 215 Minuten offen stehen und 32 von ihnen mehr als einmal ticken. Ohne diese Haelfte waere der Fix nach einer halben Stunde wieder aufgehoben.
Ausdruecklich KEINE globale Entschaerfung von levelToSeverity: andere Quellen nutzen level:'alert' zu Recht (Anbieter-Ausfall). Ein ausdrueckliches marker.severity behaelt Vorrang. Exakter Namensvergleich, kein Praefix.
Proben 157/157, davon 10 neue mit vier Umkehrkontrollen; drei Flip-Proben belegen, dass die Kontrollen beissen (global entschaerft -> 16+17 rot, Leiter durchgereicht -> 20 rot, Praefix statt exakt -> 23 rot).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixcoord
reuse_check.verdict auf gueltigen Wert (fix ist keiner)
Erlaubt sind laut @tolinax/ayoune-workitem-quality genau: reuse | extend | genuinely-new | verify. Der Validator hatte die drei Briefe deshalb als reuse-check-missing gefuehrt.
Newvault
OS-Schluesselbund-Adapter und Sitzungs-Zwischenspeicher
Zwei neue Helfer als Grundlage der Master-Phrasen-Befehle.
osKeychain.ts — store/lookup/clear ueber die Werkzeuge, die das System ohnehin mitbringt: DPAPI (Windows), security (macOS), secret-tool/libsecret (Linux). KEINE neue Abhaengigkeit; die Vorlage dazu liegt unter .claude/reports/credentials-store/schluesselbund-bibliothek-vorlage-2026-08-22.md (keytar: unbetreut, im Haus 0 lebende Nutzer, und nativ in einem heute rein javascriptigen Paket).
Kein Geheimnis reist ueber die Befehlszeile — argv steht in der Prozessliste. Alle drei Rueckseiten bekommen den Wert ueber stdin, das PowerShell-Skript reist als -EncodedCommand. Eine Probe nagelt das fest; Flip-Probe gefahren (Geheimnis nach argv verschoben => 1 rot, zurueck => 16/16).
vaultSession.ts — legt den abgeleiteten Schluessel ab, nicht die Phrase. Ausdruecklich NICHT in secureStorage: dessen Schluessel ist sha256(hostname+username), fuer jeden lokalen Leser nachrechenbar. Fuer ein Anmelde-Token mag das reichen, fuer einen Schluessel, der die e2e-Zugaenge oeffnet, waere es Theater.
Degradation ist erlaubt, Stille nicht: ohne Schluesselbund wird NICHT heimlich Klartext geschrieben, sondern geworfen; die Zustimmung ist ausdruecklich, AYOUNE_REQUIRE_ENCRYPTED_STORAGE=1 verbietet sie ganz (Semantik aus desktop-client machineAuth/persistence.ts gespiegelt).
Ein Zwischenspeicher unter veralteten Ableitungs-Parametern wird verworfen statt benutzt — nach einem Phrasen-Wechsel oeffnete er gar nichts mehr und saehe dabei aus wie ein beschaedigter Datensatz.
35 Proben (16 + 19), tsc --noEmit Exit 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newcss
Darstellung des awards-Blocks (W11a)
.ay-award-* — der Zeichner der cms-api gibt die Klassen beim Publizieren aus; ohne diese Regeln stuenden die Auszeichnungen als nackte Aufzaehlung da (dieselbe Kopplung wie bei .ay-partner-*).
Anders als beim Partner-Verzeichnis gibt es hier nur EINEN Erzeuger — eine eigene Strecke /auszeichnungen existiert nicht. Wer sie spaeter baut, schliesst an DIESE Klassen an und erfindet keinen zweiten Satz.
Das Siegel bekommt object-fit:contain wie das Partner-Logo (eine Marke darf nicht angeschnitten werden), steht aber zentriert statt linksbuendig — ein Siegel ist rund und wirkt links angeschlagen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newblocks
awards-Block fuer Auszeichnungen und Zertifikate (W11a)
Neuer Blocktyp 'awards' aus der marketing-Entitaet Awards (Fundament-Kaskade 62): Block-Config _awards[] / _awardsAuto, Filter _awardCategory, _awardsLimit, layout. Aufgeloest in resolveEntityBlocks nach block.__awardItems (Muster partners).
AUFLOESER-Weg, und die Einordnung ist hier NICHT so glatt wie bei partners: IAward traegt mit validUntil sehr wohl ein Zeitfenster, das ohne Zutun eines Menschen ablaeuft. Massgeblich bleibt der Aufloeser-Weg, weil die MENGE sich nur durch published aendert und ein Ablauf ein einmaliges, im Voraus datiertes Ereignis ist — kein taeglich wanderndes Fenster wie bei events/locations. Die Kehrseite wird an zwei Stellen entschaerft: der Resolver laesst zur Publizier-Zeit Abgelaufenes heraus, und der Zeichner schreibt die Gueltigkeit SICHTBAR an die Kachel. Damit wird aus einer stillen Falschaussage ein sichtbar veraltetes Datum.
Zwei Mengen-Bedingungen in EINEM Filter stehen unter $and, nicht zweimal $or — ein zweites $or auf derselben Ebene ueberschriebe das erste STILL und der Website-Schnitt waere ersatzlos weg.
KEIN interner Verweis aus dem slug: die Strecke /auszeichnung/<slug> existiert nicht (anders als /partner). Der Verweis der Kachel ist certificateUrl, gehaertet.
formatAwardDate bewusst ohne toLocaleDateString — dessen Ausgabe haengt am ICU-Bestand des Laufzeit-Abbilds; deutsch Punktform, sonst eindeutige ISO-Form.
aMN.Awards in die Ladeliste (18. Fremd-Modell) — ohne den Eintrag ist ay.models.Awards undefined und der Block faellt STILL auf seinen Inline-Inhalt zurueck.
BEIDE Blocktyp-Waechter nachgezogen (rg ueber src/ und __tests__/ VOR dem Push gefahren): der Mengen-Vergleich in pageblocks.test.ts bekommt seinen Charakterisierungs-Fall, die harte Zahl in renderBlockListBlocks.test.ts geht 40 -> 41. Dritter Waechter EXPLICIT_PUSHES in app.bootstrap.test.ts ebenfalls (17 -> 18) — er hat den fehlenden Eintrag im vollen Lauf gefunden.
Verify: volle Einheiten-Menge 99 Suiten / 2358 Proben gruen, davon 34 neue in awardsBlock.test.ts. Doku-Zahl 38 -> 41 (war schon vor diesem Block veraltet: locations und quotes fehlten in der Liste).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newawards
Pflegeflaeche fuer Auszeichnungen und Zertifikate (W11a)
Erste Oberflaeche der Entitaet Awards (Fundament-Kaskade 62) — bis hierher konnte niemand eine Auszeichnung anlegen, der Website-Block waere dauerhaft leer geblieben (Praezedenzfall Portfolios). Vorlage ist der Partner-Zustand 23d, gleiche Kurations-Familie.
- marketing.awards Liste mit Kennzahlen, Filtern, Schnellfiltern, Schnellanlage - marketing.awards.edit Formular in sechs Abschnitten
Zwei Schalter, die nicht dasselbe meinen: published ist die Freigabe (fail-closed), validUntil die Gueltigkeit — sie laeuft OHNE Zutun eines Menschen ab. Deshalb ein eigener Schnellfilter Abgelaufen und eine eigene Kennzahl.
Der Zeitpunkt darin ist '{{now}}', NICHT new Date() — letzteres fröre den Zeitpunkt des Pod-Starts in den gespeicherten Zustand ein (dieselbe Regel wie an 23g).
Verify: seederFieldsExist/germanUmlautHygiene/selectionFieldGuard gruen (18 Proben); Flip-Probe mit erfundenem Feld meldet 'Awards.diesesFeldGibtEsNicht' namentlich rot, der Waechter deckt den neuen Zustand also wirklich ab.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newcodingjobs
den Koordinations-Spiegel stilllegen (410)
CEO-Entscheid 2026-08-21 (coding-pm-abschluss-zuteilung-und-spiegel, E6): CodingJobs wird eingestellt, die Zuteilung wandert an ITask. Schritt 4.3 des Auftrags — der Erzeuger faellt ZUERST, sonst legt der naechste Nacht-Lauf an, was der Aufraeum-Lauf gerade archiviert.
POST /codingjobs/actions/sync-coord-briefs sagt jetzt vor jeder Auswertung mit 410 ab (nicht 404/500: der zweite Erzeuger ayauto coord publish-brief ist fail-open und laeuft damit sauber weiter). Der Betreiber-Melder npm run coord:report steigt mit einer Klartext-Meldung und Ende 0 aus.
Gemessen 2026-08-22 gegen Prod: 332 Dokumente, davon 322 reine Spiegel-Zeilen, 10 Kern (7 eigene Sonden, 2 Testlaeufer-Daten, 1 leerer Entwurf). lease.generation>0 unveraendert bei 5, alle vom 08-10. Es ging nie ein echter Auftrag durch diese Sammlung.
Laeufer und Abtast-Mechanik bleiben vollstaendig erhalten — die Stilllegung ist eine Zeile zum Zuruecknehmen; die strukturelle Entfernung ist ein eigener Fundament-Auftrag nach einem Beobachtungsfenster. 4 Proben inkl. Umkehrprobe.
Fixtasks
die vier Zuteilungs-Rechte ausdruecklich registrieren
Ohne additionalRights sieht registerModuleRights sie nie -> sie stehen in keiner Rolle und keinem Paket -> checkRights weist JEDEN Aufruf der vier Lease-Wege mit 403 ab. Die Wege waeren tot geboren gewesen.
Die Praemisse des Auftrags ist widerlegt und nachgemessen: CodingJobs.entityActions fuehrt claim/heartbeat/complete/cancel/approve/ reject — das dort zitierte 'hat keine Registry-Zeile' trifft nicht zu. Die zweite genannte Praezedenz (dispatchCodeAgent) traegt nicht, weil sie gar kein neues Recht erfindet.
additionalRights ist genau dafuer da und braucht keinen Fundament-Griff. Drei Proben pinnen es fest, inkl. Textprobe auf app.ts.
Newmailfunnel
vier Achsen auf eine konsolidiert, Aufschluesselung statt drei leerer Dashboards
CEO-Entscheid 2026-08-22 (Kombination B+A). Die drei Achsen brand-mails / campaign-mails / newsletter-types entfallen; ihre Unterscheidung wandert als vier Aufschluesselungs-Tabellen IN den einen Trichter mailfunnel-mails.
Warum das ohne Verlust geht: KPIMails fuehrt brand/campaign/newsletter/ newsletterType bereits im Schluessel UND im Rumpf (am Schema geprueft). Die drei abgeloesten Sammlungen fuehrten zusammen 22.487 Zeilen, von denen genau EINE je einen Wert trug — ihre Nullen waren wahr, es gab seit 2024-09-12 nichts zu zaehlen.
Drei Befunde aus dem Bau: 1. Die alte mails-Tabelle gruppierte nach `name`, und mailStats schreibt das Feld NIE (7/7 missing gemessen). Ausgerechnet die ueberlebende Achse waere eine einzige Sammelzeile gewesen. Alle vier Tabellen loesen ihre Bezugs-Kennung jetzt per $lookup auf _id auf — die Bauform, die der Aggregations-Dienst ausdruecklich zulaesst (Primaerschluessel-Join, pipelineValidator.ts:100-106). 2. Ein additiver Seeder entfernt nichts: eine gestrichene Achse verschwindet aus dem Code, nicht aus dem Katalog. Beide Seeder haben deshalb eine Aufraeum-Stufe (deleteMany, doppelt eingegrenzt auf seedSource UND Slug-Praefix). Der Schnitt ist ein PRAEFIX-Vergleich, kein Teilstring — sonst loeschte er mailfunnel-mails-table-newsletter-type gleich mit. 3. Die Geometrie trug nur EINE Tabelle: alle vier haetten dieselbe y bekommen und sich ueberlappt. Jede Tabelle hat jetzt ihre eigene Zeile.
Riegel ergaenzt: Aufraeum-Schnitt mit Negativ-Kontrolle, _id-Join je Tabelle, vier verschiedene Dimensionen, vier verschiedene y-Werte. deleteMany im Mock (ohne ihn haette der never-throw-Seeder die Stufe still verschluckt). 37 Proben gruen; volle Reihe 492/493 (der eine ist ein pushdeliveries-Timeout ohne Bezug zu dieser Aenderung).
Newmails
E-Mail-Trichter auf kpimails konsolidiert, Dimension gerettet und vorgezogen
CEO-Entscheid 2026-08-22 (Kombination der Wege B und A des Briefes mail-kennzahlen-dimension-marke-kampagne-entscheiden): die drei KPI-Schreiber brandEmailsStats / campaignEmailsStats / newsletterStats entfallen — ihre Dimensions-Rueckschreibung an den MailLog NICHT.
Gemessen gegen Prod am 2026-08-22: - kpibrandmails 3.772 Zeilen / kpicampaignmails 15.636 / kpinewslettertypes 3.079 = 22.487, davon traegt GENAU EINE einen Wert (Newsletter-Typ "Messe", 2024-09-12, sent 355 / delivered 280 / opened 127). - maillogs: brand und campaign bei allen 21.550 Dokumenten missing, newsletterType bei 355 gesetzt.
Zwei Befunde, die den Zuschnitt bestimmt haben: 1. Die drei Schreiber sind im ganzen Baum der EINZIGE Schreiber von brand/campaign/newsletterType am MailLog. Ersatzlos entfernt haetten sie die Achse genau in dem Moment verloren, in dem die Konsolidierung sie retten soll. Der Kern lebt als mailLogDimensions weiter. 2. Die REIHENFOLGE war falsch: stats.mails rief mailStats ZUERST und die drei Dimensions-Schreiber danach. mailStats liest die Dimension per $first — es las sie also, bevor sie gesetzt war, und weil der naechste Lauf einen anderen Zeitausschnitt traegt, erreichte sie kpimails nie. Unsichtbar, solange keine Rundmails laufen; wirksam ab der naechsten. Jetzt laeuft mailLogDimensions vorneweg.
Riegel: test/mailLogDimensions.test.mjs — Schema-Pfade (strict verwirft sonst lautlos, mailCounts-Klasse), KPIMails traegt alle vier Dimensionen, Reihenfolge gegen die kommentar-befreite Quelle, Umkehr- und Positiv-Kontrollen. Flip-Probe gefahren: umgedrehte Reihenfolge macht genau Probe 6 rot. 104/104.
⚠ Der Bestand heilt nicht rueckwirkend — nur Zeitraeume nach der Umstellung tragen die Dimension.
Fixmemory-curator-daily
Phase-B-Gate auf die Harness-Kappung ziehen (30000 -> 24985 B)
Das Gate lag ueber der Kappung: autopilot-safe.md §3.1A Pfad 1 rief das Skript genau in dem Band auf, in dem es nichts tat. Einheit bleibt bewusst BYTES, der Wert wird die Zeichen-Kappung — fuer UTF-8 gilt Buffer.byteLength(s) >= s.length, also folgt aus der Aufruf-Bedingung (Zeichen > 24985) zwingend Bytes > 24985; das Gate kann damit nie mehr ein No-Op sein, wenn sein Aufrufer feuert.
Herkunft auf decisions.log 2026-08-18 umgestellt (loest den 2026-07-01-Entscheid ab, der die 30.000 setzte). Byte-Kommentar berichtigt: der System-Reminder misst ZEICHEN, nicht Bytes (gemessen 25.436 Z = 24,84 KB gegen die gemeldeten 24.8KB) — die alte Gleichsetzung war der Mechanismus der Drift.
Flip-Probe auf identischer Fixture (26756 B): alt no-op, neu -2285 B unter Limit. Umkehr-Kontrolle (20000 B) weiterhin no-op. SIZE_FLOOR byte-identisch. Margin 512 bleibt: Ziel 24473 B erreicht mit 31 von 56 Kandidaten in Reserve, kein Thrash.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Newtasks
Zuteilung im Pull-Modell — claim/lease-heartbeat/lease-complete/lease-cancel
Portiert aus domains/coding/api/.../codingjobs (CEO-Entscheid 2026-08-21, coding-pm-abschluss-zuteilung-und-spiegel: die Lease wandert an ITask, CodingJobs wird eingestellt). Vier Wege plus drei Bibliotheken.
Die Abhol-Bereitschaft ist entschieden: ein unabhaengiges Merkmal aus BESTEHENDEN Feldern (runtimePolicy als Designation + offener PmStatus + keine lebende Lease), kein neuer Status-Wert und kein Workflow-Uebergang. Ohne die Designation waeren 3.547 offene Aufgaben holbar (Prod gemessen).
Drei Stellen, an denen eine 1:1-Portierung STILL falsch gewesen waere: - priority ist ein WORT, keine Zahl: sort{priority:-1} liefert normal vor critical. Ersetzt durch Stufen-Durchgaenge; Flip-Probe belegt die Umkehr. - ITask fuehrt weder availableAt noch attemptCount noch result: strict haette diese Schreibvorgaenge verworfen. Ersetzt bzw. entfallen. - complete existiert bereits im PM-Sinn -> lease-complete.
18 Proben gegen echten mongod, davon 20-fache Nebenlaeufigkeit.
Fixautopilot-safe
Memory-Schwelle auf die Harness-Kappung ziehen (30000 -> 24985)
Die Curation loeste bei 30.000 Zeichen aus, die Harness kappt MEMORY.md aber schon bei 24.985 und verwirft alles darueber STILL. Dazwischen lag ein Fenster von rund 5.000 Zeichen, in dem der Index abgeschnitten geladen wurde, waehrend jede Iteration gruen meldete. Gemessen war es besetzt: MEMORY.md = 25.297.
Vollzug des CEO-Entscheids decisions.log:5019 (2026-08-18), der in memory-curator.md schon umgesetzt war und hier nicht mitgezogen wurde.
Die Brief-Tabelle nannte VIER Stellen, real waren es SIEBEN Vorkommen auf sechs Zeilen plus die 30KB-Anzeige (zusaetzlich Zeile 338 und ein zweites Vorkommen in Zeile 585). Aufgefallen ist es nur, weil das Verify-Gate grep -c '30000' => 0 verlangt statt 'die vier Zeilen'. Jede Fundstelle wurde vor dem Ersetzen im Kontext geprueft; der historische Messwert 30.962 blieb unberuehrt.
Dazu je ein Absatz an 1b und 3.1A, der die Zahl ERKLAERT (Schmerzgrenze, nicht Ziel) — sonst wandert sie beim naechsten Umbau zurueck.
Nebenbefund als Folge-Brief: scripts/memory-curator-daily.mjs traegt dieselbe 30000 als ausfuehrbares Gate (Phase B no-op bei 26.271 Bytes) und rechnet dabei in BYTES statt Zeichen — ausserhalb der claim_paths und an einen aelteren CEO-Entscheid gebunden, daher eigener Brief statt stiller Mitnahme.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixinfra
redeploy-entity-consumers fuehrt stillgelegte Repositorien nicht mehr als Rueckstand
Der Melder stufte allein nach locked gegen published ein und fragte nie, ob ein Repositorium ueberhaupt ausbringbar ist — dieselbe Fehlerklasse, die CLAUDE.md fuer die Pipelines-Zeile festhaelt (zaehlt Dateien, nie laufende Dienste).
Gemessen fuehrte er platform/internal als BEHIND: nie ausgebracht, nie eine Kette gehabt, seit 2026-08-22 stillgelegt. Zwei belegte Schaeden — es entstand echte Pflege-Arbeit daran (zwei Fundament-Pin-Commits am 2026-08-21 an einem Repositorium ohne fachlichen Commit seit 2024-02-19), und --apply haette eine Kette ausgeloest, die es gar nicht gibt.
Ein Repositorium mit RETIRED*.md im Wurzelverzeichnis faellt jetzt VOR der Einstufung heraus — damit zugleich aus BEHIND, aus BEDINGT und aus der Modul-Zaehlung. Es wird als eigene Zeile AUSGEWIESEN, nie still verschluckt (ein unsichtbarer Ausschluss waere dieselbe Fehlerklasse in der anderen Richtung); die JSON-Form traegt es ebenfalls.
--apply ist damit MITgehaertet und braucht keinen zweiten Waechter: die Ausloese-Menge faellt ueber vier filter-Stufen aus derselben Liste, wer aus ihr faellt, kann sie nicht erreichen. Eine Probe nagelt genau das fest.
Verify: Trockenlauf 24 -> 23 Kandidaten, platform/internal nur noch in der Stillgelegt-Zeile, Negativ-Kontrolle domains/audit/api weiterhin drin. Neue Probenreihe 7 Proben je Richtung; alle redeploy-Reihen 49/49. Die 8 Fehlschlaege der vollen Reihe sind VORBESTEHEND — mit engem Rueckbau nur dieser Datei nachgemessen: identische acht Titel auch ohne die Aenderung.
Newcascade-68
Schema-Zwillinge - Zuteilungs-Achse an Tasks, Reiter+Seitenfilter an der lebenden Dashboard-Ebene, 1:n-Kante geschaerft
- Tasks: fuenf Sub-Schemas (lease/providerPolicy/runtimePolicy/ capabilityRequirements/budget), tokenHash mit select:false, Abhol-Index {_customerID, status, lease.expiresAt} - Spanne bewusst am ENDE, abweichend von der Feld-Reihenfolge der CodingJobs-Vorlage. lease.generation OHNE Vorgabewert (die Vorlage hat default:0): ein nicht-leeres Unter-Dokument entkaeme minimize und wanderte in jede Bestands-Aufgabe. - aYOUneStatesViewConfigSchema: pageFilter/tabs an der Dashboard-Ebene, filterValues/tab/ignorePageFilter an der Karte - dieselben geteilten Unter-Schemata wie aYOUneDashboards und CustomerDashboards, kein Nachbau. - Projects/Repositories: Kommentar-Zwillinge mit am 2026-08-22 selbst gemessenen Zahlen (0/46 bzw. 499/499 auf zwei Projekten, groesstes 466).
Flip-Probe beidseitig: gegen interfaces 2026.449.0 sechs Fehler, gegen 2026.450.0 null. Cascade68.roundtrip.test.ts 17 Proben, volle Reihe 680/680.
Newoverlays
Ebenen-Modell des Bild-Erzeugers wiederbeleben — type/Geometrie/Wasserzeichen + titelbild-taugliche Ausgabe
Der Vertrag `IOverlay` deklariert seit jeher `type: text|caption|image` samt x/y/width/height/contentPosition/fontWeight — der Renderer las davon NICHTS und rendphrase jede Ebene als zentrierte Bildunterschrift ueber die volle Leinwandbreite.
Gemessen gegen die Produktion (2026-08-22): die zwei lebenden Vorlagen tragen x:20,y:700, contentPosition:"west", fontWeight:"bold" — alles verworfen. Eine Ebene mit type:"image" waere als woertliches `caption:undefined` gerendert worden; deshalb hat sie nie jemand angelegt (0 von 16 Vorlagen).
- jobs/overlayPlan.ts reiner Planer: Text- UND Bild-Ebenen (Marke, Wasserzeichen) mit Schwerkraft, Versatz, Groesse, Deckkraft - jobs/outputProfile.ts reines Ausgabe-Profil: Inhaltstyp folgt der ECHTEN Endung (bisher fest "image/png", auch bei .jpg) und legt fuer ein Titelbild flach — Apple weist Alpha-Kanaele ab, Spotify verlangt sRGB - generateImage.ts/.js verdrahtet beides; Bild-Ebene ohne Quelle wird uebersprungen statt als caption:undefined gerendert
Rueckwaerts-vertraeglich: eine Ebene mit den Schema-Vorgabewerten erzeugt die BYTE-GLEICHE Kommandozeile wie bisher — gegen die echte gm-Bibliothek geprueft, mit Negativ-Kontrolle. PNG-Ausgaben bleiben unveraendert, solange kein Titelbild angefordert wird (coverSafe reist im Auftrag, kein Vertrags-Feld noetig).
BEWUSST NICHT wiederbelebt: backgroundColor (Vorgabewert #FFFFFF ist deckend, "nicht gesetzt" und "ausdruecklich weiss" sind nicht unterscheidbar). NOCH NICHT im Vertrag: opacity — Mechanismus gebaut, heute No-Op.
23 neue Proben (62 gesamt), drei Umkehrproben beissen nachweislich.
Newcascade-68
Zuteilungs-Achse an ITask, Reiter+Seitenfilter an der Dashboard-Ebene, 1:n-Kante Projekt/Repositorium benannt
Kaskade 68 buendelt drei rein additive Fundament-Glieder: - ITask += lease/providerPolicy/runtimePolicy/capabilityRequirements/budget samt fuenf neu deklarierten Sub-Vertraegen (Feld-Form 1:1 aus ICodingJob*, bewusst NICHT re-importiert - der Quell-Vertrag wird stillgelegt). - AyounePageViewConfigDashboards += pageFilter/tabs, ...DashboardCards += filterValues/tab/ignorePageFilter - die lebende Ebene kannte die Felder der Vorlagen-Ebene nicht, strict verwarf sie beim Speichern still. - Vertrags-Kommentare an IProject._defaultRepository (Rueckfall, nicht 'das Repositorium des Projekts') und IRepository._project (kanonische 1:n-Kante), mit am 2026-08-22 selbst gemessenen Zahlen.
modelsAndRights unveraendert bei 843 (null Entitaeten, kein Codegen-Fan-out).
Fixcoord
Praemisse von alarm-umschlag-traegt-keinen-ursprungs-zeitpunkt korrigiert
An der Flaeche widerlegt: das ausgebrachte Bedienpanel (App-Laufzeit 360 s) zeigt 'seit 13.08. 18:30 · vor 8d'. Ein acht Tage alter Ursprung kann nicht aus dem Arbeitsspeicher stammen — der START-Weg (app.component.ts:1388-1392) liefert die Vorkommens-Daten sehr wohl.
Die Suche, die zur falschen Praemisse fuehrte, war ein FALSCHES NEGATIV: eine Namens-Suche kann einen generisch gebauten Umschlag nicht sehen. Die Positiv-Kontrolle fing es nicht — sie belegte nur, dass die Suche ueberhaupt trifft, nicht dass der Erzeuger seine Felder woertlich hinschreibt.
Offen ist damit nur noch, ob der LIVE-Weg (Socket) sie ebenfalls traegt.
Fixcrawl-monitor
stehende Alarme ueber den ZUSTAND aufloesen, nicht nur ueber den Uebergang
Am 2026-08-22 standen fuenf von sechs Alarmen dieses Melders 4,4 bis 4,6 Stunden und wurden ausschliesslich von Hand geschlossen — jeder 2 bis 67 Sekunden nach seiner eigenen Quittierung. Der Melder lief durchgehend und meldete fuer de_de schon um 00:07:54Z wieder `queueStatus: ok` bei 236.603 wartenden Auftraegen.
Ursache der Klasse: `decideSymptom` loest ausschliesslich ueber den UEBERGANG bad→gut auf. Geht dieser eine Uebergang verloren, steht der Alarm fuer immer, und zwar lautlos — der Zustandsautomat nimmt danach den stillen Zweig `gut + prevStatus !== "bad"` → `none`.
Neu: `decideStandingAlertSweep` loest ueber den ZUSTAND auf. Solange fuer eine Kachel ein Alarm als offen vermerkt ist und die Lage HEUTE gut und beurteilbar ist, wird aufgeloest — unabhaengig davon, ob der Uebergang je erkannt wurde. Der Marker faellt dabei, der Weg feuert also genau einmal je Episode.
- eigenes Feld `SymptomState.openAlert`, BEWUSST keine Umdeutung von `lastAlertAtMs` (das traegt die Melde-Daempfung und wird von checkCrawlSuccessRate mitbenutzt) — `decideSymptom` bleibt unveraendert - kein Ruhe-Fenster-Aufloesen: `judgeable` ist Bedingung - nachgeholte Aufloesungen loggen als `warn`, nicht `info` — sie heissen, dass der regulaere Weg versagt hat - Waechter test/crawlStandingAlertSweep.test.ts (G1 Wiedergabe des echten Falls, G2 Flip-Probe, G3 Positiv-Kontrolle de_ch inkl. keine Doppel-Aufloesung)
Newayauto
migrate features-namen-tranche2-nachziehen
Die Namens-Tranche 2 korrigiert 27 Zustands-Namen in der PLATTFORM. Der erste Abgleich-Durchgang (2026-08-21) hatte die WEBSITE-Namen genau dieser Schluessel mit den damals SCHLECHTEN Plattform-Namen ueberschrieben (Tools, Prompts, SLAs, Middlewares). Beide Seiten laufen dadurch wieder auseinander -- diesmal mit der Plattform als der besseren Seite.
🔴 OHNE diesen Nachzug SENKT die Tranche die Deckung, statt sie zu heben: gemessen unmittelbar nach der Ausbringung 453/554 deckungsgleich gegen 471/554 davor. Nach dem Lauf: 477/554.
🔴 Das AUSBRINGUNGS-TOR ist der Kern: geschrieben wird nur, wo der Plattform-Zustand den neuen Namen BEREITS fuehrt. Ein Dienst mit roter oder laufender Kette faellt heraus statt eine Behauptung zu erzeugen, die die Plattform nicht deckt -- gemessen fielen die drei cms-Schluessel genau so heraus (Kette vorbestehend rot). Ein erneuter Lauf holt sie nach.
🔴 BEWUSST kein blindes "Website uebernimmt die Plattform" -- genau das war der Fehler des ersten Durchgangs, der 96 kuratierte Namen zerstoert hat. Hier steht eine ausdrueckliche Liste von 27 Schluesseln, deren Zielnamen aus dem deutschen `summary` des jeweiligen Zustands abgeleitet sind.
Waechter: Ausbringungs-Tor (Plattform-Seite), Alt-Wert-Waechter (Website-Seite, kein Ueberschreiben + Idempotenz), Positiv-Kontrolle auf alle 27 Schluessel, Umkehr-Kontrolle mit erfundenem Schluessel, Rueckroll-Abbild VOR dem Schreiben.
Gefahren 2026-08-22: DRY 24/27 -> APPLY geschrieben=24, Rest auf Alt-Wert=3 (die drei wartenden), an der Datenbank nachgemessen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0181TrRUCtUiQJtm5yWRLYKR
Newreporting
Abschlussquote-Familie N2 — zwei Dashboard-Vorlagen, zehn Kacheln
Die zweite Bauart des Reporting-Umzugs nach dem E-Mail-Trichter (M1): `assignment-appointments` und `assignment-events`, die Stufe 4 als falsch einsortiert nachgewiesen hat (Matrix-Befund B4). Beide standen im Ledger auf `covered-by-template` OHNE Beleg.
Je Achse fuenf Vorlagen: zwei Zaehler, die Abschlussquote, ein Ring nach dem Verknuepfungs-Zustand und eine Uebersicht mit zwei Spalten.
Sechs gemessene Befunde bestimmen die Bauform:
- Der Ring geht NICHT nach `type`. In der Alt-Antwort ist `chart[].type` ein Beschriftungs-Text, kein Feld; `calendarentries.typ` ist bei 13.477 von 13.492 `standard` — das waere ein Ring mit einem Segment. - Das Zeitfenster ist ZWEISEITIG (sieben Tage bis morgen, wie die Alt-Steuerung). Ohne Obergrenze faellt es auf 1.639 statt 22 Termine, Faktor 74. M1 durfte die Obergrenze weglassen, weil Kennzahl-Zeilen nicht in der Zukunft liegen — Termine schon. - Die Verknuepfung braucht die Unter-Pipeline-Form MIT `_customerID`, also das Gegenteil der M1-Regel: der Pruefer weist ein `$lookup` ueber ein Nicht-`_id`-Feld sonst als moeglichen mandanten-uebergreifenden Lesezugriff ab. Auf der obersten Ebene bleibt der Vergleich weiterhin verboten. - Der Mandanten-Vergleich ist typ-tolerant (`$toString` beidseitig) — `assignments._customerID` ist gemischt typisiert (96 Objekte, 1 Zeichenkette). - Der Alt-Feldname `attendees_tookPart.attendee` ist gebrochen (das Schema kennt nur `_attendee`) und hier korrigiert, nicht portiert. - Die Alt-Beschriftungen sind irrefuehrend: `stats.total` zaehlt die Basis-Menge, nicht das, was das Etikett sagt.
Beide Verknuepfungs-Achsen sind im Bestand leer (`assignments.calendarentry` bei 0 von 97, `meetings.attendees_tookPart` bei 0 von 32) — beide Quoten stehen auf 0 %. Dass die Rechnung traegt, ist mit drei Positiv-Kontrollen belegt: dieselbe Quoten-Pipeline mit gelockerter Verknuepfung rechnet 57,5 %, die Veranstaltungs-Verknuepfung ohne den Teilnehmer-Vergleich findet 21 von 97, und `$in` ueber einen gepunkteten Listen-Pfad trifft 89 Dokumente.
35 neue Proben, reine Reihe 225/225 ueber 12 Suiten. Sieben Flip-Proben, sechs beissen; die siebte (`RING_SPALTEN` 5 -> 8) beisst zu Recht NICHT, weil sie keine Verletzung erzeugt. Eine Flip-Probe hat eine eigene Probe als zu schwach entlarvt (`let: {cid: "$_customerID"}` enthaelt die Zeichenkette schon) — die nachgeschaerfte Probe haelt jetzt den Vergleich selbst fest.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixbuild
test/ aus dem Haupt-Bau ausschliessen (2x TS6059)
Der Bau war rot, seit `tsc || exit 0` entfernt wurde. Ohne `include` greift das Vorgabe-Muster `**/*` und zieht test/jobEventBuffer.test.ts + test/jobRegistry.test.ts in den Haupt-Bau — beide liegen ausserhalb von rootDir './src' (TS6059).
Die Testreihe hat ihr EIGENES tsconfig.test.json (rootDir '.', outDir './.test-out') und ueberschreibt `exclude` vollstaendig, laeuft also unveraendert weiter. dist und .test-out sind Bau-Ergebnisse und faenden sich sonst beim naechsten Lauf selbst als Quelle wieder.
Kein @ts-ignore, kein || exit 0, kein noEmitOnError-Aequivalent. Verify: npx tsc --noEmit 0 Fehler - npm run build Exit 0 - npm test 60/60 gruen - dist-Struktur unveraendert (dist/server.js liegt weiter obenauf, Einstiegspunkt intakt).
Fixtypes
Images-Felder aus dem Documents-Datensatz entfernen
Der Bau war rot, seit `tsc || exit 0` entfernt wurde — 10 Uebersetzungsfehler, alle TS2339 in jobs/generateVideo.ts an zwei gleichlautenden Stellen.
Der Code baut einen `Documents`-Datensatz, setzt darauf aber fuenf Felder, die zu `IImage` gehoeren: _productID, _productGroupID, _productDivisionID, _publicationID, _filename. Gemessen: 0 Treffer in Vertrag UND Schema von Documents, je 1 Treffer in IImage; Positiv-Kontrolle (originalname/mimetype/linktype/isCloudFile) 1/1, Umkehr- probe mit erfundenem Feld 0. Der Block stammt aus einem Bild-Upload-Pfad.
Mongoose `strict` hat alle fuenf beim Speichern die ganze Zeit STILL verworfen — der Riegel verdeckte also nicht nur einen roten Bau, sondern eine tote Schreiboperation. Es geht nichts verloren: die vier Verweise wurden ohnehin nur auf null gesetzt, und _filename trug denselben Wert wie originalname, das echte Feld des Vertrags, das eine Zeile weiter unten gesetzt wird.
Kein @ts-ignore, keine Fundament-Kaskade (nichts fehlt im Vertrag - das Feld gehoert an eine andere Entitaet). Verify: npx tsc --noEmit 0 Fehler - npm run build Exit 0 (20 .js emittiert).
Fixtypes
echte puppeteer-Typen statt Cleanup-Vertrag + ClosableProcess.kill akzeptiert number
Der Bau war rot, seit `tsc || exit 0` entfernt wurde — 22 Uebersetzungsfehler, alle in runBrowserAutomation.ts. Zwei getrennte Defekte:
1. Die Arbeits-Handles waren als ClosableBrowser/ClosablePage deklariert. Das sind die NARROW Cleanup-Vertraege (nur close()/process()), damit closeBrowserSafely ohne echtes Chrome testbar bleibt — kein Ersatz fuer die volle puppeteer-API. Jeder Schritt-Aufruf (newPage, goto, $$eval, screenshot, ...) war dadurch ein TS2339. Jetzt Browser/Page aus puppeteer; die erfuellen den Cleanup-Vertrag strukturell an den closeBrowserSafely-Aufrufstellen.
2. ClosableProcess.kill(signal: string) war in KEINER Richtung zu einem echten ChildProcess.kill (signal?: NodeJS.Signals|number) zuweisbar — number ist kein string. Ein echter Browser erfuellte ClosableBrowser also gar nicht (TS2322), entgegen dem eigenen Kommentar der Datei. Jetzt string|number: echter ChildProcess UND die Testattrappen (kill: (sig: string)) passen, ohne Node- oder puppeteer-Typen in den bewusst abhaengigkeitsfreien Helfer zu ziehen.
Kein @ts-ignore, kein || exit 0, kein noEmitOnError-Aequivalent. Verify: npx tsc --noEmit 0 Fehler - npm run build Exit 0 (9 .js emittiert) - npm test 49/49 gruen.
Newayauto
migrate ayounestates-waisen-namen — Namen der sechs Waisen
Fuer sechs Bildschirm-Zustaende greift der Code-Weg STRUKTURELL nicht: `ayounestates` wird bei jedem Pod-Start von `registerModuleStates` neu gesaet, diese sechs haben aber keinen lebenden Saeer unter ihrem Titel. Sie brauchen eine direkte Daten-Korrektur; Zielnamen aus der W7-Kuration (features-namen-snapshot-2026-08-22.tsv), nicht neu erfunden.
🔴 ZWEI PRAEMISSEN DES AUFTRAGS SIND GEGEN PRODUKTION WIDERLEGT:
(1) "alle sechs tragen _appName: null" -- falsch fuer zwei. monitoring.computingentities und -logs tragen ayoune-computing-entities-develop. Der Lauf meldet die Saeer-Kante je Zeile, statt sie zu verschweigen: ein Pod-Start KANN sie ueberschreiben.
(2) "bei den Zwillingen pruefen, ob Korrektur oder Stilllegung richtig ist" -- die Messung entscheidet fuer KORREKTUR, gegen die naive Erwartung. Der Feature-Katalog der Website kennt ausschliesslich die monitoring.*-Schluessel; fuer computing-entities.* gibt es dort KEINE Zeile. Eine Stilllegung liesse also zwei Website-Features OHNE Plattform-Gegenstueck zurueck und machte die Lage schlechter. Dasselbe bei sale.orders (die Website fuehrt sale.orders UND ecommerce.orders). Die zugrundeliegende Dublette bleibt und ist eine PRODUKT-Entscheidung (welcher Schluessel ist kanonisch?) -- sie wird benannt, nicht still geloest.
Waechter: Alt-Wert im Filter (kein Ueberschreiben einer fremden Korrektur + Idempotenz), Positiv-Kontrolle auf alle sechs Titel, Umkehr-Kontrolle mit einem erfundenen Titel, Rueckroll-Abbild VOR dem Schreiben. `name_en` bleibt unangetastet.
Gefahren 2026-08-22: DRY 6/6 -> APPLY geschrieben=6, Rest im Filter=0, an der Datenbank unabhaengig nachgemessen; Re-DRY 0 von 6 bei weiterhin 6/6 Positiv-Kontrolle (keine falsche Null).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0181TrRUCtUiQJtm5yWRLYKR
Newhr
Fundament-Pin auf 448.1/452.1 + voller Codegen-Modul-Lauf
Pin-Bump auf den publizierten Stand (interfaces 2026.447.1->2026.448.1, models 2026.451.0->2026.452.1, core 2026.315.1 unveraendert) per fix-foundation-pinning --pin-exact --de-latest, danach npm ci — ohne das liest der Erzeuger die ALTE installierte Fassung und ueberspringt neue Entitaeten still als [skew] (Kaskade-50-Falle). Lock-Beleg vor dem Commit: genau DREI Aufloesungen der Fundament-Pakete, keine verschachtelte Zweitkopie.
Kaskade-48-Pruefpunkt gefahren: use('/:id/actions') 5 -> 7. NICHTS verloren; neu verdrahtet sind employees und payslips, deren Aktionen deklariert, aber nie an den Router gehaengt waren.
Der uebrige Diff ist die Suchfelder-Vorlage (74b04f2150): die Freitextsuche liest jetzt searchableFields statt hartkodiert name/title/subject. Fuer die zwei neuen Entitaeten ist das keine Kosmetik, sondern Bedingung — ?q= haette sonst NICHT in firstName/lastName/email/headline gesucht. Testreihe 785/785 gruen (42 Suiten); der in marketing gemessene Rot-Effekt derselben Vorlage tritt in hr nicht auf.
MESSUNG statt Annahme zu den neu erzeugten Aktions-Ruempfen: von den fuenf Employees-Aktionen sind VIER durch handgeschriebene custom-Router gedeckt (generate-/stylize-portrait, grant-/revoke-portal-access) und damit tot, weil custom vor default mountet; Payslips print ebenso. UNGEDECKT ist allein import-lexware-dls — sein Rumpf ist fail-closed (ALLOWED_FIELDS leer, schreibt nichts), aber er antwortet 200 und feuert das Ereignis, ist also ein STILLER Nicht-Vollzug, bis der echte Importer gebaut ist (eigener Brief lexware-dls-importer-hr-upload-diff-upsert).
Nur die 19 inhaltlich geaenderten Dateien sind aufgenommen; der Erzeuger schreibt LF und liess 153 weitere Dateien als reine Zeilenende-Umschreibung im Status stehen — die sind verworfen, nicht committet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011XC3R3yf9u7xnZneEKnU8w
Fixroutes
getMany-Suchfelder nachgezogen — beide Lese-Routen suchen wieder gleich
Der Vorlagen-Fix 74b04f2150 (2026-08-21) hat `getMany.ts.tpl` an ihre Schwester `list.ts.tpl` angeglichen: statt der fest verdrahteten Felder name/title/subject liest sie jetzt die am Modell deklarierten `searchableFields`. Der Commit fasste GENAU EINE Datei an — kein Modul wurde danach neu erzeugt. Dieses Modul ist der erste nachgezogene Konsument.
GEMESSEN statt geschaetzt (eigene Messung 2026-08-22, gegen interfaces 2026.448.1): ueber die 18 Module mit erzeugten Lese-Routen wuerden sich 109 getMany- und 70 list-Handler bewegen; 17 Entitaeten sind hand-gehaertet und bleiben unberuehrt. marketing allein stellt 109 dieser Bewegungen — der Modul-Lauf hat 110 Dateien geaendert.
⚠ DIE ERSTE MESSUNG WAR EINE FALSCHE NULL, und sie ist hier festgehalten, weil sie die naechste Messung genauso treffen wuerde: hand-gehaertete Handler bauen ihr `$or` aus `SEARCH_FIELDS.map(...)` statt als Literal-Block. Ein Muster, das nur den Literal-Block liest, meldet fuer sie eine LEERE Feldliste — also weder einen Treffer noch einen Fehler. marketing sah dadurch nach 2 betroffenen Entitaeten aus statt 69.
ZWEI BEKANNTE NEBENWIRKUNGEN DES MODUL-LAUFS: beide gemessen, keine eingetreten. Aktions-Verdrahtung `use('/:id/actions'` 1 -> 1 Datei. Testdateien unter routes/default/__tests__ 1 -> 1, keine Loeschung im Diff. EXPECTED_MOUNT_COUNT bleibt 72 (der Waechter ist gruen, es kam keine Route dazu).
PROBEN AUF DAS NEUE VERHALTEN GEHOBEN, nicht nachgezogen: 78 Proben in 41 Reihen nagelten den ALTEN Zustand fest. Sie behaupten jetzt die aus `modelsAndRights` ABGELEITETE Feldliste (`src/lib/testing/searchableFields.ts`), nicht eine von Hand abgeschriebene — eine Probe, die die Implementierung spiegelt, belegt nichts. Die Ableitung wirft bei einem unbekannten Plural, damit ein Tippfehler nicht still auf den Rueckfall laeuft.
NEUER MODUL-WAECHTER `searchableFieldsParity.guard.test.ts` (7 Behauptungen, statisch, ohne AY-Start): jeder erzeugte getMany-Handler gegen die Deklaration, jeder list-Handler gegen seinen getMany-Zwilling, UMKEHRPROBE an LoyaltyPoints (einzige marketing-Entitaet ohne Deklaration -> exakt der Rueckfall), GEGENPROBE dass keine deklarierte Entitaet auf dem Rueckfall steht, plus die namentliche Liste der 15 hand-gehaerteten Entitaeten (eine stille Haertung faellt damit auf).
FLIP-PROBE GEFAHREN: ein Handler kuenstlich auf den Rueckfall zurueckgesetzt -> GENAU DIE DREI tragenden Behauptungen fallen, die vier Kontrollen bleiben gruen; nach dem Zuruecksetzen wieder 7/7. Der Waechter beisst also wirklich.
⚠ Der Haertungs-Marker taugt NICHT als Erkennungsmerkmal: die Vorlage zitiert die Zeichenkette `HAND-HÄRTUNG` selbst in einem Anleitungs-Kommentar, jede erzeugte Datei enthaelt sie. Erkannt wird an der zeilen-verankerten `const SEARCH_FIELDS`, die in keiner Vorlage vorkommt (29 Dateien / 15 Entitaeten).
WIRKUNGS-BELEG, nicht Bau-Beleg: Awards deklariert name/slug/issuer/description — getAwards UND listAwards rendern genau diese vier. LoyaltyPoints ohne Deklaration rendert weiterhin name/title/subject.
Bau gruen, volle Reihe 120 Suiten / 6316 Proben gruen (vorher 119 / 6309).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EK5PonxfgqrAuKZCvkjyHs
Fixpages
testimonial-Auto-Zug sortiert `createdAt` statt des schema-losen `created`
`IRating` fuehrt `created`, das `RatingSchema` deklariert es NICHT — `strict` verwirft es beim Schreiben still, es lebt nur in Alt-Dokumenten. Am Bestand gemessen 2026-08-22: `created` in 6 von 46 Dokumenten, `createdAt` in 46/46 (Positiv-Kontrolle im selben Lauf). Fuer 40 Dokumente sortierte MongoDB gegen `null` — der Zug lieferte also NICHT die neuesten `_ratingsLimit`, sondern eine beliebige Auswahl, ohne je fehlzuschlagen. Die vier Alt-Anzeigewege sortieren `createdAt` (`legacy-core-api/services/micro/content/dynamic.ts:315`).
Heute ohne sichtbare Wirkung (plattformweit passieren nur 4 Bewertungen den Filter, die Vorgabe-Grenze ist 12) — der Zweig wird erst mit wachsendem Bestand falsch, und dann lautlos. Genau deshalb war er nicht aufgefallen: der bestehende Fall (c) prueft Filter UND Grenze, die Sortierung nie. Die neue Probe schliesst die Luecke, mit Negativ-Kontrolle auf das Phantom-Feld (rot vor dem Fix belegt, gruen danach).
Dazu die Abgrenzung der ACHT bewertungs-artigen Achsen im Blocktyp-Kanon — welche Entitaet auf eine Website gehoert und welche nicht (`StoreViews` ist gar keine Bewertung; `Settings.rating` ist die achte Achse und steht per Bauart NICHT in `modelsAndRights`). Voller Beleg: `.claude/reports/bewertungs-achsen/abgrenzung-2026-08-22.md`.
`KNOWN_BLOCK_TYPES` unveraendert — kein neuer Blocktyp, die zwei Waechter bleiben gueltig.
Verify: `tsc --noEmit` Exit 0; volle Reihe 2318/2319 (der eine Fehlschlag `__tests__/contents.test.ts:282` ist VORBESTEHEND — mit gestashten Aenderungen identisch rot).
Der Zweig zog `Ratings` mit `_customerID` + `rating != ""` und sonst nichts — als einziger oeffentliche Massen-Zug der Plattform ohne Freigabe-Filter. Der `references`-Zweig 40 Zeilen tiefer in DERSELBEN Datei filtert `published: true`; vier Alt-Anzeigewege filtern `web: true` (`legacy-core-api/services/micro/content/dynamic.ts:314/:346/:390`, `replacer.ai.ts:584`).
Gemessen 2026-08-22 an den drei lebenden `testimonials`-Bausteinen: 35 Texte im Zug, davon 4 web+freigegeben und 1 mit ausdruecklich abgewaehltem `web`. `rejected` ist 0 — es wurde also keine ABGELEHNTE Bewertung gezeigt; gebrochen war das Versprechen des Schalters, nicht die Moderation.
Bewusst NICHT die strengere Dreier-Bedingung des CMS-Blocktyps `testimonial` (`approved+web+!rejected`): sie kuerzte diese drei Bausteine von ~10 auf <=4 Eintraege — eine sichtbare Inhaltsaenderung und damit eine Entscheidung, keine Fehlerbehebung. Vorgelegt als E1 in `.claude/reports/bewertungs-achsen/abgrenzung-2026-08-22.md`.
Verify: `tsc --noEmit` Exit 0 (das Repositorium fuehrt kein Testwerk).
Newdoc-engine
auch den Takt-Schluessel an den Fundament-Katalog binden
`DOC_TYPE` im Sachbezugs-Takt war eine weitere frei getippte Kopie. Ein Tippfehler dort haette gar keinen Fehler erzeugt, sondern einen stillen Leerlauf: der Takt schickt den Render-Auftrag, der Renderer findet den Fall nicht und haelt sich fuer unzustaendig.
Flip-Probe: `benefit-in-kind-reciept` faellt mit TS2820.
Verify: tsc gruen, npm test 285 Tests / 0 fail.
Newdoc-engine
Bescheinigungs-docType gegen den Fundament-Katalog typisieren
`ICertificateCase.docType` stand auf `string` — ein Tippfehler in einem Katalog-Eintrag ging still durch den Bau und schlug erst zur Laufzeit auf: `NACH_DOCTYPE` fand den Fall nicht und der Renderer hielt sich fuer unzustaendig, ohne dass irgendwo ein Fehler entstand.
Gemessen vor der Aenderung: mit `employer-sick-certificat` (ein Buchstabe fehlt) blieb der Bau gruen (Exit 0), waehrend die Positiv-Kontrolle (ein zusaetzliches Pflichtfeld am Interface) korrekt mit TS2741 an BEIDEN Katalog-Eintraegen fiel — die Kopplung fehlte also wirklich.
Jetzt traegt das Feld `EmployeeCertificateDocType` aus @tolinax/ayoune-interfaces, dieselbe Quelle, gegen die `hr-api` seine Auswahl-Selektoren prueft. Beide Tippfehler fallen mit TS2820, samt Vorschlag des richtigen Wertes.
Die zwei Nachschlage-Funktionen behalten `string` als Eingang: der Wert kommt aus einem Render-Auftrag, also vom Netz, und kann per Sache nicht zur Bauzeit geprueft werden. `NACH_DOCTYPE` bleibt aus demselben Grund auf `string` verschluesselt — eine engere Karte zwaenge jeden Aufrufer zu einer `as`-Zusicherung, und eine Zusicherung ist genau die Aufweichung, gegen die dieser Umbau steht. Der Bau-Schutz sitzt am Katalog-Eintrag, nicht an der Karte; die Datei traegt danach null neue Zusicherungen.
KEIN Pin-Bump noetig: der Worker steht bereits auf interfaces 2026.448.1 und damit exakt auf dem publizierten Stand (dist-tags.latest), der Lock traegt denselben Wert (kein halber Pin). Der Katalog ist installiert und fuehrt beide Werte; die Umkehrprobe mit einem erfundenen Wert faellt.
Verify: tsc gruen, zwei Flip-Proben (je TS2820), Gegenprobe Exit 0, npm test 281 Tests / 0 fail.
Newhr
Bescheinigungs-Selektoren gegen den Fundament-Katalog pruefen
`ABSENCE_CERTIFICATE_SELECTORS` stand auf `Record<string, ...>` — ein Tippfehler im `docType` ging still durch und schlug erst zur Laufzeit auf. Gemessen: mit `employer-sick-certificat` (ein Buchstabe fehlt) blieb der Bau gruen (Exit 0), waehrend die Positiv-Kontrolle (`maxDurationDays: "drei"`) korrekt mit TS2322 fiel — die Kopplung fehlte also wirklich, die gruene Null war echt.
Jetzt prueft ein `satisfies Partial<Record<EmployeeCertificateDocType, ...>>` das Katalog-Literal gegen `EMPLOYEE_CERTIFICATE_DOC_TYPES` aus @tolinax/ayoune-interfaces. Derselbe Tippfehler faellt mit TS2353.
`Partial` und nicht das volle `Record` ist Absicht: der Fundament-Katalog fuehrt seit Kaskade 64 auch `benefit-in-kind-receipt`, und die haengt am Mitarbeiter und einem Monat (`entityType: "Employees"`, `periodic: true`), nicht an einer Abwesenheit — ein Abwesenheits-Selektor dafuer existiert per Sache nicht. Ein volles Record verlangte einen Schluessel, der hier nie hingehoert. Gegenprobe gefahren: der zweite Katalog-Wert waere zulaessig (Exit 0), er ist nur nicht erzwungen.
`AbsenceCertificateDocType` ist aus dem Selektor-Bestand ABGELEITET (`keyof typeof`), damit keine dritte Wahrheit entsteht. `istBekannterBescheinigungsFall` ist jetzt ein Type-Guard und bleibt ausdruecklich am lokalen Bestand statt auf `isEmployeeCertificateDocType` zurueckzufallen: der Fundament-Waechter beantwortet "kennt der Katalog den Typ?", diese Funktion "kann DIESER Massenlauf ihn auswaehlen?" — fuer `benefit-in-kind-receipt` lautet die erste Antwort ja und die zweite nein.
`IAuswahlArgs.docType` bleibt `string`: der Wert kommt aus einem Anfrage-Rumpf und kann per Sache nicht zur Bauzeit geprueft werden; der Wurf bleibt der Laufzeit-Schutz, den die Testreihe festnagelt.
awaiting auf dependency ziehen (sed traf den Kommentar-Suffix nicht)
Die Zeile lautete 'awaiting: none # CEO 2026-08-21 …' — mein Muster verlangte das Zeilenende direkt nach 'none' und griff deshalb nicht. Ein blockierter Brief mit 'awaiting: none' waere weiterhin claimbar gewesen (none steht in AWAITING_CLAIMABLE).
Newpersonal
Lohnzettel-Sicht meldet sich als heikel — kuerzere Sperrfrist
Am geteilten Werkstatt-Geraet darf ein Schichtplan 20 Minuten offen stehen, ein Lohnzettel nicht. Bisher gab es genau EINE geraeteweite Frist; die Flaeche behalf sich mit „Fertig" (sofort sperren) und Vergessen beim Verlassen — beide erfassen den Fall NICHT, um den es geht: weglaufen, ohne etwas anzufassen.
Die Sicht MELDET sich jetzt als heikel (@tolinax/ayoune-lock 2026.19.0, enterSensitiveView/leaveSensitiveView); die Bibliothek waehlt daraus die KUERZERE der Mandanten-Fristen. Es bleibt bei der EINEN Sperre — nur der Wert ihres Zeitgebers aendert sich. Kein zweiter Zeitgeber.
Drei Einhaengepunkte, weil die Sicht ein ZUSTAND der Komponente ist und keine eigene Route: - wechsle() meldet an bzw. ab, je nachdem wohin gewechselt wird - ngOnDestroy meldet IMMER ab — ein Vergessen liesse die kurze Frist geraeteweit stehen (leaveSensitiveView ist bei fremder Kennung wirkungslos) - der Kopfkommentar fuehrt nicht mehr den alten Befund, sondern die Loesung
Das „Fertig" BLEIBT. Es ist die ausdrueckliche Handlung, nicht der Ersatz.
⚠ Pin bewusst gehoben (2026.18.0 -> 2026.19.0) mit Lock-Beleg VOR dem Bau. ⚠ Pfad ausserhalb des claim_paths dieses Auftrags (platform/ayoune-lock/src) — clients/terminal war zum Zeitpunkt des Edits von KEINER Sitzung gehalten (geprueft); der Auftrag verlangt die Flaechen-Umstellung ausdruecklich als Punkt 3, sein claim_path war zu eng geschnitten.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PmMj9oQCU9nxusjcbD8Dp8
Newlock
kuerzere Sperrfrist fuer als heikel gemeldete Sichten
Am geteilten Werkstatt-Geraet sind nicht alle Sichten gleich heikel: ein Schichtplan darf 20 Minuten offen stehen, ein Lohnzettel nicht. Bisher gab es genau EINE geraeteweite Untaetigkeits-Frist.
🔴 DIE ENTSCHEIDUNG, DIE DER AUFTRAG ZU MESSEN VERLANGTE — die Sicht MELDET sich als heikel, statt eine Dauer zu uebersteuern. Der Grund ist kein Geschmack: eine Flaeche, die eine ZAHL setzen darf, kann die Frist auch VERLAENGERN. Das waere ein Sicherheits-Rueckschritt, den niemand bemerkt — kein Fehler, keine Meldung, nur eine Sicht, die laenger offen steht als der Mandant erlaubt hat. Man muesste ihn mit `Math.min` abfangen — und waere damit faktisch bei dieser Loesung, nur mit einer API, die etwas anderes verspricht als sie tut. Die Flaeche sagt WAS sie ist, die Bibliothek entscheidet WIE LANGE; die Frist-Hoheit bleibt beim Mandanten.
KEINE zweite Sperre: es bleibt bei dem EINEN Zeitgeber, nur der Wert aendert sich, mit dem er gestellt wird.
🔴 Der laufende Zeitgeber wird beim An- und Abmelden SOFORT neu gestellt. Ohne das griffe die kuerzere Frist erst nach der naechsten Nutzer-Aktivitaet — also genau dann NICHT, wenn jemand die heikle Sicht oeffnet und weglaeuft, ohne etwas anzufassen. Das ist der Fall, fuer den es die Faehigkeit gibt.
🔴 Ein SET statt eines Schalters: bei einem Sichten-WECHSEL meldet sich die zweite Sicht an, BEVOR die erste sich abmeldet. Ein boolean wuerde beim `leave` der ersten zuruecksetzen, und die zweite stuende still auf der langen Frist. Als Probe festgenagelt.
Rein additiv: ohne `enterSensitiveView()` verhaelt sich alles byte-identisch wie bisher (`sensitiveViews.size === 0` -> regulaere Frist). Die neue Kontext-Methode ist optional.
Proben 62/62 (17 neu), Bau gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PmMj9oQCU9nxusjcbD8Dp8
Fixdeps
device-shell 2026.9.0 -> 2026.10.1 (nicht 10.0)
Letzte drei von sieben Verbrauchern, die exakt auf 2026.9.0 festhingen und deshalb nicht von selbst heilen.
🔴 ABWEICHUNG VOM BRIEF, und sie ist der Kern: er nennt 2026.10.0 — publiziert ist 2026.10.1, und der Unterschied ist tragend. Changelog: 10.0 feat(capabilities): Faehigkeits-Erkennung in die Kiosk-Huelle, plus Mikrofon 10.1 fix(capabilities): enumerateDevices bekommt eine Frist — die Erkennung wurde sonst NIE fertig Auf 10.0 zu pinnen hiesse, das neue Feature mitsamt seinem bekannten Haenger auf drei Kiosk-Geraeteklassen auszurollen.
PIN-FORM: exakt beibehalten. Der Brief laesst beides zu; exakt folgt der Grundregel (Richtung A, 2026-07-23) fuer VERBRAUCHER-Repositorien — reproduzierbare Baeue mit committetem Lock. Die Uebergangs-Regel auf Spannen gilt Bibliotheken, die FUNDAMENT pinnen, nicht Clients, die eine Bibliothek pinnen. `clients/pos` mit Caret ist der Ausreisser, nicht das Vorbild.
Lock-Beleg: je genau EINE Aufloesung auf 2026.10.1 in allen drei. Wirkung gemessen: `shared-lib-drift` client-facing stale (exact pins) 4 -> 1 = Grundlinie (der verbleibende mobile-app|communicator-Eintrag). Ratsche NICHT bewegt.
Newpm
Abschnitt "Website" an den Modul- und Feature-Bildschirmen
Die Kaskade `modul-feature-vertrag-website-felder-kanon` hat den Freigabe-Schalter geliefert — es gab aber keinen Ort, an dem ihn jemand umlegt. Diese Datei ist feld-fuer-feld hand-kuriert; ein neues Vertragsfeld erscheint hier NICHT von selbst. Der Schmerzpunkt der Quell-Kaskade ("ein Redakteur kann einen Eintrag nicht zurueckhalten, ohne seinen Text zu loeschen") war damit nicht behoben, sondern eine Schicht nach oben gewandert.
Der eigentliche Auftrag sind die HINWEISE, nicht die Felder: ein leeres Kaestchen heisst hier NICHT "aus", sondern "keine Aussage — es gilt das Ersatz-Tor". Jeder Hinweis nennt deshalb die Bedeutung bei LEER, und jeder ist am echten Tor belegt (isPubliclyVisible, consumer-app), nicht am Vertragstext: - showOnWebsite false=versteckt / true=sichtbar (gewinnt in BEIDE Richtungen) - published:false haelt zurueck AUCH bei showOnWebsite:true - leer MIT Beschreibung sichtbar, leer OHNE versteckt - _websites leer = auf ALLEN Auftritten, nicht "auf keinem" - slug leer = Rueckfall auf key (kein 404)
Der echte Bestandsfall traegt `slug:""` und `showOnWebsite:null` — also GESETZTE leere Werte, nicht "absent". Am Tor geprueft: verhaelt sich identisch zu absent, die Flaeche darf sie also schreiben.
Bewusst `multiSelect` statt des im Auftrag genannten `remoteSelect`+multiple: in dieser Datei ist `multiSelect` die belegte Bauform (1x, `_components`), `remoteSelect` kommt 0x mit `multiple` vor.
`sortOrder` nur beim Modul — das Feature fuehrt es bereits unter "Stammdaten"; ein zweites Feld auf denselben Pfad waeren zwei Bedienorte fuer einen Wert.
Vorab gemessen: das installierte models kennt alle fuenf Felder (2026.452.1) — der lokale node_modules hing auf 451.0, wo KEINES existiert; der Lock war korrekt, `npm ci` hat es geradegezogen. Ohne diese Pruefung haette `strict` jede Eingabe still verworfen.
Proben 650/650 (38 Reihen), inkl. der feldweisen Spec-Struktur-Probe.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PmMj9oQCU9nxusjcbD8Dp8
Newsenderbrands
Sektion "Gegenzeichnung" — Bedienflaeche fuer die zwei Signatur-Felder
`ISenderBrandLetterhead.signatureImage`/`.signatureName` stehen seit der Kaskade `arbeitgeber-gegenzeichnung-...` im Vertrag (interfaces 2026.446.0), hatten aber KEINE Bedienflaeche: `letterhead` war in `senderbrands.edit` vollstaendig unkuriert (gemessen: 0 Treffer fuer den Feldpfad). Ein Vertrag ohne Flaeche ist kein lebender Zweig.
Die Hinweistexte sind am Vertrag abgelesen, nicht erfunden (`ISenderBrand.ts:193-203`): die Marke ist der kanonische Traeger, NICHT der Mandant (CEO-Entscheid 2026-08-05 — ein Mandant fuehrt mehrere Marken, die Grafik gehoert zu der, unter der das Schreiben hinausgeht); `ayounecustomers.signatureImage` bleibt Rueckfall fuer Bestandsdaten, `signatureName` faellt auf `managingDirectors[0]` zurueck.
Vorab gemessen statt angenommen: das INSTALLIERTE models (2026.450.0) kennt beide Feldpfade — ohne das haette `strict` jede Eingabe still verworfen.
Scope-Grenze eingehalten: die uebrigen elf `letterhead`-Felder (Anschrift, Rechtsform, USt-IdNr., ...) sind bewusst NICHT mitkuriert. Am gebauten Zustand belegt — Negativ-Kontrolle: keines von ihnen im Zustand; Positiv-Kontrolle: beide neuen Feldpfade und "Countersignature" drin.
Proben 1130/1130 (59 Reihen).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PmMj9oQCU9nxusjcbD8Dp8
Fixinfra
redeploy-entity-consumers meldete eine stille Null aus dem falschen Verzeichnis
URSACHE, reproduziert statt vermutet: die Scan-Wurzel kam aus `process.cwd()`, und der Verzeichnis-Durchlauf verschluckt einen fehlenden Pfad still (`catch { return }`). Aus einem beliebigen anderen Verzeichnis gefahren lieferte der Befehl deshalb `affected: []` — bei FUNKTIONIERENDER Registry-Abfrage und ohne jeden Hinweis.
Das ist byte-genau die Form des Befundes vom 2026-08-21 (`affected: []` bei `published: 2026.444.0`) und beantwortet die Frage des Briefes, welche der beiden Stufen versagt: die KANDIDATEN-Erkennung, nicht der Versions-Vergleich. Waere es der Vergleich gewesen, haetten 24 Zeilen mit `UNKNOWN` dagestanden.
Drei Aenderungen: - `defaultScanRoot()` nutzt `getMonorepoRoot()` (leitet aus dem EIGENEN Ort ab) statt cwd — der kanonische Helfer, den drei andere Befehle bereits nutzen. - `scanEntityConsumers` WIRFT, wenn unter der Wurzel keiner der vier Ordner liegt. Eine leere Menge ist hier nie eine Entwarnung; ohne den Waechter ist eine falsche Wurzel von einem leeren Monorepo nicht zu unterscheiden. - Ehrliche Zaehlung in Zusammenfassung UND json: `Kandidaten: N · davon BEHIND: M · CURRENT · UNBEKANNT`. „0 von 0" und „0 von 24" sahen bisher gleich aus — genau dieser Unterschied war der ganze Befund. Plus ein ausdruecklicher Hinweis, wenn die veroeffentlichte Fassung nicht lesbar war (dann ist jeder Stand UNBEKANNT, nicht CURRENT).
Flip-Probe (test/redeploy-entity-consumers-scan-root.test.mjs, 3 Proben inkl. Negativ-Kontrolle): vorher liefert `scanEntityConsumers(<leeres Verzeichnis>)` still `[]`, nachher wirft es. Suite-Ebene: Grundlinie 9 Fehlschlaege, danach 8 — die 8 sind vorbestehend und fremd (Renderer, coord-admin, Formatter), meine Datei kippt von rot auf gruen.
Ende-zu-Ende aus /tmp gefahren: 24 Kandidaten, 24 BEHIND, `custom-functions-worker` gefuehrt.
Beide dokumentierten Sperrgruende sind aufgeloest, und beide waren echte Hindernisse: (1) die Personalakte traegt die Zuordnung (IEmployee.benefits[], Kaskade 64), bedienbar seit dem Reiter "Sachbezuege"; (2) der doc-engine-Laeufer kennt den Verteiler-Zweig dispatch-benefit-receipts.
🔴 `defaultData: { op: "dispatch-benefit-receipts" }` ist die tragende Zeile, nicht Beiwerk. Der Einplaner reicht woertlich `data: d.defaultData ?? {}` durch — ohne sie kaeme beim Laeufer ein LEERER Job an, der weder `op` noch `sourceEntityType` traegt und mit "Unsupported sourceEntityType" abgewiesen wuerde. Der Takt liefe dann jeden Monat und erzeugte NICHTS, ohne dass etwas fehlschlaegt.
Der vorgesehene Takt `0 6 1 * *` ist unveraendert uebernommen (06:00 UTC am 1. — frueh genug fuer die Lohnabrechnung des Vormonats); der Verteiler bescheinigt entsprechend den VORmonat.
⚠ Heute tragen 0 von 53 Akten eine Zuordnung — der Takt laeuft zunaechst ins Leere, und das ist die richtige Richtung: der Auftrag verbietet Erst-Zuordnungen ausdruecklich, die pflegt der CEO selbst ueber die neue Flaeche.
Proben 60/60.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PmMj9oQCU9nxusjcbD8Dp8
Newdispatch
Verteiler-Zweig `dispatch-benefit-receipts` (Glied C)
Faechert den Monats-Auftrag HR_BENEFIT_RECEIPTS_MONTHLY in die Kohorte auf und stellt je Treffer einen regulaeren render-employee-certificate-Job ein. Damit entstehen die Bestaetigungen mit den ECHTEN Werten der Personalakte statt mit Aufruf-Parametern — der Grund, warum der Takt bis zur Sachbezugs-Definition abgeschaltet angemeldet war.
🔴 DERSELBE idempotencyKey wie der Hand-Massenlauf (`Employees:<id>:benefit-in-kind-receipt:pdf:<period>`) — keine Kosmetik, sondern die Zusage, dass Takt und Handgriff sich nicht doppeln. Der Hand-Weg bleibt als Ausnahme-Werkzeug bestehen.
🔴 Ein VERTEILER, kein Renderer: der Auftrag traegt weder renderID noch sourceEntityType, steht deshalb oben bei `stamp-signature`. Unten waere er mit "Unsupported sourceEntityType" abgewiesen worden.
Die drei Regeln, an denen ein Fehler teuer waere, liegen in der puren Haelfte und sind einzeln geprobt: - Zweckbindung PFLICHT (§ 2 Abs. 1 Nr. 10 ZAG) — ohne sie ist der Gutschein eine Geldleistung und ein Beleg mit dem Freigrenzen-Text eine FALSCHE Steuer-Aussage. - Freigrenze (§ 8 Abs. 2 S. 11 EStG) UEBERSPRINGT, wirft nicht — ein Wurf liesse den Monats-Lauf fuer ALLE Mandanten sterben. Genau auf 50,00 € ist noch tragbar. - Ein FEHLENDES Datum ist offen, nicht ungueltig — sonst fiele jede Zuordnung ohne Enddatum aus dem Takt, und das waeren nach Bauart fast alle.
Mandanten-uebergreifend (Plattform-Takt), anders als der hr-api-Hand-Lauf. Mehrere tragbare Zuordnungen: die hoechste gewinnt, es wird NICHT summiert — die Bestaetigung beschreibt EINE Zuwendung.
⚠ `npm run build` ist hier `tsc || exit 0` und schluckt Fehler; separat mit `npx tsc --noEmit` geprueft (0 Fehler). Proben 281/281.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PmMj9oQCU9nxusjcbD8Dp8
Newemployees
Reiter "Sachbezuege" an hr.employees.edit (Glied B)
Die Bedienflaeche zur Zuordnungs-Achse `IEmployee.benefits[]` (Fundament aus Kaskade 64). Ohne sie traegt das Fundament, aber niemand kann eine Zuordnung pflegen — der Vertrag war bis hierher kein lebender Zweig.
Wiederhol-Editor (`typ: 'ObjectArray'`) nach dem Muster des `wage`-Reiters, mit `_wageType` als Referenz-Select ueber die atomare Komponente (CEO-Regel: jedes ObjectId-Feld ist ein Auswahlfeld, nie Freitext). `showIf: NUR_MENSCH` — ein KI-Mitarbeiter bekommt keinen Tankgutschein, und die Freigrenze des § 8 Abs. 2 S. 11 EStG setzt ein Arbeitsverhaeltnis voraus.
🔴 BEWUSSTE ABWEICHUNG VOM AUFTRAG: der Monatswert wird in CENT erfasst, nicht "als Euro-Eingabe". Gemessen 2026-08-22 gibt es in der SDUI-Schicht keine Cent/Euro-Umrechnung — `amountCents` kommt plattformweit nur im Lohn-Export vor, nie als Eingabefeld, und FIELD_TYPE_MAP kennt keinen Geld-Typ. Ein als "Euro" beschriftetes number-Feld schriebe den Wert UNVERAENDERT nach monthlyValueCents: wer 50 tippt, haette 0,50 € hinterlegt und der Beleg wiese 0,50 € aus, ohne dass irgendetwas fehlschlaegt. Ein Feld, das sagt was es speichert, ist richtiger als eine vorgetaeuschte Umrechnung; die Freigrenze steht als Rechenhilfe im Hilfetext.
Der Katalog-Waechter traegt eine harte Zahl (13 -> 14, Beschreibung mitgezogen); der Feld-Waechter (employeeEditFieldsExist) bestaetigt, dass alle benefits.*-Pfade im Schema existieren. Proben 78/78.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PmMj9oQCU9nxusjcbD8Dp8
Newshop
verwandte Produkte am Produkt + CSS fuer den `quotes`-Block (W9)
VERWANDTE PRODUKTE — die Auftrags-Auflage war eine Messung, nicht eine Vermutung ("woraus folgt die Verwandtschaft?"). Gemessen an `IProduct` UND am Schema: sie ist GEPFLEGT, nicht abgeleitet — fuenf Achsen als `[{product: ObjectId(ref 'Products'), name}]`. Vier davon sind Empfehlungen und werden angezeigt; `bundleProducts` bleibt DRAUSSEN, weil eine Stueckliste ("dieses Set enthaelt X") eine andere Frage beantwortet als "was koennte Sie noch interessieren". Der Bestand stuetzt das: die 91 Buendel verweisen ueberwiegend auf Varianten DESSELBEN Titels — als "verwandtes Produkt" gezeigt waere das schlicht falsch.
`isPublic: true` wirkt fail-closed (derselbe Schalter wie `getShopProduct`): ein nicht-oeffentliches Produkt erscheint nicht, auch wenn es verknuepft ist. Ohne den Filter waere die Empfehlungs-Achse ein Weg, Unveroeffentlichtes zu zeigen.
Diese Strecke liest LIVE — die Frage "Marker oder Publizier-Zeit" stellt sich hier gar nicht, anders als bei den gebackenen Seiten der cms-api. Die Karte kommt von `renderProductCard` (Wiederverwendung), nicht von einem zweiten Zeichner.
CSS FUER `quotes` — der cms-Renderer gibt `.ay-quote*` aus; ohne Regeln faellt der Block als linksbuendiger Stapel auf die Seite. Das ist dieselbe STILLE Klasse, die 2026-08-17 die Kundenlogos und Referenzen traf: fehlendes CSS wirft nichts und faellt in keinem Bau auf. Die sechs Selektoren sind im Layout-Wachposten festgenagelt, der genau dieses Auseinanderlaufen melden soll.
Proben: 10 neu (related-products.test.mjs), volle Menge 1538/1538 gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PmMj9oQCU9nxusjcbD8Dp8
Newpages
Blocktyp `quotes` — zitierte Aussagen Dritter aus `Quotes` (hub), W9
Dritter zitat-naher Block neben `testimonial` (Ratings, ohne Sprache) und `references` (Kundenstimmen des Mandanten). Drei Traeger, drei Quellen — keiner ersetzt einen anderen; die Abgrenzung steht im Kanon-Kommentar.
Gemessen (nicht angenommen): `Quotes` traegt KEINEN W0-Kanon — kein `published`, kein `slug`, kein `_websites`, kein `sortOrder`, am publizierten Vertrag UND am Mongoose-Schema geprueft. Daraus die zwei Vertraege dieses Zweiges:
(1) Die ausdrueckliche Auswahl (`_quotes[]`) filtert nicht — zeichengleich zum `_ratings[]`/`_faqs[]`-Weg. Wer ein Zitat in den Block legt, gibt es frei. (2) Der Auto-Zug verlangt ein Kriterium (`_quoteCategory`/`_quoteLang`) und fragt OHNE Kriterium die Datenbank gar nicht erst. Grund ist gemessen: bei EINEM Mandanten liegen 48.428 importierte Fremdzitate — ein kriterienloser Zug haette daraus zwoelf willkuerliche auf die Website gestellt, ohne dass irgendetwas fehlschlaegt.
Publizier-Zeit statt Marker (EPIC-Regel): ein Zitat aendert sich nur, wenn ein Mensch es aendert — kein Zeitfenster, keine wandernde Adresse.
Alle drei Wachposten bedient: KNOWN_BLOCK_TYPES 39 -> 40, die harte Zahl in renderBlockListBlocks.test.ts, UND der Charakterisierungs-Fall in BLOCK_CASES (den der Auftrag nicht nannte — er verlangt Markup-Belege, keine Zaehlung).
Proben: 13 neu (quotesBlock.test.ts), volle Einheiten-Menge 2312/2313. Die eine rote ist ein Bestandsbefund (contents.test.ts, searchableFields-Vorlage) und von dieser Arbeit unberuehrt — belegt und im bestehenden Brief nachgetragen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PmMj9oQCU9nxusjcbD8Dp8
Fixwallboard
die uebrigen Aufgaben-Kacheln filtern und gruppieren auf _project
Derselbe tote Feldname wie in taskBySPE, an acht weiteren Stellen derselben Datei - alle auf ay.models.Tasks. Nach dem pm-Praefix-Feldzug W1 (2026-08-20) tragen gemessen 30.579 von 42.392 Aufgaben _project und nur 106 den Altnamen; jede Projekt-Einengung der Wand wurde damit still verworfen.
Am schwersten traf es taskByProject: die Kachel GRUPPIERTE auf $project, wodurch 42.286 von 42.392 Aufgaben (99,75 %) in einem einzigen Eimer _id:null landeten und als 'Projekt geloescht' beschriftet wurden. Die Kachel war damit vollstaendig unbrauchbar.
NICHT angefasst - und das ist ein Messergebnis, keine Auslassung: worklogs hat die Wanderung NICHT mitgemacht (gemessen 3.709 von 3.987 Arbeitszeiten tragen project, 0 tragen _project). In dieser Datei wird jedoch keine Arbeitszeit nach Projekt gefiltert; die einzige WorkLogs-Abfrage laeuft ueber buildWorkLogsQuery. Ein WorkLogs-Feldzug ist ein eigener Vorgang.
Restkontrolle: 0 Vorkommen des Altnamens in der Datei, 10 Stellen auf _project, 331 Proben gruen.
Fixwallboard
taskBySPE wendet das Zeitfenster an und filtert auf die lebenden Feldnamen
Die Kachel nahm :from/:to entgegen und benutzte KEINE der beiden Grenzen - sie zeigte immer den gesamten Bestand und behauptete dabei, sie zeige den gewaehlten Zeitraum (gemessen ~124.000 Dokument-Abrufe je Aufruf, 3 x 41.441). Zwei weitere Filter waren aus demselben Grund wirkungslos und sind mitbehoben:
- project -> _project (pm-Praefix-Feldzug W1). Gemessen 2026-08-21: 30.579 von 42.392 Aufgaben tragen _project, nur 106 den Altnamen. Jede Projekt-Einengung wurde still verworfen. - service -> servicedesk. Das Feld 'service' existiert NICHT (0 von 42.392); der Balken 'Service' war strukturell immer 0, ohne Fehler, ohne Warnung.
Zeitachse createdAt, gemessen statt angenommen: alle fuenf Geschwister-Kacheln mit demselben :from/:to-Vertrag filtern darauf (neue BFF-Route wie Alt-Route), createdAt ist zu 100 % belegt (doneAt 26,5 %, due 13,2 %), und start/end sind zu 99,9 % sekundengenaue Kopien von createdAt. Herleitung im Kopf von lib/taskStatsQuery.ts.
Fehlende, unlesbare oder vertauschte Grenzen liefern jetzt 400 statt eines stillen Rueckfalls auf 'alles' - ebenso eine unlesbare Projekt-Kennung.
11 neue Proben, Flip-Probe beidseitig: alle drei wiederhergestellten Defekte werden von genau der zustaendigen Probe rot gemeldet, die uebrigen 328 bleiben gruen.
Fixbriefing
Bau-Skript verschluckt keine Uebersetzungsfehler mehr; drei ueberholte Begruendungen richtiggestellt
- `build`: `rimraf dist && tsc || exit 0` -> `rimraf dist && tsc`. Das `|| exit 0` machte JEDEN Bau gruen. Beidseitig belegt: altes Skript + absichtlicher TS-Fehler -> exit 0 (verschluckt, Fehler stand im Protokoll); neues Skript + derselbe Fehler -> exit 2. Fehler danach zurueckgenommen, Bau exit 0, Testreihe 326/326.
- `tenantBriefings.ts` + `lib/tenantBriefing.ts`: beide Kommentare behaupteten, `Settings.defaultmails.briefing` existiere im Schema nicht, und nannten "94 Briefings, 0 zugestellt". Beides ueberholt. Das Feld liegt seit Kaskade 59 im Schema (installiert 2026.450.0, mit Negativ-Kontrolle belegt). Der Umgehungs-Weg bleibt richtig, aber weil das Feld je Mandant LEER ist.
- `dailyBriefing/channels.ts`: `sendBriefingEmail` ist NICHT tot. Es hat genau einen lebenden Aufrufer monorepo-weit - diesen - und er erzeugt alle 72 gescheiterten Zustellungen (550 5.6.0 No Sending Domain found in From header). Ausserdem festgehalten: dieser Weg schreibt keine MailLogs-Zeile.
Kein Verhaltens-Change am Zustellweg - nur Bau-Riegel und Begruendungen.
Newsitemap
Kanal-Schalter und slug-Adresse der Funktions-Achse nachziehen
Spiegel der consumer-app-Aenderung — eine Sitemap darf NIE mehr zeigen als der Renderer. Beide Tore sind jetzt wieder Zeile fuer Zeile identisch.
published = LEBENSZYKLUS (false haelt zurueck) showOnWebsite = KANAL (false haelt zurueck, true gibt frei) D3 des Kanons, CEO-Entscheid 2026-08-21 "beide Felder".
ADRESSE: slug gewinnt, key traegt weiter. Eine Sitemap, die den key ausgibt, waehrend der Renderer das slug bedient, zeigt auf eine Weiterleitung statt auf die Seite.
Der Adressierbarkeits-Filter fragt ab jetzt nach dem SEGMENT (slug ODER key) statt nur nach key: ein Modul mit kuratiertem slug, aber ohne Code-Bezeichner — der Normalfall bei einem KUNDEN-Mandanten — waere sonst still aus der Sitemap gefallen, obwohl der Renderer es ausliefert.
Kein Fundament-Bump noetig, gemessen statt angenommen: das Repositorium deklariert nur core, sein Lock haelt models 2026.451.0 (ohne die neuen Felder). Der Leseweg ist davon unberuehrt — mongoose reicht unbekannte Schema-Pfade in die MongoDB-Projektion durch, und lean() hydriert nicht. Positiv-Kontrolle: ein bekanntes Feld ist ebenfalls in der Projektion.
Newfunktionen
Freigabe-Schalter und slug-Adresse der Modul-Achse verdrahten
Die oeffentliche Modul- und Funktions-Achse lieh sich ihr Tor bisher aus: gezeigt wurde, was eine kuratierte Beschreibung traegt. Das trug fail-closed, war aber kein Vertrag — ein Redakteur konnte einen Eintrag nicht zurueckhalten, ohne seinen Text zu loeschen.
Ab interfaces 2026.448.1 / models 2026.452.1 gibt es ZWEI echte Schalter (D3 des Kanons, CEO-Entscheid 2026-08-21 "beide Felder"): published = LEBENSZYKLUS der Beschreibung showOnWebsite = KANAL (auf DIESEM Auftritt zeigen) Beide sind hier verdrahtet. Ein Schalter, den kein Leser liest, ist ein Feld ohne Wirkung — genau die Fehlerklasse, gegen die der Auftrag steht.
ADRESSE (Kanon R3): slug gewinnt, key traegt weiter. getPublicModule loest ab jetzt ueber BEIDE auf — sonst waere jede bereits verlinkte key-Adresse ab dem ersten kuratierten slug eine 404. Kanonische Adresse, hreflang und Brotkrumen ziehen mit, sonst zeigte canonical auf eine andere Seite als die besuchte.
DAS ERSATZ-TOR BLEIBT und das ist Absicht: 38 Module und 554 Funktionen tragen die neuen Felder nicht, Backfill ist ausgeschlossen — ein strenges Tor schaltete die Achse in derselben Sekunde dunkel. Es zu entfernen, sobald der Katalog befuellt ist, ist ein eigener Vorgang.
Probe test/module-feature-website-gate.test.mjs (11 Faelle, je mit Gegenprobe). Flip-Probe gefahren: Kanal-Veto ausgehebelt => 1 rot, wiederhergestellt => 11 gruen; sie bestaetigt sich also nicht selbst. Volle Reihe 1522/1522 (ohne die Aenderung 1511/1511 — die Differenz sind genau die neuen Proben).
Newledger
siebter Status covered-by-template + Nenner-Trennung
CEO-Entscheid 2026-08-21 (Weg b): der Seiten-Ausgang "geht in einer Dashboard-Vorlage auf" bekommt einen eigenen Status statt der Notloesung blocked+Feld.
- STATUSES 6 -> 7, COVERED_BY_TEMPLATE_CUTOVER + SIDE_EXIT_STATUSES - isOpenWork als EINE Definition von "offen" (pickNext + summarizeCounts lesen sie beide, koennen also nicht auseinanderlaufen) - summarizeCounts: neue open/pctOpen/sideExit/coveredByTemplate; total und pctVerifiedImproved BEWUSST unveraendert -> keine stille Nenner-Verschiebung, die Quote bleibt ueber den Tag hinweg vergleichbar - STATUS_PROGRESS_RANK umnummeriert, neuer Wert ueber blocked/unter compatible - ohne Rang faellt er ueber ?? 0 auf pending und verloere still jeden Zwillings-Vergleich - renderMd: Bruch-Warnung wie DEDUP_CUTOVER, Modul-Spalten aus STATUSES statt hart verdrahtet, eigene offen-Zeile - cmdAudit gegen SIDE_EXIT_STATUSES, Kennzahl-Namen tragen die gemessene Groesse (_ohneSeitenausgang statt _notWontfix) - Testdatei umgedreht: die Probe "Status-Satz wird NICHT erweitert" ist ueberholt und belegt jetzt das Gegenteil, mit Positiv-Kontrollen
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MnH17sGDL6zTAvv1cbcKFS
Fixmodule-feature
_websites braucht default:undefined, sonst materialisiert die Liste als []
Die Umkehrprobe am Persistat hat es gefangen, der Bau nicht: eine Mongoose- Liste materialisiert sich IMMER als []. Ohne default: undefined waere _websites: [] beim naechsten Speichern in alle 38 Module und 554 Features gewandert — genau der Vorgabewert, den der Auftrag ausdruecklich verbietet, und er machte "nie entschieden" von "ausdruecklich mandanten-global" ununterscheidbar.
Dieselbe Fehlerklasse wie quality.flags und IEmployee.benefits (Kaskade 64).
Hydrierung jetzt 20/20 gruen, inkl. Negativ-Kontrolle (erfundenes Feld faellt, strict greift also wirklich) und Umkehrprobe am Persistat.
Ohne die Zwillinge verwirft `strict` jede Eingabe STILL — der Vertrag traege die Felder, gespeichert wuerde nichts, und kein Fehler erschiene.
Flip-Probe beidseitig gefahren: gegen interfaces 2026.447.1 bricht der Bau mit 2x TS2353 (Features.ts:53, Modules.ts:58), gegen 2026.448.1 mit 0 Fehlern. Beide .add()-Bloecke sind also wirklich typgeprueft (der Konstruktor traegt den Typ-Parameter).
ZWEI Schalter, nicht einer — D3 des Kanons, CEO-Entscheid 2026-08-21 "beide Felder": published = LEBENSZYKLUS, showOnWebsite = KANAL.
KEIN Default, KEIN Backfill bei keinem der Felder: ein Vorgabewert wanderte beim naechsten Speichern in alle 38 Module bzw. 554 Features.
Zwei Lesepfad-Indexe je Schema deklariert (Bauform wie Awards). BEWUSST nicht unique: der Kanon nennt {_customerID, _websites, slug} als Schluessel, und _websites ist eine LISTE — ein unique-Index darueber traefe je Eintrag statt je Kombination. Gleiche Lage wie Awards/Partners.
Fixalerts
Laerm-Drossel las den Kill-Schalter falsch — value statt isEnabled
`Flags.isEnabled` beantwortet NICHT „steht der Schalter auf an?", sondern „gibt es das Merkmal und ist es nicht stale/archived?" — `flag.value` kommt in seiner Auswertung gar nicht vor. Ein Kill-Schalter mit dem Wert "false" galt damit als EINGESCHALTET, allein weil jemand ihn angelegt hat.
Gemessener Schaden: beim Mandanten tolinax UG steht `config.wallboardAlertThrottle.disabled` seit dem 2026-07-14T19:52 auf value:"false", und die Beschreibung des Schalters sagt woertlich „REVERT des Notnagels … auf false … wird nicht mehr gebraucht". Der Rueckbau ist NIE wirksam geworden — die Drossel war fuenfeinhalb Wochen aus. Unbemerkt, weil ein erneut klingelnder Dauerfehler an einer Wandanzeige wie normales Alarmieren aussieht. Belegt am 2026-08-21: dieselbe Signatur klingelte in zehn Minuten 16-mal, waehrend zehn andere Alarme im selben Fenster je genau einmal klingelten.
Der Beleg stand in derselben Datei: die Fenster-Groesse liest `getValue`, und der ENV-Zweig parst 'true'|'1'|'yes'. Der WERT war immer gemeint.
Rueckfall bewusst beibehalten: ohne hinterlegten Wert entscheidet weiterhin `isEnabled`, ein wertloses Merkmal aendert sein Verhalten also nicht.
5 Proben (249 statt 244), Flip auf `Boolean(raw)` — den Originalfehler — beisst.
Fixkeyword-planner
raise BullMQ lockDuration to survive long domain jobs
Domain jobs upsert up to 10,000 keywords sequentially (2 DB round-trips each) plus Google Ads quota-retry backoff, routinely exceeding the 30s BullMQ default lockDuration. Once the lock expires mid-job, stalled-job recovery reassigns the job and the original process's later moveToFinished races the reassignment, surfacing as "could not renew lock", "Missing key for job ... moveToFinished" and "Lock mismatch" errors (dozens/week in the Errors-as-Tasks firehose). Raise lockDuration to 10 minutes on both keyword-planner workers.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011XC3R3yf9u7xnZneEKnU8w
Fixmodule-feature
Vertrag sagt die UEBERGANGS-Semantik der Schalter, nicht die Ziel-Semantik
Der 448.0-Kommentar behauptete "absent = auf der Website nicht zeigen". Der Leser kann das heute nicht tun: 38 Module und 554 Funktionen tragen das Feld nicht, und Backfill ist ausdruecklich ausgeschlossen — ein strenges Tor schaltete die Achse in derselben Sekunde dunkel.
Richtig ist: absent = KEINE Aussage, Rueckfall auf das bestehende Ersatz-Tor ("traegt eine kuratierte Beschreibung", selbst fail-closed); GESETZT gewinnt das Feld in beide Richtungen. Die Ziel-Semantik (absent = nicht zeigen) ist als solche benannt und braucht einen eigenen Vorgang.
Ein Vertrag, der etwas anderes behauptet als sein Leser tut, ist die teuerste Sorte Kommentar — er bescheinigt sich selbst.