Files
tessera-ctl/.planning/phases/17-eigene-ausschreibungs-quellen-je-nutzer/17-03-PLAN.md
T
2026-08-12 08:26:00 +02:00

22 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, user_setup, estimate, must_haves
phase plan type wave depends_on files_modified autonomous requirements user_setup estimate must_haves
17-eigene-ausschreibungs-quellen-je-nutzer 03 execute 3
17-01
17-02
apps/web/src/app/(portal)/modules/tender-radar/my-sources/page.tsx
apps/web/src/app/(portal)/modules/tender-radar/my-sources/my-sources.test.tsx
apps/web/src/app/(portal)/modules/tender-radar/settings/page.tsx
apps/web/src/app/(portal)/modules/tender-radar/settings/settings-roles.test.tsx
apps/web/src/app/(portal)/modules/tender-radar/settings/components/RssFeedListForm.tsx
apps/web/src/app/(portal)/modules/tender-radar/settings/components/RssFeedListForm.test.tsx
apps/web/src/app/(portal)/modules/tender-radar/settings/components/DigestIntervalForm.tsx
apps/web/src/app/(portal)/modules/tender-radar/page.tsx
apps/web/src/lib/tender-radar-api.ts
apps/web/src/messages/de.json
apps/web/src/messages/en.json
.planning/todos/pending/2026-08-11-tender-radar-einstellungen-mischen-rollen.md
true
SRC-04
SRC-05
tokens raw_tokens tasks confidence
55000 55000 3 low
truths artifacts key_links
Auf `/modules/tender-radar/my-sources` findet ein normaler Nutzer alles, was ihm gehoert: sein Postfach, seine Feeds, sein Benachrichtigungsintervall (D-01, D-02, D-04).
Auf `/modules/tender-radar/settings` bleibt nur Administrationskram: Abrufintervall der oeffentlichen Quelle und die plattformweiten Feeds (D-03).
Ein normaler Nutzer sieht auf der Administrationsseite keine Bedienelemente mehr, die beim Speichern in eine Fehlermeldung laufen (offener Punkt 5).
Das Zahnrad auf der Modulseite fuehrt jeden Nutzer dorthin, wo er etwas einstellen darf.
Alle neuen Beschriftungen liegen in beiden Sprachdateien vor.
apps/web/src/app/(portal)/modules/tender-radar/my-sources/page.tsx
apps/web/src/app/(portal)/modules/tender-radar/settings/components/DigestIntervalForm.tsx
apps/web/src/app/(portal)/modules/tender-radar/my-sources/my-sources.test.tsx
apps/web/src/app/(portal)/modules/tender-radar/settings/settings-roles.test.tsx
RssFeedListForm(scope) <-> POST /rss-feeds mit `scope` — dieselbe Komponente bedient beide Seiten, die Berechtigung entscheidet der Server.
useAuthStore().user.role <-> Sichtbarkeit der Administrationsabschnitte — reine Anzeigefrage, die verbindliche Pruefung bleibt im Server.
Zahnrad in tender-radar/page.tsx <-> /modules/tender-radar/my-sources — der Weg, den jeder Nutzer gehen darf.
Die Oberflaeche wird nach Zustaendigkeit getrennt. Bisher stehen vier Abschnitte untereinander auf einer Seite, die drei verschiedenen Zustaendigkeiten gehoeren — wer nur seine Benachrichtigungen auf woechentlich stellen will, scrollt an Abrufintervallen, Feed-Adressen und Postfach-Zugangsdaten vorbei und sieht Bedienelemente, die er gar nicht bedienen darf.

Nach diesem Plan gibt es zwei Seiten:

  • Meine Quellen (/modules/tender-radar/my-sources, fuer jeden mit Modulzugang): mein Postfach, meine Feeds, mein Benachrichtigungsintervall.
  • Einstellungen (/modules/tender-radar/settings, nur Administration): Abrufintervall der oeffentlichen Quelle und die plattformweiten Feeds.

Purpose: D-03, D-04 und den urspruenglichen Anlass des Backlog-Punkts abschliessen. Output: Vollstaendige Nutzerseite, reduzierte und rollengepruefte Administrationsseite, angepasstes Zahnrad, Beschriftungen in beiden Sprachen.

