Compare commits

...

4 commits

Author SHA1 Message Date
43320be1fb Companion-App: finale Action-Card-Grafiken eingebunden (29/30)
- 29 PNGs als app/cards/s<service>-c<change>.png (PNG-Nr n -> Service floor(n/5),
  Change n%5). Karten-Screen zeigt jetzt die finale Grafik statt Text.
- Helfer cardImg()/cardMedia(); Text-Fallback fuer die noch fehlende Karte
  s0-c0 ("Open Source von oben!").
- Service Worker cacht die Karten-Grafiken vorab (Offline), CACHE -> v2.
- README-Umsetzungsstand ergaenzt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 14:12:20 +02:00
b12bed892a Workshop-Arbeitsstand: Materialliste + Board-Layout auf 5 Review / 39 Pucks
- materialliste: Review 6->5, Station-Pucks 36 (+1 Blank Reserve), Bahn 39
  Positionen, Etiketten 39; Arbeitsstand-Hinweis ergaenzt.
- gen_board_layout.py: Review-Aktivitaeten = Franks 5; Titel/Subtitle 39/36;
  board-layout.svg neu generiert (39 Pucks, well-formed).
- Notiz review-phase_arbeitsstand-frank.md aktualisiert (Material + Board jetzt
  auf Arbeitsstand; YAML + kanonisches Konzept bleiben bei 6/40 bis Michael).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 13:51:38 +02:00
10fe33fba6 Review-Phase: Arbeitsstand (Frank, Change-Enablement) in die App
- App-Review rv_01..rv_06 -> 5 Aktivitaeten (Service-Reviews durchfuehren,
  Bewertung, Aenderungen definieren/starten/implementieren). Rolle DPM ergaenzt;
  Routing-Pfade RUN/DPM/MB an rv_04; Tour-Narrative angepasst.
- RACI + Quiz abgeleitet (Frank nennt nur Aktivitaeten) und als Arbeitsstand
  markiert. App jetzt 39 Stationen.
- NICHT geaendert: Blueprint-YAML, kanonisches Konzept, materialliste, board-layout
  (zeigen weiter 6 Review-Pucks) — vor Uebernahme mit Michael abstimmen.
- Notiz: 00_Konzept/review-phase_arbeitsstand-frank.md (Vorschlag + offene Punkte:
  MB undefiniert, Retirement fehlt, Vokabular-/RACI-Abgleich).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 13:43:57 +02:00
668367a241 Companion-App: 30 Action-Card-Texte (Titel + Szenario) eingebaut
- USE_CASES.changes von Strings auf {titel, text} umgestellt (6 Services x 5 =
  30 Action Cards, Stil wie das Freiburg-Action-Card-PDF).
- UI zeigt Titel (fett) + Text + "Was passiert an welchen Stellen?";
  Karten-Screen, Run-Token, Tour-Banner und Debrief (MD+JSON) nachgezogen.
- Helfer acard()/cardHtml(); JS-Syntax + Datenform verifiziert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 17:01:28 +02:00
36 changed files with 212 additions and 153 deletions

View file

@ -0,0 +1,59 @@
# Review-Phase — Arbeitsstand (Vorschlag Frank)
**Status:** Arbeitsstand **für den Workshop** · **nicht** im Blueprint-YAML · **nicht**
kanonisch ins Konzept übernommen (vor Übernahme mit **Michael** rückkoppeln).
**Quelle:** E-Mail Frank · **Logik:** Change-Enablement (analog YASM).
> **Wo schon übernommen (Workshop-Material):** Companion-App (`04_Tablet-Quiz/app/`,
> Review = rv_01rv_05), `01_3D-Druck/materialliste.md` (**36** Station-Pucks / **39**
> Positionen) und die **`board-layout`-Skizze**. **Bewusst NICHT geändert:**
> `service-lifecycle_*.yaml` (Single Source of Truth) und das **kanonische Konzept**
> (`README_konzept.md`, Phasen-Tabelle zeigt weiter 6/40) — vor Übernahme mit
> **Michael** abstimmen.
## Die 5 vorgeschlagenen Aktivitäten
1. **Durchführen von Service-Reviews**
- KPIs & Monitoring auswerten, Problems & Incidents auswerten, Kundenfeedback sammeln/einholen
- Bewerten der zugrunde liegenden Infrastruktur
- Service-Review-Dokument ausfüllen
2. **Bewertung der Review-Ergebnisse**
- RFC erstellen (Normal- bzw. Major-Change)
- Berichte (wenn nötig) an die SOR weiterleiten
- Ergebnisse in der SOR ganzheitlich bewerten
3. **Definieren von Service-Änderungen**
- passende Änderungsvorschläge formulieren
- Vorschläge bewerten & konsolidieren
- ausgewählte Änderung beschreiben
4. **Starten von Service-Änderungen**
- Normal Change: Planung der Umsetzung
- Major Change: Routing klären (RUN / DPM / MB?)
- Major Change: „Change-Steckbrief" ausfüllen & weiterleiten
5. **Implementieren von Service-Änderungen**
- Normal & Major (Weg RUN): durchführen, dokumentieren, abschließen durch SO
- Major (Weg DPM): Change geht durch Demand- & Projektprozess
- Major (Weg MB): direkt in den Projektprozess oder Durchführung im RUN
## Abbildung gegenüber dem bisherigen Stand (rv_01rv_06)
| bisher | neu (Frank) |
|--------|-------------|
| rv_01 KPIs/RCA · rv_02 Performance-Review | 1. Service-Reviews durchführen |
| rv_03 SOR-Review | 2. Bewertung (RFC, SOR) |
| rv_04 Service Improvement | 3. Änderungen definieren |
| rv_05 Redesign → DPM | 4./5. Starten/Implementieren (Routing) |
| rv_06 Außerbetriebnahme | *— nicht explizit abgedeckt* |
## Offene Punkte (vor Konzept-/YAML-Übernahme klären)
- **„MB"** als dritter Routing-Weg ist **nicht definiert** (RUN und DPM sind klar).
- **Retirement / Außerbetriebnahme** (bisher rv_06) fehlt — bewusst entfallen oder
unter „Service-Änderung" subsumiert?
- **RACI + Quizfragen** wurden für die App **abgeleitet** (Franks Entwurf nennt nur
die Aktivitäten) — fachlich gegenprüfen.
- **Vokabular** („RFC", „Change-Steckbrief", Routing RUN/DPM/MB) mit den anderen
Phasen und der Rollen-/Gate-YAML abgleichen.
- **Konsequenz Board:** Bahn = **39 statt 40** Positionen (36 Station-Pucks + 3 Gate).
In Workshop-Material (App, materialliste, board-layout) bereits nachgezogen; alle
Pucks sind identische Blanks → faktisch nur **1 Puck weniger / als Reserve**. Das
**kanonische Konzept (`README_konzept.md`) zeigt noch 40** — nach Michael-Freigabe nachziehen.

View file