Entscheidung zum offenen Punkt 5 (Rollenpruefung). Die Administrationsseite prueft die Rolle des angemeldeten Kontos aus dem bereits vorhandenen Anmelde-Speicher im Browser und zeigt einem normalen Nutzer statt der Bedienelemente einen kurzen Satz mit einem Verweis auf "Meine Quellen". Begruendung: Die verbindliche Absicherung liegt und bleibt im Server — die Endpunkte fuer das Abrufintervall und fuer plattformweite Feeds sind dort rollengeprueft. Die Pruefung im Browser ist ausschliesslich eine Anzeigefrage: sie verhindert, dass jemand Knoepfe sieht, die beim Speichern in eine Fehlermeldung laufen. Genau das war der Anlass des Backlog-Punkts.

<execution_context> @$HOME/.claude/gsd-core/workflows/execute-plan.md @$HOME/.claude/gsd-core/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/17-eigene-ausschreibungs-quellen-je-nutzer/17-CONTEXT.md @.planning/phases/17-eigene-ausschreibungs-quellen-je-nutzer/17-01-SUMMARY.md @.planning/phases/17-eigene-ausschreibungs-quellen-je-nutzer/17-02-SUMMARY.md @.planning/todos/pending/2026-08-11-tender-radar-einstellungen-mischen-rollen.md

@apps/web/src/app/(portal)/modules/tender-radar/settings/page.tsx @apps/web/src/app/(portal)/modules/tender-radar/settings/components/RssFeedListForm.tsx @apps/web/src/lib/tender-radar-api.ts @apps/web/src/lib/stores/auth-store.ts

Task 1: "Meine Quellen" wird vollstaendig — Postfach, eigene Feeds, Benachrichtigung apps/web/src/lib/tender-radar-api.ts, apps/web/src/app/(portal)/modules/tender-radar/settings/components/RssFeedListForm.tsx, apps/web/src/app/(portal)/modules/tender-radar/settings/components/DigestIntervalForm.tsx, apps/web/src/app/(portal)/modules/tender-radar/my-sources/page.tsx, apps/web/src/messages/de.json, apps/web/src/messages/en.json apps/web/src/app/(portal)/modules/tender-radar/settings/page.tsx, apps/web/src/app/(portal)/modules/tender-radar/settings/components/RssFeedListForm.tsx, apps/web/src/lib/tender-radar-api.ts (Zeilen 370-560) - Auf "Meine Quellen" stehen drei Abschnitte untereinander: Mein Postfach, Meine Feeds, Benachrichtigung. - Die Feed-Liste dort zeigt ausschliesslich die eigenen Feeds; plattformweite Feeds erscheinen darunter als kurze, nicht bedienbare Aufzaehlung mit dem Hinweis, dass die Administration sie pflegt. - Ein neu angelegter Feed erscheint sofort in der eigenen Liste. - Zu einem plattformweiten Feed gibt es auf dieser Seite keinen Entfernen-Knopf. - Die Auswahl des Benachrichtigungsintervalls speichert und quittiert wie bisher. **Zugriffsschicht (`lib/tender-radar-api.ts`).** Der Typ `RssFeedSource` bekommt das vom Server gelieferte Kennzeichen, ob der Eintrag plattformweit ist. `createRssFeed` bekommt einen zweiten Parameter fuer den Wirkungsbereich (`personal` oder `platform`, Vorgabe `personal`), der in den Rumpf der Anfrage wandert. `listRssFeeds` und `deleteRssFeed` bleiben in ihrer Signatur, ihr Verhalten aendert sich serverseitig.

Feed-Liste (RssFeedListForm.tsx). Die Komponente bekommt eine Eigenschaft scope mit den Werten personal und platform. Sie laedt weiterhin die eine Liste vom Server und teilt sie selbst auf:

  • scope="personal": bearbeitbar sind die eigenen Eintraege (anlegen, entfernen). Die plattformweiten Eintraege erscheinen darunter als schlichte Aufzaehlung ohne Knoepfe, mit einem Satz, dass die Administration sie pflegt — so ist auf einen Blick klar, dass niemand bei null anfaengt.
  • scope="platform": bearbeitbar sind die plattformweiten Eintraege; angelegt wird mit dem entsprechenden Wirkungsbereich. Eigene Eintraege anderer Nutzer tauchen hier gar nicht auf, weil der Server sie nicht liefert. Die Fehleranzeige bleibt wie sie ist: eine abgelehnte Adresse zeigt weiterhin die ausfuehrliche Meldung des Servers, nicht einen selbstgebauten Ersatztext. Die Ablehnung wegen erreichter Obergrenze (aus Plan 17-02) laeuft ueber denselben Weg und braucht keine Sonderbehandlung.

Benachrichtigungsintervall (DigestIntervalForm.tsx). Den heute in der Einstellungsseite eingebetteten Block (Laden beim Aufbau, Auswahlfeld Taeglich/Woechentlich/Aus, Speichern bei Aenderung, Erfolgs- und Fehlermeldung) unveraendert in eine eigene Komponente herausloesen. Kein Verhalten aendern, keine neuen Beschriftungsschluessel — nur verschieben, damit beide Seiten dieselbe Umsetzung benutzen koennen und die Einstellungsseite in Task 2 sauber kleiner wird.

Seite (my-sources/page.tsx). Die in Plan 17-01 angelegte Seite um zwei Abschnitte erweitern: unter "Mein Postfach" folgt "Meine Feeds" (RssFeedListForm mit scope="personal"), darunter "Benachrichtigung" (DigestIntervalForm). Aufbau, Abstaende und Trennlinien wie auf der heutigen Einstellungsseite uebernehmen, damit nichts fremd wirkt. Fuer Konten mit Administratorrolle unten ein Verweis auf die Administrationsseite; fuer alle anderen erscheint dieser Verweis nicht. Die Rolle kommt aus dem vorhandenen Anmelde-Speicher; solange dieser noch nicht befuellt ist, den Verweis weglassen statt kurz aufblitzen zu lassen.

Beschriftungen (messages/de.json, messages/en.json). Alle neuen Schluessel im Namensraum tenderRadar unter mySources anlegen und in beiden Dateien pflegen: Seitentitel, die drei Abschnittsueberschriften, der ehrliche Hinweis zu D-05 (Ausschreibungen aus den eigenen Quellen erscheinen auch bei Kollegen desselben Mandanten), der Satz zu den von der Administration gepflegten Feeds, der Verweis auf die Administrationsseite. Keine Beschriftung nur auf Deutsch anlegen. cd /home/vicolab/projects/tessera-ctl && pnpm --filter @tessera/web type-check cd /home/vicolab/projects/tessera-ctl && node -e "const de=require('./apps/web/src/messages/de.json'),en=require('./apps/web/src/messages/en.json');const walk=(o,p='')=>Object.entries(o).flatMap(([k,v])=>typeof v==='object'&&v?walk(v,p+k+'.'):[p+k]);const d=walk(de.tenderRadar),e=walk(en.tenderRadar);const miss=d.filter(k=>!e.includes(k)).concat(e.filter(k=>!d.includes(k)));if(miss.length){console.error('Fehlende Uebersetzungen:',miss);process.exit(1)}console.log('i18n vollstaendig:',d.length,'Schluessel')" Als normaler Nutzer /modules/tender-radar/my-sources oeffnen: drei Abschnitte sind sichtbar. Einen eigenen RSS-Feed anlegen — er erscheint in der eigenen Liste, der plattformweite service.bund.de-Feed steht darunter ohne Entfernen-Knopf. Das Benachrichtigungsintervall auf "Woechentlich" stellen, Seite neu laden, Wert steht noch. Kein Verweis auf die Administrationsseite sichtbar. Die Typpruefung laeuft fehlerfrei, beide Sprachdateien decken denselben Schluesselsatz ab. Ein normaler Nutzer kann auf einer Seite Postfach, eigene Feeds und Benachrichtigungsintervall pflegen und sieht keinen Knopf, der ihm verwehrt ist.