@ -1,8 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1576 846" font-family="Arial, Helvetica, sans-serif"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1576 846" font-family="Arial, Helvetica, sans-serif">
<rect x="0" y="0" width="1576" height="846" fill="#f7f7f5"/> <rect x="0" y="0" width="1576" height="846" fill="#f7f7f5"/>
<defs><marker id="ah" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto" markerUnits="strokeWidth"><path d="M0,0 L7,3 L0,6 Z" fill="#666"/></marker></defs> <defs><marker id="ah" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto" markerUnits="strokeWidth"><path d="M0,0 L7,3 L0,6 Z" fill="#666"/></marker></defs>
<text x="30" y="44" font-size="26" font-weight="800" fill="#1a1a1a">Service-Lifecycle — Board-Layout (40 Pucks)</text> <text x="30" y="44" font-size="26" font-weight="800" fill="#1a1a1a">Service-Lifecycle — Board-Layout (39 Pucks)</text>
<text x="30" y="68" font-size="14" fill="#555">37 Aktivitaeten + 3 Gate-Pucks · 1 Puck = &#216;100 mm · lose Bahn, Sequenz links nach rechts</text> <text x="30" y="68" font-size="14" fill="#555">36 Aktivitaeten + 3 Gate-Pucks · 1 Puck = &#216;100 mm · lose Bahn, Sequenz links nach rechts</text>
<rect x="30" y="96" width="150" height="86" rx="9" fill="#2F80C9"/> <rect x="30" y="96" width="150" height="86" rx="9" fill="#2F80C9"/>
<text x="105.0" y="137.0" text-anchor="middle" font-size="17" font-weight="800" fill="#fff">DESIGN</text> <text x="105.0" y="137.0" text-anchor="middle" font-size="17" font-weight="800" fill="#fff">DESIGN</text>
<text x="105.0" y="157.0" text-anchor="middle" font-size="12" fill="#fff">4 Pucks</text> <text x="105.0" y="157.0" text-anchor="middle" font-size="12" fill="#fff">4 Pucks</text>
@ -428,7 +428,7 @@
<path d="M 1223.0 560 V 580.0 H 243.0 V 598" fill="none" stroke="#999" stroke-width="2.2" stroke-dasharray="5 4" marker-end="url(#ah)"/> <path d="M 1223.0 560 V 580.0 H 243.0 V 598" fill="none" stroke="#999" stroke-width="2.2" stroke-dasharray="5 4" marker-end="url(#ah)"/>
<rect x="30" y="600" width="150" height="86" rx="9" fill="#8E63B5"/> <rect x="30" y="600" width="150" height="86" rx="9" fill="#8E63B5"/>
<text x="105.0" y="641.0" text-anchor="middle" font-size="17" font-weight="800" fill="#fff">REVIEW</text> <text x="105.0" y="641.0" text-anchor="middle" font-size="17" font-weight="800" fill="#fff">REVIEW</text>
<text x="105.0" y="661.0" text-anchor="middle" font-size="12" fill="#fff">6 Pucks</text> <text x="105.0" y="661.0" text-anchor="middle" font-size="12" fill="#fff">5 Pucks</text>
<circle cx="243.0" cy="643.0" r="35" fill="#f3eff7" stroke="#8E63B5" stroke-width="2"/> <circle cx="243.0" cy="643.0" r="35" fill="#f3eff7" stroke="#8E63B5" stroke-width="2"/>
<circle cx="243.0" cy="616.0" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/> <circle cx="243.0" cy="616.0" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/>
<circle cx="264.1" cy="626.2" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/> <circle cx="264.1" cy="626.2" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/>
@ -439,7 +439,7 @@
<circle cx="221.9" cy="626.2" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/> <circle cx="221.9" cy="626.2" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/>
<circle cx="243.0" cy="643.0" r="16" fill="#ffffff" opacity="0.92" stroke="#8E63B5" stroke-width="0.8"/> <circle cx="243.0" cy="643.0" r="16" fill="#ffffff" opacity="0.92" stroke="#8E63B5" stroke-width="0.8"/>
<text x="243.0" y="643.5" text-anchor="middle" font-size="11.5" font-weight="700" fill="#1a1a1a">rv_01</text> <text x="243.0" y="643.5" text-anchor="middle" font-size="11.5" font-weight="700" fill="#1a1a1a">rv_01</text>
<text x="243.0" y="690.0" text-anchor="middle" font-size="9.5" fill="#333">Taktische RCA + KPIs</text> <text x="243.0" y="690.0" text-anchor="middle" font-size="9.5" fill="#333">Service-Reviews durchf.</text>
<line x1="294" y1="643.0" x2="296" y2="643.0" stroke="#666" stroke-width="2.2" marker-end="url(#ah)"/> <line x1="294" y1="643.0" x2="296" y2="643.0" stroke="#666" stroke-width="2.2" marker-end="url(#ah)"/>
<circle cx="341.0" cy="643.0" r="35" fill="#f3eff7" stroke="#8E63B5" stroke-width="2"/> <circle cx="341.0" cy="643.0" r="35" fill="#f3eff7" stroke="#8E63B5" stroke-width="2"/>
<circle cx="341.0" cy="616.0" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/> <circle cx="341.0" cy="616.0" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/>
@ -451,7 +451,7 @@
<circle cx="319.9" cy="626.2" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/> <circle cx="319.9" cy="626.2" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/>
<circle cx="341.0" cy="643.0" r="16" fill="#ffffff" opacity="0.92" stroke="#8E63B5" stroke-width="0.8"/> <circle cx="341.0" cy="643.0" r="16" fill="#ffffff" opacity="0.92" stroke="#8E63B5" stroke-width="0.8"/>
<text x="341.0" y="643.5" text-anchor="middle" font-size="11.5" font-weight="700" fill="#1a1a1a">rv_02</text> <text x="341.0" y="643.5" text-anchor="middle" font-size="11.5" font-weight="700" fill="#1a1a1a">rv_02</text>
<text x="341.0" y="690.0" text-anchor="middle" font-size="9.5" fill="#333">Performance &amp; Improvement</text> <text x="341.0" y="690.0" text-anchor="middle" font-size="9.5" fill="#333">Bewertung d. Ergebnisse</text>
<line x1="392" y1="643.0" x2="394" y2="643.0" stroke="#666" stroke-width="2.2" marker-end="url(#ah)"/> <line x1="392" y1="643.0" x2="394" y2="643.0" stroke="#666" stroke-width="2.2" marker-end="url(#ah)"/>
<circle cx="439.0" cy="643.0" r="35" fill="#f3eff7" stroke="#8E63B5" stroke-width="2"/> <circle cx="439.0" cy="643.0" r="35" fill="#f3eff7" stroke="#8E63B5" stroke-width="2"/>
<circle cx="439.0" cy="616.0" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/> <circle cx="439.0" cy="616.0" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/>
@ -463,7 +463,7 @@
<circle cx="417.9" cy="626.2" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/> <circle cx="417.9" cy="626.2" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/>
<circle cx="439.0" cy="643.0" r="16" fill="#ffffff" opacity="0.92" stroke="#8E63B5" stroke-width="0.8"/> <circle cx="439.0" cy="643.0" r="16" fill="#ffffff" opacity="0.92" stroke="#8E63B5" stroke-width="0.8"/>
<text x="439.0" y="643.5" text-anchor="middle" font-size="11.5" font-weight="700" fill="#1a1a1a">rv_03</text> <text x="439.0" y="643.5" text-anchor="middle" font-size="11.5" font-weight="700" fill="#1a1a1a">rv_03</text>
<text x="439.0" y="690.0" text-anchor="middle" font-size="9.5" fill="#333">SOR Periodischer Review</text> <text x="439.0" y="690.0" text-anchor="middle" font-size="9.5" fill="#333">Aenderungen definieren</text>
<line x1="490" y1="643.0" x2="492" y2="643.0" stroke="#666" stroke-width="2.2" marker-end="url(#ah)"/> <line x1="490" y1="643.0" x2="492" y2="643.0" stroke="#666" stroke-width="2.2" marker-end="url(#ah)"/>
<circle cx="537.0" cy="643.0" r="35" fill="#f3eff7" stroke="#8E63B5" stroke-width="2"/> <circle cx="537.0" cy="643.0" r="35" fill="#f3eff7" stroke="#8E63B5" stroke-width="2"/>
<circle cx="537.0" cy="616.0" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/> <circle cx="537.0" cy="616.0" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/>
@ -475,7 +475,7 @@
<circle cx="515.9" cy="626.2" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/> <circle cx="515.9" cy="626.2" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/>
<circle cx="537.0" cy="643.0" r="16" fill="#ffffff" opacity="0.92" stroke="#8E63B5" stroke-width="0.8"/> <circle cx="537.0" cy="643.0" r="16" fill="#ffffff" opacity="0.92" stroke="#8E63B5" stroke-width="0.8"/>
<text x="537.0" y="643.5" text-anchor="middle" font-size="11.5" font-weight="700" fill="#1a1a1a">rv_04</text> <text x="537.0" y="643.5" text-anchor="middle" font-size="11.5" font-weight="700" fill="#1a1a1a">rv_04</text>
<text x="537.0" y="690.0" text-anchor="middle" font-size="9.5" fill="#333">Service Improvement</text> <text x="537.0" y="690.0" text-anchor="middle" font-size="9.5" fill="#333">Aenderungen starten</text>
<line x1="588" y1="643.0" x2="590" y2="643.0" stroke="#666" stroke-width="2.2" marker-end="url(#ah)"/> <line x1="588" y1="643.0" x2="590" y2="643.0" stroke="#666" stroke-width="2.2" marker-end="url(#ah)"/>
<circle cx="635.0" cy="643.0" r="35" fill="#f3eff7" stroke="#8E63B5" stroke-width="2"/> <circle cx="635.0" cy="643.0" r="35" fill="#f3eff7" stroke="#8E63B5" stroke-width="2"/>
<circle cx="635.0" cy="616.0" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/> <circle cx="635.0" cy="616.0" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/>
@ -487,24 +487,12 @@
<circle cx="613.9" cy="626.2" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/> <circle cx="613.9" cy="626.2" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/>
<circle cx="635.0" cy="643.0" r="16" fill="#ffffff" opacity="0.92" stroke="#8E63B5" stroke-width="0.8"/> <circle cx="635.0" cy="643.0" r="16" fill="#ffffff" opacity="0.92" stroke="#8E63B5" stroke-width="0.8"/>
<text x="635.0" y="643.5" text-anchor="middle" font-size="11.5" font-weight="700" fill="#1a1a1a">rv_05</text> <text x="635.0" y="643.5" text-anchor="middle" font-size="11.5" font-weight="700" fill="#1a1a1a">rv_05</text>
<text x="635.0" y="690.0" text-anchor="middle" font-size="9.5" fill="#333">Redesign / Erweiterung</text> <text x="635.0" y="690.0" text-anchor="middle" font-size="9.5" fill="#333">Aenderungen umsetzen</text>
<line x1="686" y1="643.0" x2="688" y2="643.0" stroke="#666" stroke-width="2.2" marker-end="url(#ah)"/>
<circle cx="733.0" cy="643.0" r="35" fill="#f3eff7" stroke="#8E63B5" stroke-width="2"/>
<circle cx="733.0" cy="616.0" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/>
<circle cx="754.1" cy="626.2" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/>
<circle cx="759.3" cy="649.0" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/>
<circle cx="744.7" cy="667.3" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/>
<circle cx="721.3" cy="667.3" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/>
<circle cx="706.7" cy="649.0" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/>
<circle cx="711.9" cy="626.2" r="3" fill="none" stroke="#8E63B5" stroke-width="1.1" opacity="0.6"/>
<circle cx="733.0" cy="643.0" r="16" fill="#ffffff" opacity="0.92" stroke="#8E63B5" stroke-width="0.8"/>
<text x="733.0" y="643.5" text-anchor="middle" font-size="11.5" font-weight="700" fill="#1a1a1a">rv_06</text>
<text x="733.0" y="690.0" text-anchor="middle" font-size="9.5" fill="#333">Ausserbetriebnahme</text>
<path d="M 22 391.0 C 4 391.0, 4 517.0, 22 517.0" fill="none" stroke="#d23" stroke-width="2.6" marker-end="url(#ah)" marker-start="url(#ah)"/> <path d="M 22 391.0 C 4 391.0, 4 517.0, 22 517.0" fill="none" stroke="#d23" stroke-width="2.6" marker-end="url(#ah)" marker-start="url(#ah)"/>
<text x="2" y="454.0" font-size="11" fill="#d23" transform="rotate(-90 8 454.0)" text-anchor="middle">Betriebs-Loop</text> <text x="2" y="454.0" font-size="11" fill="#d23" transform="rotate(-90 8 454.0)" text-anchor="middle">Betriebs-Loop</text>
<line x1="782" y1="643.0" x2="846" y2="643.0" stroke="#8E63B5" stroke-width="2.6" marker-end="url(#ah)"/> <line x1="684" y1="643.0" x2="748" y2="643.0" stroke="#8E63B5" stroke-width="2.6" marker-end="url(#ah)"/>
<text x="854" y="637.0" font-size="12.5" font-weight="700" fill="#8E63B5">zurueck in DPM</text> <text x="756" y="637.0" font-size="12.5" font-weight="700" fill="#8E63B5">zurueck in DPM</text>
<text x="854" y="655.0" font-size="11" fill="#666">rv_05 Redesign / rv_06 Retirement</text> <text x="756" y="655.0" font-size="11" fill="#666">rv_05 Redesign / rv_06 Retirement</text>
<circle cx="43" cy="791" r="10" fill="#d23"/> <circle cx="43" cy="791" r="10" fill="#d23"/>
<text x="60" y="796" font-size="12.5" fill="#333">Gate-Puck (rot, Etikett G1/G2/G3 + Icon)</text> <text x="60" y="796" font-size="12.5" fill="#333">Gate-Puck (rot, Etikett G1/G2/G3 + Icon)</text>
<circle cx="373" cy="791" r="10" fill="#eaf2f9" stroke="#2F80C9" stroke-width="2"/> <circle cx="373" cy="791" r="10" fill="#eaf2f9" stroke="#2F80C9" stroke-width="2"/>

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Before After
Before After