Task 2: Die Administrationsseite schrumpft auf das, was Administration ist apps/web/src/app/(portal)/modules/tender-radar/settings/page.tsx, apps/web/src/app/(portal)/modules/tender-radar/page.tsx, apps/web/src/messages/de.json, apps/web/src/messages/en.json apps/web/src/app/(portal)/modules/tender-radar/settings/page.tsx, apps/web/src/app/(portal)/modules/tender-radar/page.tsx (Zeilen 60-95), apps/web/src/lib/stores/auth-store.ts - Ein Konto mit Rolle ADMIN oder SUPER_ADMIN sieht auf der Einstellungsseite genau zwei Abschnitte: Abrufintervall und plattformweite Feeds. - Ein Konto mit Rolle USER sieht dort keinen dieser Abschnitte, sondern einen kurzen Satz und einen Verweis auf "Meine Quellen". - Solange die Kontodaten im Browser noch nicht geladen sind, erscheint weder das eine noch das andere, sondern ein Platzhalter — kein kurzes Aufblitzen der Administrationsabschnitte. - Postfach und Benachrichtigungsintervall kommen auf dieser Seite nicht mehr vor. - Das Zahnrad auf der Modulseite fuehrt fuer jeden Nutzer nach "Meine Quellen". **Einstellungsseite (`settings/page.tsx`).** Die Abschnitte "E-Mail-Alerts" und "Benachrichtigungen" ersatzlos herausnehmen — sie leben ab jetzt auf "Meine Quellen" (D-01, D-04). Uebrig bleiben das Abrufintervall (`SourceConfigForm`, D-03) und die plattformweiten Feeds (`RssFeedListForm` mit `scope="platform"`). Der ganze Inhalt wird von einer Rollenpruefung umschlossen: Rolle aus dem vorhandenen Anmelde-Speicher lesen; ist sie noch nicht bekannt, einen Ladeplatzhalter zeigen; ist sie ADMIN oder SUPER_ADMIN, den Inhalt zeigen; sonst einen kurzen Satz ("Diese Seite verwaltet plattformweite Einstellungen und ist Administratoren vorbehalten.") mit einem Verweis auf `/modules/tender-radar/my-sources`. Der Kopfkommentar der Datei wird auf den neuen Zuschnitt umgeschrieben; die Absaetze zu den entfernten Abschnitten entfallen, ein Absatz zu Phase 17 kommt hinzu. Beim Abschnitt der plattformweiten Feeds einen Satz ergaenzen, dass diese fuer alle Nutzer gelten und persoenliche Feeds woanders liegen.

Modulseite (page.tsx). Das Zahnrad zeigt heute auf die Administrationsseite. Es zeigt kuenftig auf /modules/tender-radar/my-sources — das ist die Seite, die jeder Nutzer aufrufen darf, und Administratoren gehen von dort mit einem Klick weiter. Beschriftung und Vorlesetext des Knopfes entsprechend anpassen (neuer Schluessel, alter Schluessel bleibt fuer den Verweis auf der Nutzerseite in Gebrauch, falls passend — sonst entfernen). Den vorhandenen Kommentar zum absoluten Verweis (die dynamische Einstellungsroute greift fuer dieses Modul nicht) beibehalten und auf den neuen Zielpfad anpassen.

Beschriftungen. Neue Schluessel in beiden Sprachdateien anlegen: der Hinweistext fuer Nutzer ohne Administratorrolle, der Verweistext auf "Meine Quellen", der Zusatzsatz beim Abschnitt der plattformweiten Feeds, die neue Zahnrad-Beschriftung. Nicht mehr benutzte Schluessel (settings.emailSectionTitle, settings.emailSectionBody, settings.notificationsSectionTitle und die Digest-Schluessel, sofern sie auf die Nutzerseite umziehen) erst entfernen, nachdem im gesamten apps/web/src nachgesehen wurde, dass sie nirgends mehr verwendet werden. cd /home/vicolab/projects/tessera-ctl && pnpm --filter @tessera/web type-check cd /home/vicolab/projects/tessera-ctl && node -e "const de=require('./apps/web/src/messages/de.json'),en=require('./apps/web/src/messages/en.json');const walk=(o,p='')=>Object.entries(o).flatMap(([k,v])=>typeof v==='object'&&v?walk(v,p+k+'.'):[p+k]);const d=walk(de.tenderRadar),e=walk(en.tenderRadar);const miss=d.filter(k=>!e.includes(k)).concat(e.filter(k=>!d.includes(k)));if(miss.length){console.error('Fehlende Uebersetzungen:',miss);process.exit(1)}console.log('i18n vollstaendig:',d.length,'Schluessel')" Als Konto mit Rolle USER /modules/tender-radar/settings direkt in der Adresszeile aufrufen: es erscheinen keine Bedienelemente, sondern der Hinweis samt Verweis. Danach als Administrator dieselbe Adresse: Abrufintervall und plattformweite Feeds sind da, Postfach und Benachrichtigungsintervall nicht mehr. Auf der Modulseite fuehrt das Zahnrad in beiden Faellen nach "Meine Quellen". Die Einstellungsseite traegt nur noch Abrufintervall und plattformweite Feeds und ist fuer Konten ohne Administratorrolle leer bis auf Hinweis und Verweis. Das Zahnrad fuehrt jeden Nutzer auf eine Seite, auf der er etwas einstellen darf. Beide Sprachdateien decken denselben Schluesselsatz ab.

Task 3: Tests fuer beide Seiten und der Backlog-Punkt wird zugemacht apps/web/src/app/(portal)/modules/tender-radar/my-sources/my-sources.test.tsx, apps/web/src/app/(portal)/modules/tender-radar/settings/settings-roles.test.tsx, apps/web/src/app/(portal)/modules/tender-radar/settings/components/RssFeedListForm.test.tsx, .planning/todos/pending/2026-08-11-tender-radar-einstellungen-mischen-rollen.md apps/web/src/app/(portal)/modules/tender-radar/settings/components/RssFeedListForm.test.tsx, apps/web/src/app/(portal)/modules/tender-radar/settings/components/SourceConfigForm.test.tsx - `RssFeedListForm` mit `scope="personal"`: die gelieferte Liste enthaelt einen plattformweiten und einen eigenen Eintrag; nur der eigene traegt einen Entfernen-Knopf, der plattformweite erscheint ohne. - `RssFeedListForm` mit `scope="platform"`: umgekehrt. - `RssFeedListForm` mit `scope="personal"` schickt beim Anlegen den Wirkungsbereich `personal` mit, mit `scope="platform"` entsprechend `platform`. - Einstellungsseite mit Rolle USER: die Ueberschriften der Administrationsabschnitte kommen nicht vor, der Verweis auf "Meine Quellen" schon. - Einstellungsseite mit Rolle ADMIN: die Administrationsabschnitte kommen vor. - Einstellungsseite ohne geladene Kontodaten: weder das eine noch das andere. - "Meine Quellen": alle drei Abschnittsueberschriften kommen vor; der Verweis auf die Administrationsseite nur bei Rolle ADMIN. **Testaufbau.** Vorbild sind die vorhandenen Tests im selben Verzeichnis: die Zugriffsschicht `@/lib/tender-radar-api` wird ersetzt, `next-intl` wird durch eine Zuordnung von Schluessel auf Text ersetzt. Neu hinzu kommt das Ersetzen des Anmelde-Speichers, damit die Rolle im Test gesetzt werden kann — dieselbe Ersetzungsform wie bei den uebrigen Ersetzungen, ueber den Modulpfad des Speichers.

Wichtig zur Aussagekraft. Die Pruefungen muessen an dem haengen, was der Nutzer sieht: vorhandene beziehungsweise fehlende Beschriftungen und Knoepfe im gerenderten Ergebnis. Nicht pruefen, ob eine Komponente "aufgerufen wurde" — und die erwarteten Texte im Test woertlich hinschreiben, nicht aus derselben Zuordnung ableiten, die auch die Komponente benutzt. Ein Test, der seine Erwartung mit dem Pruefling selbst baut, beweist nichts.

RssFeedListForm.test.tsx. Bestehende Faelle auf die neue Eigenschaft scope nachziehen (Vorgabe im Test ausdruecklich setzen, nicht raten) und die drei Faelle aus dem Abschnitt "behavior" ergaenzen. Der bestehende Fall, dass die ausfuehrliche Servermeldung bei einer gesperrten Adresse angezeigt wird, muss unveraendert weiterlaufen.

settings-roles.test.tsx (neu). Die drei Rollenfaelle der Einstellungsseite. Zusaetzlich ein Fall, der belegt, dass die Ueberschriften der entfernten Abschnitte (Postfach, Benachrichtigung) auf dieser Seite nicht mehr vorkommen.

my-sources.test.tsx (neu). Die drei Abschnitte sind da; der Verweis auf die Administrationsseite erscheint nur bei Administratorrolle.