View file

@ -52,13 +52,12 @@ PHASES = [
("sp_11", "RCA & Workaround", False), ("sp_11", "RCA & Workaround", False),
]), ]),
("REVIEW", "#8E63B5", [ ("REVIEW", "#8E63B5", [
("rv_01", "Taktische RCA + KPIs", False), ("rv_01", "Service-Reviews durchf.", False),
("rv_02", "Performance & Improvement", False), ("rv_02", "Bewertung d. Ergebnisse", False),
("rv_03", "SOR Periodischer Review", False), ("rv_03", "Aenderungen definieren", False),
("rv_04", "Service Improvement", False), ("rv_04", "Aenderungen starten", False),
("rv_05", "Redesign / Erweiterung", False), ("rv_05", "Aenderungen umsetzen", False),
("rv_06", "Ausserbetriebnahme", False), ]), # Arbeitsstand Frank (Change-Enablement); nicht im YAML/Konzept
]),
] ]
# Layout-Parameter # Layout-Parameter
@ -132,9 +131,9 @@ svg.append('<defs><marker id="ah" markerWidth="9" markerHeight="9" refX="7" refY
'<path d="M0,0 L7,3 L0,6 Z" fill="#666"/></marker></defs>') '<path d="M0,0 L7,3 L0,6 Z" fill="#666"/></marker></defs>')
# Titel # Titel
svg.append(f'<text x="30" y="44" font-size="26" font-weight="800" fill="#1a1a1a">' svg.append(f'<text x="30" y="44" font-size="26" font-weight="800" fill="#1a1a1a">'
f'Service-Lifecycle — Board-Layout (40 Pucks)</text>') f'Service-Lifecycle — Board-Layout (39 Pucks)</text>')
svg.append(f'<text x="30" y="68" font-size="14" fill="#555">' svg.append(f'<text x="30" y="68" font-size="14" fill="#555">'
f'37 Aktivitaeten + 3 Gate-Pucks · 1 Puck = &#216;{TILE_MM} mm · ' f'36 Aktivitaeten + 3 Gate-Pucks · 1 Puck = &#216;{TILE_MM} mm · '
f'lose Bahn, Sequenz links nach rechts</text>') f'lose Bahn, Sequenz links nach rechts</text>')
row_y = {} row_y = {}

View file