Backlog-Punkt. 2026-08-11-tender-radar-einstellungen-mischen-rollen.md nach .planning/todos/completed/ verschieben und im Kopf einen kurzen Abschnitt ergaenzen, wie er geloest wurde: nicht nur die Rollenpruefung aus der urspruenglichen Skizze, sondern der Umbau auf nutzereigene Quellen, und die Grundsatzfrage aus Punkt 2 ist mit "eigene nutzerseitige Modulseite" beantwortet — das ist ab jetzt die Bauform, an der sich DKV-Fleet und kuenftige Module orientieren. cd /home/vicolab/projects/tessera-ctl && pnpm --filter @tessera/web exec vitest run src/app/(portal)/modules/tender-radar cd /home/vicolab/projects/tessera-ctl && pnpm --filter @tessera/web exec vitest run cd /home/vicolab/projects/tessera-ctl && pnpm --filter @tessera/api exec vitest run src/tenders && pnpm --filter @tessera/api type-check && pnpm --filter @tessera/web type-check test ! -f .planning/todos/pending/2026-08-11-tender-radar-einstellungen-mischen-rollen.md && test -f .planning/todos/completed/2026-08-11-tender-radar-einstellungen-mischen-rollen.md Alle Tests in Web und API laufen gruen, beide Typpruefungen sind sauber, der Backlog-Punkt liegt im Ordner der erledigten Punkte.

<threat_model>

Trust Boundaries

Boundary Description
Browser-Anzeige -> Serverpruefung Die Rollenpruefung im Browser ist eine Anzeigefrage; die verbindliche Pruefung liegt im Server.
Nutzerseite -> Endpunkte fuer plattformweite Einstellungen Die Nutzerseite darf keinen Weg zu plattformweiten Schreibvorgaengen oeffnen.

STRIDE Threat Register

Threat ID Category Component Severity Disposition Mitigation Plan
T-17-14 Elevation of Privilege Rollenpruefung im Browser medium mitigate Die Pruefung blendet nur Bedienelemente aus. Die Endpunkte fuer Abrufintervall und plattformweite Feeds bleiben serverseitig rollengeprueft (Plan 17-02, T-17-08). Ein Test belegt, dass die Rolle USER die Abschnitte nicht zu sehen bekommt.
T-17-15 Elevation of Privilege RssFeedListForm mit scope="personal" high mitigate Der Wirkungsbereich wandert als Wunsch in die Anfrage; ob er gewaehrt wird, entscheidet ausschliesslich der Server. Ein manipuliertes Frontend erreicht nichts.
T-17-16 Information Disclosure Kurzes Aufblitzen der Administrationsabschnitte vor dem Laden der Kontodaten low mitigate Solange die Rolle unbekannt ist, wird ein Platzhalter gezeigt; ein Test deckt genau diesen Zustand ab.
T-17-17 Repudiation Unvollstaendige Uebersetzung low mitigate Ein Pruefschritt vergleicht die Schluesselsaetze beider Sprachdateien und bricht bei Abweichung ab.
T-17-SC Tampering Paketinstallationen low accept Diese Phase installiert kein neues Paket.
</threat_model>
1. `pnpm --filter @tessera/web exec vitest run` — vollstaendig gruen. 2. `pnpm --filter @tessera/api exec vitest run src/tenders` — vollstaendig gruen. 3. Beide Typpruefungen fehlerfrei. 4. Schluesselvergleich der beiden Sprachdateien ohne Abweichung. 5. Browser-Gegenprobe mit zwei Konten (Rolle USER und Rolle ADMIN) wie in den `human-check`-Schritten der Tasks 1 und 2. Eine erfolgreiche Antwort des Servers allein reicht als Nachweis nicht — es zaehlt, was auf der Seite steht.

<success_criteria>

  • "Meine Quellen" traegt Postfach, eigene Feeds und Benachrichtigungsintervall.
  • Die Einstellungsseite traegt nur Abrufintervall und plattformweite Feeds.
  • Ein normaler Nutzer sieht auf der Einstellungsseite keine Bedienelemente mehr, die beim Speichern in eine Fehlermeldung laufen.
  • Das Zahnrad fuehrt jeden Nutzer auf eine fuer ihn bedienbare Seite.
  • Alle Beschriftungen liegen in Deutsch und Englisch vor.
  • Der Backlog-Punkt ist abgeschlossen und die Grundsatzfrage darin beantwortet. </success_criteria>
Create `.planning/phases/17-eigene-ausschreibungs-quellen-je-nutzer/17-03-SUMMARY.md` when done.