@ -32,7 +32,7 @@ Stand: 2026-06-04 · Maße in mm · Mengen für **ein** Workshop-Set.
| Figurenplätze | **7 Mulden** Ø 22 × 1,5 mm (Einführ-Fase), auf Kreis r 33 (Ø 66) | | Figurenplätze | **7 Mulden** Ø 22 × 1,5 mm (Einführ-Fase), auf Kreis r 33 (Ø 66) |
| Etikettenmulde | mittig Ø 37,5 × 0,3 mm (Rundetikett Ø37; Puck-Rand bleibt sichtbar) | | Etikettenmulde | mittig Ø 37,5 × 0,3 mm (Rundetikett Ø37; Puck-Rand bleibt sichtbar) |
| Farbe | je Phase (blau/orange/grün/teal/lila) | | Farbe | je Phase (blau/orange/grün/teal/lila) |
| Menge | **37** (eine je Aktivität) | | Menge | **36** (eine je Aktivität) — Druck von 37 empfohlen: 1 Blank als Reserve |
| Material | PLA, Infill 15 % | | Material | PLA, Infill 15 % |
| Druckzeit | ~1,3 h/Puck | | Druckzeit | ~1,3 h/Puck |
| Datei | `openscad/puck.scad` | | Datei | `openscad/puck.scad` |
@ -45,11 +45,16 @@ Stand: 2026-06-04 · Maße in mm · Mengen für **ein** Workshop-Set.
| Transition | orange | tr_02tr_08, tr_10, tr_11 | 9 | | Transition | orange | tr_02tr_08, tr_10, tr_11 | 9 |
| Operation | grün | op_01op_07 | 7 | | Operation | grün | op_01op_07 | 7 |
| Support | teal | sp_01sp_11 | 11 | | Support | teal | sp_01sp_11 | 11 |
| Review | lila | rv_01rv_06 | 6 | | Review | lila | rv_01rv_05 | 5 |
| | | **Σ Station-Pucks** | **37** | | | | **Σ Station-Pucks** | **36** |
Die 3 Gates (tr_01, tr_09, tr_12) sind **Gate-Pucks** (gleiches Teil, rot, §2) → 3 Stück. Die 3 Gates (tr_01, tr_09, tr_12) sind **Gate-Pucks** (gleiches Teil, rot, §2) → 3 Stück.
**Bahn gesamt = 37 + 3 = 40 Positionen.** **Bahn gesamt = 36 + 3 = 39 Positionen.**
> **Arbeitsstand Review (Workshop):** Die Review-Phase nutzt **5 Aktivitäten** nach
> Frank (Change-Enablement) statt bisher 6 — daher 36 statt 37 Station-Pucks. Details
> + offene Punkte: [`../00_Konzept/review-phase_arbeitsstand-frank.md`](../00_Konzept/review-phase_arbeitsstand-frank.md).
> Noch **nicht** im Blueprint-YAML / kanonischen Konzept (Michael-Freigabe ausstehend).
Die Pucks werden zu einer **linearen Bahn** ausgelegt (kein Ring), bei Platzmangel Die Pucks werden zu einer **linearen Bahn** ausgelegt (kein Ring), bei Platzmangel
mäandrierend. Als optische Linie + Halt empfiehlt sich eine **flache Unterlage/Matte** mäandrierend. Als optische Linie + Halt empfiehlt sich eine **flache Unterlage/Matte**
@ -151,19 +156,19 @@ linearen Puck-Bahn — das **Design-Segment ist der Start** vor dem ersten Puck.
| Teil | Menge | Datei | | Teil | Menge | Datei |
|------|------:|-------| |------|------:|-------|
| Station-Puck | 37 | `openscad/puck.scad` (Phasenfarbe) | | Station-Puck | 36 (+1 Reserve) | `openscad/puck.scad` (Phasenfarbe) |
| Gate-Puck | 3 | `openscad/puck.scad` (rot) | | Gate-Puck | 3 | `openscad/puck.scad` (rot) |
| Phasen-Ring-Segment | 5 | `openscad/phasen-ring-<phase>.scad` (je Phasenfarbe) | | Phasen-Ring-Segment | 5 | `openscad/phasen-ring-<phase>.scad` (je Phasenfarbe) |
| Aktiv-Feld (RACI-Fläche) | 1 | `openscad/aktiv-feld.scad` | | Aktiv-Feld (RACI-Fläche) | 1 | `openscad/aktiv-feld.scad` |
| Rollen-Figuren (Einzel) | 32 | 16 Rollen × 2 | | Rollen-Figuren (Einzel) | 32 | 16 Rollen × 2 |
| Team-Sonderfiguren | 6 | 3 Teams × 2 | | Team-Sonderfiguren | 6 | 3 Teams × 2 |
| Entscheidungs-Chips | 12 | (einfache Münze + Gravur) | | Entscheidungs-Chips | 12 | (einfache Münze + Gravur) |
| Rundetiketten Ø37 | 40 | via Generator aus YAML (ID + Phasenfarbe); ablösbar bevorzugt | | Rundetiketten Ø37 | 39 | via Generator aus YAML (ID + Phasenfarbe); ablösbar bevorzugt |
## Kritische Maße & Passungen (bitte prüfen) ## Kritische Maße & Passungen (bitte prüfen)
- **Puck = einziges Bahn-Teil** (Ø 100 × 6). Station vs. Gate nur über Filamentfarbe - **Puck = einziges Bahn-Teil** (Ø 100 × 6). Station vs. Gate nur über Filamentfarbe
+ Etikett — **ein STL, 40× drucken** (37 Phasen + 3 rot). + Etikett — **ein STL, 39× drucken** (36 Phasen + 3 rot; +1 Blank als Reserve).
- **Figuren-Mulde Ø 22, Tiefe 1,5** für Sockel **Ø 20** (reinstellbar, Fase als - **Figuren-Mulde Ø 22, Tiefe 1,5** für Sockel **Ø 20** (reinstellbar, Fase als
Einführhilfe). 7 Mulden auf Kreis r 33. Einführhilfe). 7 Mulden auf Kreis r 33.
- **Etikettenmulde Ø 37,5 × 0,3** — Rundetikett Ø37 (Puck-Rand bleibt sichtbar, - **Etikettenmulde Ø 37,5 × 0,3** — Rundetikett Ø37 (Puck-Rand bleibt sichtbar,

View file

@ -6,7 +6,9 @@
> (offline-/kioskfähig). Sie führt den kompletten Flow durch (Action Card → > (offline-/kioskfähig). Sie führt den kompletten Flow durch (Action Card →
> Startpunkt → optionale Tour → Station: Diskussion/Quiz/Auflösung → Debrief mit > Startpunkt → optionale Tour → Station: Diskussion/Quiz/Auflösung → Debrief mit
> **Markdown-/JSON-Export**). Inhalte (40 Stationen, 45 Quizfragen, 6 Use-Cases) > **Markdown-/JSON-Export**). Inhalte (40 Stationen, 45 Quizfragen, 6 Use-Cases)
> sind derzeit in `app/index.html` eingebettet. **Deployment:** statisch, siehe > sind derzeit in `app/index.html` eingebettet. Die **finalen Action-Card-Grafiken**
> (Freiburg-digital-Layout) liegen in `app/cards/` (`s<service>-c<change>.png`, 29/30 —
> `s0-c0` „Open Source von oben!" fehlt noch → Text-Fallback). **Deployment:** statisch, siehe
> [`app/DEPLOY.md`](app/DEPLOY.md). **Lokal testen:** `python -m http.server 8099 > [`app/DEPLOY.md`](app/DEPLOY.md). **Lokal testen:** `python -m http.server 8099
> --directory 04_Tablet-Quiz/app` (oder Preview-Config `.claude/launch.json`). > --directory 04_Tablet-Quiz/app` (oder Preview-Config `.claude/launch.json`).

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

View file

@ -245,12 +245,11 @@ const TOUR = {
sp_09: "Ein Incident bleibt ungelöst eine strukturelle Ursache wird vermutet. Der Problem Manager legt einen Problem Record an.", sp_09: "Ein Incident bleibt ungelöst eine strukturelle Ursache wird vermutet. Der Problem Manager legt einen Problem Record an.",
sp_10: "Mehrere ähnliche Speicher-Fehler häufen sich → sie werden geclustert und gebündelt als Problem Record erfasst.", sp_10: "Mehrere ähnliche Speicher-Fehler häufen sich → sie werden geclustert und gebündelt als Problem Record erfasst.",
sp_11: "Ursachensuche: Das Speicherproblem entsteht durch einen Timeout unter Last. Es gibt einen Workaround (Eintrag in die Wissensdatenbank) und die Entscheidung, dass ein Change nötig ist.", sp_11: "Ursachensuche: Das Speicherproblem entsteht durch einen Timeout unter Last. Es gibt einen Workaround (Eintrag in die Wissensdatenbank) und die Entscheidung, dass ein Change nötig ist.",
rv_01: "Im Review werden Support-KPIs und wiederkehrende Fälle ausgewertet die Speicher-Probleme tauchen klar als Muster auf.", rv_01: "Im Service-Review werden die Betriebsdaten des Beteiligungs-Moduls ausgewertet: KPIs, die gehäuften Speicher-Incidents, Kundenfeedback und die Infrastruktur — alles fließt ins Service-Review-Dokument.",
rv_02: "Der Service wird über vier Dimensionen per Ampel bewertet (Leistung, Stabilität, Nutzerzufriedenheit, Zukunftsfähigkeit). Empfehlung in unserem Fall: IMPROVEMENT.", rv_02: "Die Ergebnisse werden bewertet: Der wiederkehrende Speicher-Fehler rechtfertigt einen RFC. Der Bericht geht an die SOR, die ganzheitlich bewertet.",
rv_03: "Die SOR sichtet das Review und entscheidet. In unserem Beispiel: VERBESSERUNG NÖTIG (klein) → rv_04. (Alternativen wären: weiter wie bisher, Redesign rv_05 oder Außerbetriebnahme rv_06.)", rv_03: "Auf Basis der Bewertung werden Änderungsvorschläge formuliert (z. B. Last-/Timeout-Optimierung), konsolidiert und die ausgewählte Änderung beschrieben.",
rv_04: "Die kleine Verbesserung wird geplant und umgesetzt z. B. die Last- und Timeout-Optimierung beim Speichern. Sie fließt zurück in den laufenden Betrieb (Operation). Damit schließt sich der Kreis.", rv_04: "Die Änderung wird gestartet. Es ist eine überschaubare Anpassung → Normal-Change-Logik: Umsetzung planen. (Wäre sie grundlegend, würde hier das Routing RUN/DPM/MB geklärt und ein Change-Steckbrief erstellt.)",
rv_05: "In unserem Beispiel NICHT gewählt. Wäre die Änderung grundlegend (z. B. eine komplett neue Beteiligungsplattform), würde sie hier als neuer Demand an den Demand-Lifecycle übergeben der Service liefe quasi neu durch Design und Transition.", rv_05: "Die Änderung wird umgesetzt: Im Weg RUN führt der Service Owner sie durch, dokumentiert und schließt sie ab. Der Service läuft verbessert weiter — der Kreis schließt sich."
rv_06: "Ebenfalls NICHT gewählt. Würde das Modul z. B. wegen eines Nachfolgesystems abgeschaltet, plant die SOR hier die kontrollierte Stilllegung (Retirement-Plan)."
} }
}; };
@ -270,7 +269,7 @@ const ROLLEN = {
projektteam:"Projektteam", queue_koordinator:"Queue Koordinator", projektteam:"Projektteam", queue_koordinator:"Queue Koordinator",
first_level_agent:"1st Level Agent", second_level_agent:"2nd Level Agent", first_level_agent:"1st Level Agent", second_level_agent:"2nd Level Agent",
testmanagement:"Testmanagement", architektur:"Architektur", testmanagement:"Testmanagement", architektur:"Architektur",
lieferant:"Lieferant", operations_manager:"Betrieb (AL B&C / AL App)" lieferant:"Lieferant", operations_manager:"Betrieb (AL B&C / AL App)", dpm:"Demand Portfolio Manager"
}; };
const PHASEN = { const PHASEN = {
design:{label:"Design", color:"var(--design)"}, design:{label:"Design", color:"var(--design)"},
@ -291,56 +290,56 @@ const USE_CASES = [
{ service:"Zentrale VDI (Virtual-Desktop-Infrastructure)", { service:"Zentrale VDI (Virtual-Desktop-Infrastructure)",
desc:"Bereitstellung von virtuellen Windows-Desktops über das interne Rechenzentrum.", desc:"Bereitstellung von virtuellen Windows-Desktops über das interne Rechenzentrum.",
changes:[ changes:[
"Strategische IT-Richtlinie auf Geheiß des OB, die den Umstieg von einer proprietären VDI-Lösung auf eine Open-Source-Alternative (z. B. OpenStack + Thin-Client) vorschreibt.", {titel:"Open Source von oben!", text:"Der OB gibt die Richtung vor: Die proprietäre VDI-Lösung soll auf eine Open-Source-Alternative (OpenStack + Thin-Client) umgestellt werden."},
"Das Bauamt fordert die Einführung einer neuen Fachsoftware (z. B. ein GIS-Tool), die auf dem virtuellen Desktop zugänglich sein soll.", {titel:"Bauamt will mehr!", text:"Das Bauamt fordert ein neues GIS-Fachverfahren, das künftig direkt auf dem virtuellen Desktop laufen soll."},
"Rebranding des Logos der Stadt Freiburg: Anpassung der Desktop-Hintergrundgrafik erforderlich.", {titel:"Tapetenwechsel", text:"Die Stadt bekommt ein neues Logo — der Desktop-Hintergrund aller virtuellen Arbeitsplätze muss angepasst werden."},
"Quartalsweises Update der VDI-Host-Hypervisor-Firmware im Standard-Change-Katalog verankert.", {titel:"Quartalspflege", text:"Das turnusmäßige Firmware-Update der VDI-Host-Hypervisoren steht an — im Standard-Change-Katalog längst hinterlegt."},
"Stromausfall: Ausfall eines VDI-Host-Clusters, der die sofortige Migration der Sitzungen auf ein Backup-Cluster erfordert." {titel:"Blackout!", text:"Ein Stromausfall reißt ein ganzes VDI-Host-Cluster aus dem Betrieb — die Sitzungen müssen sofort auf ein Backup-Cluster migriert werden."}
]}, ]},
{ service:"Managed VPN-Access Service", { service:"Managed VPN-Access Service",
desc:"Zentral verwalteter VPN-Dienst, der Mitarbeitenden der Fachämter sicheren Remote-Zugriff auf interne Systeme (Intranet, Fachanwendungen, Datenbanken) ermöglicht.", desc:"Zentral verwalteter VPN-Dienst, der Mitarbeitenden der Fachämter sicheren Remote-Zugriff auf interne Systeme (Intranet, Fachanwendungen, Datenbanken) ermöglicht.",
changes:[ changes:[
"EU-weite neue Datenschutz- oder IT-Sicherheitsverordnung, die die gesamte VPN-Architektur neu gestaltet.", {titel:"Brüssel ruft!", text:"Eine neue EU-weite IT-Sicherheitsverordnung zwingt dazu, die gesamte VPN-Architektur neu aufzustellen."},
"HPA fordert die Einführung einer neuen Authentifizierungsmethode (z. B. verpflichtende Nutzung von FIDO2-Security-Keys).", {titel:"Schlüsselpflicht", text:"Das Hauptpersonalamt verlangt eine neue Authentifizierung: FIDO2-Security-Keys werden für alle verpflichtend."},
"HPA möchte eine interne Anwendung (z. B. ein neues Intranet-Portal) in die Split-Tunnel-Liste ergänzen, weil Nutzer nun von zu Hause darauf zugreifen müssen.", {titel:"Heimvorteil", text:"Ein neues Intranet-Portal soll in die Split-Tunnel-Liste, damit Mitarbeitende auch aus dem Homeoffice darauf zugreifen."},
"Regelmäßige (monatliche) Firmware-Updates der VPN-Appliance, bereits im Change-Katalog als Standard-Change definiert.", {titel:"Monatsroutine", text:"Das monatliche Firmware-Update der VPN-Appliance steht an — als Standard-Change bereits freigegeben."},
"Erfolgreicher Phishing-Angriff: Eine VPN-Benutzerzertifikatskette ist kompromittiert sofortige Sperrung und Neu-Ausstellung der Zertifikate erforderlich." {titel:"Gephisht!", text:"Ein erfolgreicher Phishing-Angriff hat eine VPN-Zertifikatskette kompromittiert — sofort sperren und neu ausstellen."}
]}, ]},
{ service:"Online-Bürgerportal für Meldungen & Anträge", { service:"Online-Bürgerportal für Meldungen & Anträge",
desc:"Zentrales Web-Portal, über das Bürger*innen Meldungen (z. B. Straßenreparatur, Lärm) und Anträge (z. B. Baugenehmigung, Personalausweis) digital einreichen und den Status verfolgen.", desc:"Zentrales Web-Portal, über das Bürger*innen Meldungen (z. B. Straßenreparatur, Lärm) und Anträge (z. B. Baugenehmigung, Personalausweis) digital einreichen und den Status verfolgen.",
changes:[ changes:[
"Neues Landesgesetz zur digitalen Bürgerbeteiligung (z. B. verpflichtende Online-Beteiligungs-Tools für größere Baumaßnahmen) → komplette Erweiterung des Portals um Beteiligungs-Module.", {titel:"Mitreden, Pflicht!", text:"Ein neues Landesgesetz schreibt digitale Bürgerbeteiligung vor — das Portal muss um komplette Beteiligungs-Module erweitert werden."},
"Neuer Gemeinderat fordert Einführung eines neuen Meldetyps (z. B. „Klimaschutz-Meldungen“) durch das Umweltamt neue Formulare und Workflows erforderlich.", {titel:"Ratsbeschluss!", text:"Der Gemeinderat will einen neuen Meldetyp „Klimaschutz“ — das Umweltamt braucht dafür eigene Formulare und Workflows."},
"Bürgerservice meldet Rechtschreibfehler in einem statischen Hinweis-Text, der geändert werden muss.", {titel:"Rotstift gefragt", text:"Der Bürgerservice meldet einen Rechtschreibfehler in einem statischen Hinweistext, der korrigiert werden muss."},
"Monatliches Sicherheits-Patch-Update des Web-Servers (Apache/Nginx) bereits im Change-Katalog definiert.", {titel:"Patchday", text:"Das monatliche Sicherheits-Patch des Webservers (Apache/Nginx) steht an — im Change-Katalog definiert."},
"Entdeckung einer kritischen XSS-Lücke in einem Eingabe-Formular, die eine sofortige Hot-Fix-Implementierung nötig macht." {titel:"Lücke im Formular!", text:"In einem Eingabe-Formular wird eine kritische XSS-Schwachstelle entdeckt — ein Hotfix muss sofort raus."}
]}, ]},
{ service:"Zentrales Dokumenten-Management-System (DMS)", { service:"Zentrales Dokumenten-Management-System (DMS)",
desc:"Elektronisches Archiv, das alle ein- und ausgehenden Dokumente (PDF, Scans, E-Mails) zentral speichert, versioniert und revisionssicher archiviert.", desc:"Elektronisches Archiv, das alle ein- und ausgehenden Dokumente (PDF, Scans, E-Mails) zentral speichert, versioniert und revisionssicher archiviert.",
changes:[ changes:[
"EU-DSGVO-Ergänzung (z. B. neue Vorgaben zur Daten-Minimierung) → komplette Neugestaltung des Metadaten-Modells und der Archivierungs-Policies.", {titel:"Datendiät", text:"Eine DSGVO-Ergänzung zur Datenminimierung erzwingt die komplette Neugestaltung von Metadaten-Modell und Archivierungs-Policies."},
"Einführung eines neuen Fachverfahrens (z. B. „Bürgerbeteiligungs-Verfahren“), das eigene Dokumenten-Klassen und Workflows erfordert.", {titel:"Akten-Zuwachs", text:"Ein neues Fachverfahren zieht ein und braucht eigene Dokumentenklassen und Workflows im DMS."},
"Rückmeldung aus dem Finanzamt: Ein Metadaten-Feld (z. B. neues Dropdown-Feld „Kostenstelle“) muss angepasst werden.", {titel:"Dropdown-Wunsch", text:"Das Finanzamt bittet um ein neues Metadaten-Feld „Kostenstelle“ als Auswahlliste."},
"Quartalsweises Update der DMS-Software-Version (Sicherheits- und Funktions-Patches) im Standard-Change-Katalog.", {titel:"Versionspflege", text:"Das quartalsweise Update der DMS-Software (Sicherheits- und Funktions-Patches) steht an."},
"Ransomware-Alarm: Verdächtige Verschlüsselung von Dokumenten sofortige Isolation des Storage-Systems und Wiederherstellung aus letzten Snapshots." {titel:"Ransomware!", text:"Alarm: Dokumente werden verdächtig verschlüsselt — Storage sofort isolieren und aus den letzten Snapshots wiederherstellen."}
]}, ]},
{ service:"Kommunales GIS-Portal (Geoinformations-System)", { service:"Kommunales GIS-Portal (Geoinformations-System)",
desc:"Web-basiertes Karten- und Analyse-Portal, das Fachämtern (Bau, Umwelt, Verkehr) räumliche Daten (Flurstücke, Infrastruktur, Umweltzonen) und bearbeitbare Layer bereitstellt.", desc:"Web-basiertes Karten- und Analyse-Portal, das Fachämtern (Bau, Umwelt, Verkehr) räumliche Daten (Flurstücke, Infrastruktur, Umweltzonen) und bearbeitbare Layer bereitstellt.",
changes:[ changes:[
"Bundesweite Vorgabe zur Nutzung von EU-Standards → komplette Migration des GIS-Stacks zu konformen Services und Datenmodellen.", {titel:"Norm-Zwang", text:"Eine bundesweite Vorgabe zu EU-Standards erzwingt die komplette Migration des GIS-Stacks auf konforme Services und Datenmodelle."},
"Das Umweltamt möchte ein neues Analyse-Modul (z. B. „Klimarisiko-Analyse“) einführen, das neue Daten-Layer und Rechen-Ressourcen erfordert.", {titel:"Klimarisiko im Blick", text:"Das Umweltamt will ein neues Analyse-Modul „Klimarisiko“ — mit neuen Daten-Layern und Rechen-Ressourcen."},
"Rückmeldung aus dem Bauamt: Korrektur einer falschen Beschriftung eines Karten-Layers.", {titel:"Falsch beschriftet", text:"Das Bauamt meldet eine falsche Beschriftung eines Karten-Layers, die korrigiert werden muss."},
"Monatliches Update der GIS-Software (z. B. GeoServer 2.23 → 2.24) im Standard-Change-Katalog definiert.", {titel:"GeoServer-Update", text:"Das monatliche Update der GIS-Software (GeoServer 2.23 → 2.24) steht an — im Standard-Change-Katalog."},
"Entdeckung einer kritischen Schwachstelle an einer Schnittstelle, die unautorisierten Datenzugriff ermöglicht → sofortige Deaktivierung des Dienstes und Patch-Roll-out." {titel:"Schnittstelle offen!", text:"An einer Schnittstelle wird eine kritische Schwachstelle entdeckt, die unautorisierten Datenzugriff erlaubt — Dienst sofort abschalten und patchen."}
]}, ]},
{ service:"Beschaffungs- und Vertrags-System für Fachämter", { service:"Beschaffungs- und Vertrags-System für Fachämter",
desc:"Web-Applikation, über die Fachämter interne Beschaffungen (Material, Dienstleistungen) anlegen, prüfen und Verträge digital verwalten.", desc:"Web-Applikation, über die Fachämter interne Beschaffungen (Material, Dienstleistungen) anlegen, prüfen und Verträge digital verwalten.",
changes:[ changes:[
"Neue EU-Vergaberichtlinie zwingt zur Einführung von e-Invoicing und erweiterten Transparenz-Reports.", {titel:"Vergabe neu!", text:"Eine neue EU-Vergaberichtlinie zwingt zur Einführung von E-Invoicing und erweiterten Transparenz-Reports."},
"Einführung eines neuen Lieferanten-Portals (z. B. für Badenova), das neue API-Schnittstellen und Authentifizierungs-Flows erfordert.", {titel:"Portal für Partner", text:"Ein neues Lieferanten-Portal (z. B. für Badenova) soll andocken — mit neuen API-Schnittstellen und Authentifizierungs-Flows."},
"Rückmeldung aus dem Finanzamt: Anpassung eines Formular-Labels (z. B. „Kostenstelle“ → „Kostenstelle (4-stellig)“).", {titel:"Vierstellig, bitte", text:"Das Finanzamt wünscht eine kleine Anpassung: aus dem Label „Kostenstelle“ wird „Kostenstelle (4-stellig)“."},
"Quartalsweises Sicherheits-Patch-Update des Anwendungs-Servers bereits im Change-Katalog.", {titel:"Patch-Quartal", text:"Das quartalsweise Sicherheits-Patch des Anwendungsservers steht an — bereits im Change-Katalog."},
"Entdeckung einer kritischen Schwachstelle im Vertrags-Upload-Modul, die das Hochladen von Schadcode ermöglicht → sofortige Deaktivierung des Upload-Endpoints und Hot-Fix." {titel:"Upload-Falle!", text:"Im Vertrags-Upload wird eine kritische Lücke entdeckt, über die sich Schadcode hochladen lässt — Endpoint sofort sperren, Hotfix einspielen."}
]} ]}
]; ];
@ -759,80 +758,67 @@ const STATIONEN = [
optionen:["Ein Workaround","Ein Betriebshandbuch","Eine Gate-Vorlage","Ein Review-Bericht"], richtig:0, optionen:["Ein Workaround","Ein Betriebshandbuch","Eine Gate-Vorlage","Ein Review-Bericht"], richtig:0,
expl:"sp_11 ermittelt die Ursache, erstellt einen Workaround und entscheidet über Change-Bedarf (aktualisiert den Problem Record)."} expl:"sp_11 ermittelt die Ursache, erstellt einen Workaround und entscheidet über Change-Bedarf (aktualisiert den Problem Record)."}
]}, ]},
/* Review-Phase = ARBEITSSTAND (Vorschlag Frank, Change-Enablement) — noch NICHT
im Blueprint-YAML; vor Konzept-Uebernahme mit Michael abstimmen.
RACI + Quiz hier abgeleitet (Franks Entwurf nennt nur die Aktivitaeten). */
{ id:"rv_01", phase:"review", typ:"aktivitaet", { id:"rv_01", phase:"review", typ:"aktivitaet",
name:"Taktische Root-Cause-Analyse + Auswertung Support-KPIs", name:"Durchführen von Service-Reviews",
beschreibung:"Strukturelle Analyse wiederkehrender Supportfälle und KPIs, um mittelfristige Verbesserungen anzustoßen.", beschreibung:"Den Service systematisch auswerten und die Ergebnisse im Service-Review-Dokument festhalten.",
umfasst:["KPI-Auswertung (Supportqualität, Trends)","Wiederkehrende Tickets und systemische Ursachen","Abgleich mit Problem Records","Ableitung taktischer Verbesserungsmaßnahmen"], umfasst:["KPIs & Monitoring auswerten","Problems & Incidents auswerten","Kundenfeedback sammeln/einholen","zugrunde liegende Infrastruktur bewerten","Service-Review-Dokument ausfüllen"],
artefakt:"Taktische Verbesserungsmaßnahmen", artefakt:"Service-Review-Dokument",
raci:[["service_owner","A"],["problem_manager","R"]], raci:[["service_owner","A"],["betriebsteam","R"],["service_support_team","C"],["problem_manager","C"]],
quiz:[ quiz:[
{frage:"Wer führt die taktische Root-Cause-Analyse durch (R)?", {frage:"Welches Dokument entsteht beim Service-Review?",
optionen:["Problem Manager","Service Owner","SOR","1st Level Agent"], richtig:0, optionen:["RFC","Service-Review-Dokument","Change-Steckbrief","Incident Record"], richtig:1,
expl:"Der Problem Manager führt die taktische Analyse durch; der Service Owner priorisiert und bestätigt Maßnahmen (A)."} expl:"KPIs, Incidents, Feedback und Infrastrukturbewertung werden im Service-Review-Dokument zusammengeführt."}
]}, ]},
{ id:"rv_02", phase:"review", typ:"aktivitaet", { id:"rv_02", phase:"review", typ:"aktivitaet",
name:"Service Performance & Improvement Review", name:"Bewertung der Review-Ergebnisse",
beschreibung:"Operativer Review des Servicezustands anhand Qualitätsberichten, Supportdaten und Problem-Analysen. Methodik: 4-Dimensionen-Modell mit Ampelbewertung.", beschreibung:"Die Review-Ergebnisse bewerten und bei Änderungsbedarf einen RFC erstellen; relevante Berichte gehen an die SOR.",
umfasst:["Leistung gegen Ziele und SLAs abgleichen","Gesamterfüllungsgrad bewerten","Operative Risiken, Störungen, Engpässe diskutieren","Optimierungsmaßnahmen identifizieren","Vorbereitung für SOR-Review","Handlungsempfehlung: CONTINUE / IMPROVEMENT / REDESIGN / RETIRE"], umfasst:["RFC für Normal- bzw. Major-Change erstellen","Berichte bei Bedarf an die SOR weiterleiten","Ergebnisse in der SOR ganzheitlich bewerten"],
artefakt:"Service Performance & Improvement Review (Ampelbewertung)", artefakt:"RFC (Request for Change)",
raci:[["service_owner","A/R"],["spm","I"]],
quiz:[
{frage:"Welche Bewertungsskala nutzt das Service Performance & Improvement Review?",
optionen:["110 Punkte","Grün / Gelb / Rot","Pass / Fail","A / B / C"], richtig:1,
expl:"Das 4-Dimensionen-Modell (Leistung, Stabilität, Zufriedenheit, Zukunftsfähigkeit) wird per Ampel (Grün/Gelb/Rot) bewertet."}
]},
{ id:"rv_03", phase:"review", typ:"aktivitaet",
name:"SOR: Periodischer Service Review",
beschreibung:"Die SOR bewertet regelmäßig die Serviceleistung und trifft operative Entscheidungen. Verzweigt in vier Wege.",
umfasst:["Sichtung des Service Performance & Improvement Reviews","Bewertung von Stabilität & Betriebsreife","Priorisierung operativer Verbesserungen","Freigabe kleinerer Maßnahmen","Schnittstelle zur Demand-/Portfolio-Steuerung"],
artefakt:"SOR-Entscheidung (Review)",
pfade:[
["Weiter wie bisher","Service stabil, keine Maßnahmen → Operation"],
["Verbesserung nötig (klein)","→ rv_04 Service Improvement"],
["Strukturelle Überarbeitung","→ rv_05 Redesign / Erweiterung"],
["Service End-of-Life","→ rv_06 Außerbetriebnahme"]
],
raci:[["sor","A"],["service_owner","R"]],
quiz:[
{frage:"Wer trifft im periodischen Service Review die Entscheidung (A)?",
optionen:["Service Owner","SOR","SPM","Problem Manager"], richtig:1,
expl:"Die SOR entscheidet; der Service Owner berichtet und präsentiert (R)."},
{frage:"Welche Wege kann rv_03 eröffnen?",
optionen:["Nur Weiter oder Stop","Weiter / Improvement / Redesign / Retirement","Go / No-Go","Intern / Extern"], richtig:1,
expl:"Weiter wie bisher, kleine Verbesserung (rv_04), Redesign (rv_05) oder Außerbetriebnahme (rv_06)."}
]},
{ id:"rv_04", phase:"review", typ:"aktivitaet",
name:"Service Improvement",
beschreibung:"SOR-Entscheidung: Planung und Steuerung kleinerer Verbesserungsmaßnahmen innerhalb des Service.",
umfasst:["Probleme in Verbesserungs-Initiativen umwandeln","Aufwandsschätzung und Priorisierung","Zielmetriken festlegen","Verantwortlichkeiten zuweisen","Sichtbarkeit im Service-Portfolio"],
artefakt:"Verbesserungs-Initiative (zurück in Operation)",
raci:[["service_owner","A/R"],["spm","C"],["problem_manager","C"]],
quiz:[
{frage:"Wohin fließen die Verbesserungsmaßnahmen aus rv_04 zurück?",
optionen:["In die Operation (laufender Betrieb)","In die Design-Phase","In den Demand-Lifecycle","An Gate 1"], richtig:0,
expl:"Service Improvement setzt kleinere Maßnahmen im laufenden Betrieb (Operation) um."}
]},
{ id:"rv_05", phase:"review", typ:"aktivitaet",
name:"Service Redesign / Erweiterung",
beschreibung:"SOR-Entscheidung: strukturelle Weiterentwicklung des Service. Übergabe an den Demand-Lifecycle.",
umfasst:["Überarbeitung der Service-Definitionen","Anpassung von Komponenten, Prozessen, Rollen","Anforderungen für Entwicklungsprozess identifizieren","Übergabe an Demand-Lifecycle-Prozess"],
artefakt:"Neuer Demand (Redesign) → DPM",
raci:[["service_owner","A"],["spm","C"]],
quiz:[
{frage:"Wohin übergibt rv_05 (Redesign/Erweiterung)?",
optionen:["An die Operation","An den Demand-Lifecycle (DPM) als neuer Demand","An den Support","An Gate 2"], richtig:1,
expl:"Strukturelle Änderungen werden als neuer Demand erfasst und durchlaufen den Demand-Lifecycle."}
]},
{ id:"rv_06", phase:"review", typ:"aktivitaet",
name:"Service außer Betrieb nehmen",
beschreibung:"Strukturierte Entscheidung und kontrollierte Abschaltung eines Services.",
umfasst:["Auslöser definieren (Nutzung, Kosten, Überalterung)","Bewertung durch SOR","Retirement-Plan erstellen (Kommunikation, Migration, Abschaltung)","Koordination mit Portfolio (SPM)","Übergabe an Projekt-/Transitionsthemen"],
artefakt:"Retirement-Plan / Decommissioning-Auftrag → DPM",
raci:[["sor","A"],["service_owner","R"],["spm","C"]], raci:[["sor","A"],["service_owner","R"],["spm","C"]],
quiz:[ quiz:[
{frage:"Wer entscheidet über die Außerbetriebnahme eines Services (A)?", {frage:"Was wird erstellt, wenn die Bewertung Änderungsbedarf zeigt?",
optionen:["Service Owner","SOR","SPM","Support Manager"], richtig:1, optionen:["Ein RFC (Request for Change)","Ein Incident Record","Ein Test-Report","Eine Service-Definition"], richtig:0,
expl:"Die SOR entscheidet über die Stilllegung; der Service Owner führt den Retirement-Plan aus (R)."} expl:"Bei Normal- oder Major-Change entsteht ein RFC; Berichte gehen an die SOR."},
{frage:"Wer bewertet die Review-Ergebnisse ganzheitlich?",
optionen:["Der 1st Level","Die SOR","Das Testmanagement","Der Lieferant"], richtig:1,
expl:"Die SOR bewertet die Ergebnisse als Gremium ganzheitlich."}
]},
{ id:"rv_03", phase:"review", typ:"aktivitaet",
name:"Definieren von Service-Änderungen",
beschreibung:"Auf Basis der Bewertung konkrete Änderungsvorschläge formulieren, konsolidieren und die ausgewählte Änderung beschreiben.",
umfasst:["passende Änderungsvorschläge formulieren","Vorschläge bewerten & konsolidieren","ausgewählte Änderung beschreiben"],
artefakt:"Beschriebene Service-Änderung",
raci:[["service_owner","A"],["sor","C"],["architektur","C"]],
quiz:[
{frage:"Was ist das Ziel dieser Aktivität?",
optionen:["Incidents schließen","Änderungsvorschläge formulieren, konsolidieren und beschreiben","Den Service abschalten","Das Budget planen"], richtig:1,
expl:"Aus der Bewertung werden konkrete, beschriebene Service-Änderungen abgeleitet."}
]},
{ id:"rv_04", phase:"review", typ:"aktivitaet",
name:"Starten von Service-Änderungen",
beschreibung:"Die Änderung anstoßen: bei Normal Change die Umsetzung planen; bei Major Change das Routing klären und den Change-Steckbrief ausfüllen.",
umfasst:["Normal Change: Umsetzung planen","Major Change: Routing klären (RUN / DPM / MB)","Major Change: Change-Steckbrief ausfüllen & weiterleiten"],
artefakt:"Change-Steckbrief (bei Major Change)",
raci:[["service_owner","A"],["sor","C"],["spm","C"],["dpm","I"]],
pfade:[["RUN","Durchführung im laufenden Betrieb (Service Owner)"],["DPM","über den Demand- & Projektprozess"],["MB","direkt in den Projektprozess oder RUN"]],
quiz:[
{frage:"Was muss beim Major Change vor der Umsetzung geklärt werden?",
optionen:["Das Routing: RUN, DPM oder MB","Die Ticket-Queue","Der Eskalationsweg","Die Lizenzkosten"], richtig:0,
expl:"Beim Major Change wird das Routing (RUN/DPM/MB) geklärt und ein Change-Steckbrief erstellt; beim Normal Change wird direkt die Umsetzung geplant."}
]},
{ id:"rv_05", phase:"review", typ:"aktivitaet",
name:"Implementieren von Service-Änderungen",
beschreibung:"Die Änderung gemäß gewähltem Weg umsetzen, dokumentieren und abschließen.",
umfasst:["Normal & Major (Weg RUN): SO führt durch, dokumentiert, schließt ab","Major (Weg DPM): Demand- & Projektprozess","Major (Weg MB): Projektprozess oder RUN"],
artefakt:"Umgesetzte & dokumentierte Service-Änderung",
raci:[["service_owner","A"],["projektteam","R"],["dpm","C"]],
quiz:[
{frage:"Wer führt eine Änderung auf dem Weg „RUN“ durch?",
optionen:["Der Service Owner","Der Demand Portfolio Manager","Ein externes Projektteam","Die SOR"], richtig:0,
expl:"Im Weg RUN führt der Service Owner die Änderung durch, dokumentiert und schließt sie ab."}
]} ]}
]; ];
@ -852,6 +838,18 @@ const $ = s => document.querySelector(s);
const cur = () => STATIONEN[S.index]; const cur = () => STATIONEN[S.index];
function pkey(sid, qi){ return sid+"#"+qi; } function pkey(sid, qi){ return sid+"#"+qi; }
function roleLabel(id){ return ROLLEN[id] || id; } function roleLabel(id){ return ROLLEN[id] || id; }
function acard(si, ci){ return USE_CASES[si].changes[ci]; } // {titel, text}
function cardHtml(si, ci){ const c = acard(si, ci);
return `<div style="font-weight:800;font-size:18px;color:var(--ink);margin-bottom:6px">${c.titel}</div>`
+ `<div>${c.text}</div>`
+ `<div style="color:var(--muted);font-style:italic;margin-top:8px">Was passiert an welchen Stellen?</div>`; }
// Finale Action-Card-Grafik (cards/s<service>-c<change>.png). Eine fehlt noch
// (s0-c0 "Open Source von oben!") -> Text-Fallback.
function cardImg(si, ci){ return (si===0 && ci===0) ? null : `cards/s${si}-c${ci}.png`; }
function cardMedia(si, ci){ const f = cardImg(si, ci);
return f ? `<img class="acImg" src="${f}" alt="Action Card: ${acard(si,ci).titel}"
style="display:block;width:100%;max-width:300px;margin:6px auto;border-radius:8px;box-shadow:0 2px 10px rgba(0,0,0,.12)">`
: cardHtml(si, ci); }
/* ====================== RENDER: SIDEBAR ====================== */ /* ====================== RENDER: SIDEBAR ====================== */
function renderList(){ function renderList(){
@ -906,7 +904,7 @@ function renderCardScreen(){
<label>Service<select id="serviceSel"></select></label> <label>Service<select id="serviceSel"></select></label>
<label>Change-Typ<select id="changeSel"></select></label> <label>Change-Typ<select id="changeSel"></select></label>
</div> </div>
<div class="ctText" id="cardTrigger">${USE_CASES[S.service].changes[S.change]}</div> <div class="ctText" id="cardTrigger">${cardMedia(S.service,S.change)}</div>
<div class="actions"> <div class="actions">
<button class="ghost" id="randomCard">🎲 Zufällig ziehen</button> <button class="ghost" id="randomCard">🎲 Zufällig ziehen</button>
<button class="ghost" id="tourBtn">📘 Geführtes Beispiel</button> <button class="ghost" id="tourBtn">📘 Geführtes Beispiel</button>
@ -918,7 +916,7 @@ function renderCardScreen(){
svc.innerHTML = USE_CASES.map((u,i)=>`<option value="${i}">${u.service}</option>`).join(""); svc.innerHTML = USE_CASES.map((u,i)=>`<option value="${i}">${u.service}</option>`).join("");
ch.innerHTML = CHANGE_TYPES.map((c,i)=>`<option value="${i}">${c}</option>`).join(""); ch.innerHTML = CHANGE_TYPES.map((c,i)=>`<option value="${i}">${c}</option>`).join("");
svc.value=S.service; ch.value=S.change; svc.value=S.service; ch.value=S.change;
const refresh=()=>{ $("#cardTrigger").textContent=USE_CASES[S.service].changes[S.change]; }; const refresh=()=>{ $("#cardTrigger").innerHTML=cardMedia(S.service,S.change); };
svc.onchange=()=>{ S.service=+svc.value; save(); refresh(); }; svc.onchange=()=>{ S.service=+svc.value; save(); refresh(); };
ch.onchange=()=>{ S.change=+ch.value; save(); refresh(); }; ch.onchange=()=>{ S.change=+ch.value; save(); refresh(); };
$("#randomCard").onclick=()=>{ S.service=Math.floor(Math.random()*USE_CASES.length); S.change=Math.floor(Math.random()*CHANGE_TYPES.length); save(); draw(); }; $("#randomCard").onclick=()=>{ S.service=Math.floor(Math.random()*USE_CASES.length); S.change=Math.floor(Math.random()*CHANGE_TYPES.length); save(); draw(); };
@ -943,7 +941,7 @@ function renderTour(){
const last = S.tourIndex === STATIONEN.length-1; const last = S.tourIndex === STATIONEN.length-1;
$("#panel").innerHTML = ` $("#panel").innerHTML = `
<div class="tourBanner">📘 Geführtes Beispiel · <b>${USE_CASES[TOUR.service].service}</b> — ${CHANGE_TYPES[TOUR.change]}<br> <div class="tourBanner">📘 Geführtes Beispiel · <b>${USE_CASES[TOUR.service].service}</b> — ${CHANGE_TYPES[TOUR.change]}<br>
<span style="color:var(--muted)">${USE_CASES[TOUR.service].changes[TOUR.change]}</span></div> <span style="color:var(--muted)"><b>${acard(TOUR.service,TOUR.change).titel}</b> — ${acard(TOUR.service,TOUR.change).text}</span></div>
<div class="tourProg">Station ${S.tourIndex+1} von ${STATIONEN.length}</div> <div class="tourProg">Station ${S.tourIndex+1} von ${STATIONEN.length}</div>
${chip} ${chip}
<div class="stationName">${st.name}</div> <div class="stationName">${st.name}</div>
@ -1046,7 +1044,7 @@ function renderRun(){
<div class="stationId">${st.id}</div> <div class="stationId">${st.id}</div>
<div class="token">Action Card: <b>${USE_CASES[S.service].service}</b> <div class="token">Action Card: <b>${USE_CASES[S.service].service}</b>
<span class="ctChip">${CHANGE_TYPES[S.change]}</span> <span class="ctChip">${CHANGE_TYPES[S.change]}</span>
<div class="ctText">${USE_CASES[S.service].changes[S.change]}</div> <div class="ctText"><b>${acard(S.service,S.change).titel}</b> — ${acard(S.service,S.change).text}</div>
</div> </div>
`; `;
@ -1183,7 +1181,7 @@ function openDebrief(){
let md = `# SLC-Workshop — Debrief\n\n`; let md = `# SLC-Workshop — Debrief\n\n`;
md += `**Service:** ${USE_CASES[S.service].service}\n`; md += `**Service:** ${USE_CASES[S.service].service}\n`;
md += `**Change-Typ:** ${CHANGE_TYPES[S.change]}\n`; md += `**Change-Typ:** ${CHANGE_TYPES[S.change]}\n`;
md += `**Auslöser:** ${USE_CASES[S.service].changes[S.change]}\n`; md += `**Action Card:** „${acard(S.service,S.change).titel}“ — ${acard(S.service,S.change).text}\n`;
md += `**Stationen bearbeitet:** ${doneN}/${STATIONEN.length}\n`; md += `**Stationen bearbeitet:** ${doneN}/${STATIONEN.length}\n`;
md += `**Quiz:** ${correct}/${total} richtig\n\n`; md += `**Quiz:** ${correct}/${total} richtig\n\n`;
md += `## Als unklar markiert\n`; md += `## Als unklar markiert\n`;
@ -1199,7 +1197,8 @@ function openDebrief(){
erzeugt: stamp, erzeugt: stamp,
service: USE_CASES[S.service].service, service: USE_CASES[S.service].service,
changeTyp: CHANGE_TYPES[S.change], changeTyp: CHANGE_TYPES[S.change],
ausloeser: USE_CASES[S.service].changes[S.change], actionCard: acard(S.service,S.change).titel,
ausloeser: acard(S.service,S.change).text,
stationenBearbeitet: doneN, stationenBearbeitet: doneN,
stationenGesamt: STATIONEN.length, stationenGesamt: STATIONEN.length,
quiz: { richtig: correct, gesamt: total }, quiz: { richtig: correct, gesamt: total },

View file

@ -1,6 +1,13 @@
/* Service Worker — SLC-Workshop Companion (App-Shell, offline-first) */ /* Service Worker — SLC-Workshop Companion (App-Shell, offline-first) */
const CACHE = "slc-companion-v1"; const CACHE = "slc-companion-v2";
const ASSETS = ["./", "index.html", "manifest.webmanifest", "icon.svg"]; const SHELL = ["./", "index.html", "manifest.webmanifest", "icon.svg"];
// Action-Card-Grafiken (cards/s<service>-c<change>.png) fuer Offline vorab cachen.
const CARDS = [];
for (let s = 0; s <= 5; s++) for (let c = 0; c <= 4; c++) {
if (s === 0 && c === 0) continue; // s0-c0 fehlt (Text-Fallback)
CARDS.push(`cards/s${s}-c${c}.png`);
}
const ASSETS = SHELL.concat(CARDS);
self.addEventListener("install", (e) => { self.addEventListener("install", (e) => {
e.waitUntil( e.waitUntil(