đȘ Hilfestellung â Stufen und Schnittstellen (verbindlicher Vertrag)
âŹïž Netzwerk-Labor · Vertrag: Architektur · Spezifikation: Konzept â Netzwerk-Labor
Zweck. Das Labor soll einen Menschen ohne Syntaxkenntnis spielbar machen, ohne ihn zu langweilen, sobald er die Syntax kennt. DafĂŒr bekommt jede Ausbildungslage eine eigene Hilfe-Stufe mit einer messbaren Zahl von Hilfen. Dieser Text ist der Vertrag zwischen den Bausteinen â zuerst hier eintragen, dann bauen (AGENTS.md âVertrag zuerst").
Grundsatz (aus dem Auftrag, 07.10.2026): Ein Azubi kann die ganzen Syntaxe nicht kennen, und im Spielflow hat er keinen Zugriff auf eine Lernnotiz. Also muss das Spiel die Hilfe dort anbieten, wo er steht: im Terminal, in der Leiste, im Trainingsbereich. Die Hilfe ist schrittweise â sie wĂ€chst mit dem Bildungsstand und mit dem Lernstand der einzelnen Fertigkeit.
§ 1 · Begriff: Bildungsstand â ErklĂ€rtiefe â Niveau
Drei Dinge hieĂen bisher âNiveau". Ab hier sind sie getrennt â daran hĂ€ngt jeder weitere Baustein.
| Begriff | Feld | Werte | Wer liest das? |
|---|---|---|---|
| Bildungsstand (neu) | Spiel.einst.stufe | "azubi" · "azubi-plus" · "geselle" · "meister" | Spiel.stufe.*, Terminal, Leiste, Trainingsbereich, Konsole, Simulation |
| ErklÀrtiefe (bestehend) | Spiel.einst.niveau | "E" · "AP1" · "AP2" | UI.konsole (heute), Spiel.grundText |
| PrĂŒfungsstrenge je Ticket (bestehend) | Spiel.einst.wahl, inst.niveau | "auto" · "E" · "AP1" · "AP2" | Spiel.niveauVon, Spiel.abnahme |
Regel: Der Bildungsstand ist eine Voreinstellung des Menschen, kein Spielzustand und keine Automatik. Er wird nie automatisch geĂ€ndert. Spiel.einst.niveau (ErklĂ€rtiefe) wird beim Setzen des Bildungsstands mitgezogen, bleibt aber einzeln verstellbar â wer AP2-Texte bei Azubi-Hilfe will, darf das.
§ 2 · Die vier Stufen (Zahlen sind Vertrag)
stufe ist der Rang: azubi 1 · azubi-plus 2 · geselle 3 · meister 4. Vergleiche immer ĂŒber den Rang, nie ĂŒber den Namen.
azubi (1) | azubi-plus (2) | geselle (3) | meister (4) | |
|---|---|---|---|---|
| Wer | 1. Lehrjahr, kennt keine Syntax | 2.â3. Lehrjahr, kennt Grundbefehle | kann den Job, ĂŒbt PrĂŒfung | will keine Hilfe |
| Terminal: Vorschlagsstreifen | 1 Vorschlag, mit Syntax + ErklÀrung | bis 2 VorschlÀge, knapp | nur nach Fehler | keiner |
| Terminal: âWas geht hier?" | immer sichtbar | sichtbar | nach Fehler | nein |
Terminal: Befehls-GerĂŒst (zeige) | ja (Befehl ausgeschrieben) | ja, knapper Text | nein | nein |
| Terminal: Tipp zu falschem Befehl | ausfĂŒhrlich + Beispiel | knapp | knapp | nein |
| Terminal: Werkzeugleiter | alle 6 Sprossen sichtbar | sichtbar | erst nach Fehler | nein |
| Leiste/Mini: Hilfe-Knopf | bei jeder Frage, stufenweise (2 Hilfen) | bei jeder Frage (1 Hilfe) | nur nach falscher Antwort | nein |
| Leiste/Mini: Anker (âwo steht das?") | immer nach der Antwort | nach der Antwort | nur bei Fehler | nein |
| Trainingsbereich: gefĂŒhrte Simulation | angeboten, mit GerĂŒst | angeboten, ohne GerĂŒst | Vorratsliste | nein |
| Simulation: GrĂŒnde in Klartext | ausfĂŒhrlich (E-Stufe) | ausfĂŒhrlich (E-Stufe) | knapp (AP1) | nur Codes |
Konsole (CLI): einstieg, Tipps | einstieg:true, tipps:"alle" | einstieg:true, tipps:"fehler" | einstieg:false, tipps:"fehler" | einstieg:false, tipps:"keine" |
Hilfen sind schrittweise, nie alle auf einmal. Jede Stufe hat einen Vorrat (Kontingent), der mit jeder geöffneten Sprosse kleiner wird. Der Vorrat wird nur durch Hilfen verbraucht, nie durch Fehler.
Nachgetragen 07.10.2026 nach dem GegenprĂŒfungsbefund B1 von
prĂŒfer-vertrag: Die ErklĂ€rtiefe ist eine eigene Achse (§ 1) und wird nicht vom Bildungsstand gesteuert â sie wird beisetzen()nur mitgezogen. Deshalb sind fĂŒrazubiundazubi-plusbeide Male ausfĂŒhrliche Texte richtig:azubi-plusunterscheidet sich nicht durch kĂŒrzere Texte, sondern durch weniger und knappere VorschlĂ€ge (2 statt 1, knapper formuliert) und einen kleineren Vorrat (4 statt 6). Wer fĂŒrazubi-pluskĂŒrzere ErklĂ€rtexte will, verstellt die ErklĂ€rtiefe â die steht bewusst getrennt.
2.2 Wann erscheint eine Hilfe â zweite Abfrage, nicht zweite Wahrheit
Nachgetragen 07.10.2026 nach dem GegenprĂŒfungsbefund B2 von
prĂŒfer-vertrag: Die Tabelle oben nennt fĂŒr die Werkzeugleiste, den Mini-Hilfe-Knopf und den Lernanker âerst nach Fehler" (geselle). Das ist keine Aussage darĂŒber, ob eine Stufe die FlĂ€che hat â das istdarf()â, sondern darĂŒber, wann sie erscheint. Beides muss abfragbar sein, sonst baut jede FlĂ€che die Bedingung selbst nach und es entstehen zwei Wahrheiten.
Spiel.stufe.darf("leiter" | "anker" | "miniHilfe" | "training") // hat diese Stufe die FlĂ€che ĂŒberhaupt?
Spiel.stufe.wann("leiter" | "anker" | "miniHilfe") // "immer" | "nachfehler" | "nein"
Spiel.stufe.wannPasst(flaeche, {fehler}) // -> {ja, grund}
wann(...)liefert die Bedingung als Wert,wannPasst(...)wertet sie mit dem mitgegebenen Zustand aus und gibt{ja, grund}zurĂŒck â nichttrue/false. Die BegrĂŒndung ist Teil der Antwort, damit eine FlĂ€che sie anzeigen kann, ohne die Regel zu kennen. Achtung:if (Spiel.stufe.wannPasst("miniHilfe"))ist immer wahr (Objekt!). Richtig istif (Spiel.stufe.wannPasst("miniHilfe", {fehler}).ja).darf()undwann()sind zwei verschiedene Fragen.darf("leiter")ist fĂŒrgeselletrueâ die Werkzeugleiste gibt es, sie erscheint nur erst nach einem Fehler (wann("leiter") === "nachfehler"). Wer beides verwechselt, zeigt einem Gesellen die Leiter sofort.- Jede FlĂ€che fragt
wannPasstâ keine baut die Fehler-Bedingung selbst nach. Das ist der Punkt, an demmini.jsund die Konsole sonst auseinanderlaufen wĂŒrden. - Fehlt der Zustand (
fehlernicht ĂŒbergeben), gilt er als ânoch kein Fehler" â eine Hilfe ânach Fehler" erscheint dann nicht. Im Zweifel wird nichts verraten. wann("training")ist nicht belegt und liefert deshalb"nein"; der Trainingsbereich ist kein Fehler-Hilfsmittel, sondern ein Ort (Regel: unbekannte FlĂ€che â"nein").
2.3 Abfragewege der Tabelle (Befund B3)
Nachgetragen 07.10.2026 nach dem GegenprĂŒfungsbefund B3 von
prĂŒfer-vertrag: Zwei Zeilen der Tabelle hatten keinen Abfrageweg ĂŒberSpiel.stufe; wer sie je Stufe steuern wollte, hĂ€tte die Tabelle ein zweites Mal ausgewertet. Sie sind jetzt Teil der einen Tabelle:
| Frage | kann(...) | darf(...) | ||
|---|---|---|---|---|
| âWas geht hier?" (GerĂŒst-Text je Modus) | kann("wasGeht") â "immer" \ | "nachfehler" \ | "nein" | darf("wasGeht") â abgeleitet: kann("wasGeht") !== "nein". Es gibt dafĂŒr bewusst keine zweite Tabelle. |
Befehls-GerĂŒst (zeige) | kann("geruest") â true \ | "knapp" \ | false | darf("geruest") â eigener Schalter, weil "knapp" zwar vorhanden, aber nicht true ist. |
Konsole: einstieg/tipps je Stufe | kann("einstieg"), kann("tipps") | â (Werte, keine Schalter) |
2.1 Vorrat (Kontingent) â verbindlich
Spiel.HILFE_KONTO = {azubi: 6, "azubi-plus": 4, geselle: 2, meister: 0}
Spiel.stufe.hilfenFrei()â noch freie Hilfen dieser Instanz (nicht rĂŒckwirkend, je Ticket).- GezĂ€hlt wird in
inst.hilfen(bestehende Liste:{stufe, t}) plusinst.hilfenFrei. - Der Vorrat hÀngt an der Stufe des TICKETS, nicht an der des Menschen. TrÀgt
inst.stufeeine Stufe, gilt sie; sonst gilt die Voreinstellungazubi.Spiel.einst.stufe(Bildungsstand des Menschen) bestimmt nur, welche Hilfe sichtbar ist â ein Mensch, der mitten im offenen Ticket auf âmeister" umstellt, verliert den Vorrat dieses Tickets nicht. *(Nachgetragen 07.10.2026 nach einem Befund vonstufen-bauer; der Code undtests/spiel-stufensystem.test.jshalten es so.)* - Ist der Vorrat leer, ist die nĂ€chste Sprosse nicht gesperrt, sondern kostet Sterne wie bisher (Stufe 4/5 = âœ, Stufe 6 = â1). Ein Azubi darf nie in eine Sackgasse laufen â das ist die hĂ€rteste Regel dieses Vertrags. Ein leerer Vorrat Ă€ndert nur den Text (âjetzt kostet es Sterne").
§ 3 · Datenform: Spiel.stufe (neu)
Datei src/spiel/stufensystem.js â neu, ein Besitzer. Ăffentliche FlĂ€che (nichts anderes aufrufen):
Spiel.STUFE = [
/* Jede Zeile der Tabelle § 2 hat hier ihren Wert â auch die drei Spalten aus § 2.3. */
{id:"azubi", rang:1, name:"Azubi (1. Lehrjahr)", kurz:"Azubi", niveau:"E", tipps:"alle", vorschlaege:1,
leiter:"immer", erklaerung:"ausfuehrlich", wasGeht:"immer", geruest:true, einstieg:true},
{id:"azubi-plus", rang:2, name:"Azubi (fortgeschritten)", kurz:"Azubi+", niveau:"E", tipps:"fehler", vorschlaege:2,
leiter:"immer", erklaerung:"ausfuehrlich", wasGeht:"immer", geruest:"knapp", einstieg:true},
{id:"geselle", rang:3, name:"Geselle / PrĂŒfungsvorbereitung", kurz:"Geselle", niveau:"AP1", tipps:"fehler", vorschlaege:1,
leiter:"nachfehler", erklaerung:"knapp", wasGeht:"nachfehler", geruest:false, einstieg:false},
{id:"meister", rang:4, name:"Meister / Profi", kurz:"Meister", niveau:"AP2", tipps:"keine", vorschlaege:0,
leiter:"nein", erklaerung:"nurcodes", wasGeht:"nein", geruest:false, einstieg:false},
];
Spiel.stufe.alle() // -> Spiel.STUFE (Kopie, nie das Original)
Spiel.stufe.id() // -> aktueller Bildungsstand, Standard "azubi"
Spiel.stufe.def(id?) // -> Eintrag
Spiel.stufe.rang(id?) // -> 1..4
Spiel.stufe.setzen(id, {still}?) // setzt einst.stufe + zieht einst.niveau nach; sendet "stufe" + "niveau"
Spiel.stufe.kann(frage) // -> Wert aus der Tabelle: vorschlaege, tipps, niveau, einstieg, wasGeht,
// geruest, leiter, erklaerung; kann("konto") -> Zahl
Spiel.stufe.darf(frage) // -> true/false: HAT diese Stufe die FlÀche? ("leiter", "anker", "miniHilfe",
// "training", "geruest" aus der Schaltertabelle; "wasGeht" ABGELEITET)
Spiel.stufe.wann(flaeche) // -> "immer" | "nachfehler" | "nein" WANN erscheint die FlÀche? (§ 2.2)
Spiel.stufe.wannPasst(flaeche, {fehler}) // -> {ja, grund} wertet die Bedingung aus â siehe § 2.2
Spiel.stufe.erklaerung() // -> "ausfuehrlich" | "knapp" | "nurcodes"
Spiel.stufe.konto() // -> {frei, gesamt} fĂŒr die offene Instanz (null ohne Instanz)
Spiel.stufe.hilfenFrei([inst])// -> nur die Zahl (fĂŒr eine Zeile ânoch 3 von 6 Hilfen")
Spiel.stufe.hilfeZiehen(inst) // -> {frei:boolean, grund} verbraucht EINE freie Hilfe, wenn vorhanden
Spiel.stufe.text(ausfuehrlich, knapp, nurcodes) // -> den Text der Stufe (nie null)
Spiel.stufe.erklaerungText(id?) // -> eine Zeile fĂŒr die OberflĂ€che (kein DOM)
Spiel.stufe.freigabeText(id?) // -> eine Zeile âwas ist gerade frei" (kein DOM)
Regeln:
- Kein Aufruf darf werfen, wenn
einst.stufefehlt oder unbekannt ist â RĂŒckfall"azubi". Spiel.stufeliest nieL(Lernmotor) â die Stufe ist eine Voreinstellung, der Lernstand kommt woanders her.Spiel.stufe.setzenschreibt ĂŒberSpiel.einstSetzen(nicht direkt instore), damit die OberflĂ€che es merkt.- Deterministisch: kein
Math.random, keinDate.nowin dieser Datei. - Wer eine Hilfe-FlÀche baut, fragt
darf()UNDwannPasst()â nie nur eines von beiden, und baut die Fehler-Regel nicht selbst nach (§ 2.2).
§ 4 · Terminal-Hilfe: Datenform DATEN.vorschlaege
Neue Datei src/daten/hilfen.js â Datenpaket, ein Besitzer. Der Kern ruft es ĂŒber Spiel.hilfe.passend(...), nie ĂŒber direkte Tabellenzugriffe aus der OberflĂ€che.
DATEN.hilfen.VORSCHLAEGE = [
{ id: "link-pruefen", // eindeutig, stabil, PrÀfix nach Bereich
bereich: "link", // = Spiel.LEITER[].id, damit Leiter und Vorschlag dieselbe Sprache sprechen
skills: ["lab.link"], // Fertigkeiten, bei denen das passt
befehl: "show ip interface brief", // was im Terminal steht (leer = kein Befehl)
erklaerung: "Ăberblick: jede Schnittstelle mit Adresse und Status.", // EIN Satz, warum
syntax: "show ip interface brief", // Muster zum Lernen, ggf. mit <Platzhalter>
geraet: "ios", // "ios" | "host-windows" | "host-linux" | "fw" | "alle"
modus: ["priv","config"], // zulÀssige CLI-Modi, leer = alle
ebene: 1, // 1..3 (1 = harmlos ansehen, 3 = Àndert etwas)
art: "pruefen" // "pruefen" (Ă€ndert nichts) | "aendern" (Ă€ndert Konfiguration)
}, âŠ
];
DATEN.hilfen.GERUEST = { // âWas geht hier?â je Modus â Text, kein Befehl
ios: {user: "âŠ", priv: "âŠ", config: "âŠ", if: "âŠ", vlan: "âŠ", dhcp: "âŠ", âŠ},
"host-windows": {âŠ}, "host-linux": {âŠ}, fw: {âŠ}
};
DATEN.hilfen.SYNTAX = { // Syntax-BrĂŒcke: âich will X" -> âso schreibt man das"
"ip-adresse-setzen": {titel:"IP-Adresse setzen", ios:"ip address <IP> <Maske>", host:"netsh interface ip set address âŠ", âŠ}
};
4.1 Der Vorschlagsstreifen (src/ui/hilfe.js, PrÀfix hl-)
UI.hilfe.zeichnen(container, {netz, geraetId, modus, sitzung, letzterFehler}) â die OberflĂ€che kennt die Sitzung, weil UI.konsole sie hĂ€lt. Entscheidung liegt in Spiel.hilfe, nicht in der OberflĂ€che:
Spiel.hilfe.passend({netz, id, modus, art, verlauf, letzterFehler, max}) // -> [{âŠvorschlag, warum}]
Spiel.hilfe.geruest({netz, id, modus, art}) // -> string | null
Spiel.hilfe.leiter({netz, id, skill}) // -> Spiel.LEITER + {vorschlag}
Spiel.hilfe.syntaxBruecke({netz, id, modus, text, fehler}) // -> {titel, muster, beispiel} | null
Wo es hĂ€ngt (verbindlich, sonst ist es wirkungslos â AGENTS.md âWirkung vor GrĂŒn"): UI.konsole.oeffnen hĂ€ngt den Streifen unter den Schirm, UI.konsole.ausfuehren ruft UI.hilfe.aktualisieren(K, {letzterFehler: r}) nach jeder Zeile auf, und UI.konsole.vorschlag (der bestehende E-Vorschlag) wird durch Spiel.hilfe gespeist, damit es nur eine Vorschlagsquelle gibt.
§ 5 · Leiste/Mini: Lernanker
Spiel.mini bekommt nichts Neues an Datenformen, nur zwei FlÀchen:
Spiel.mini.hilfe(id) // -> {text, art:"denkhilfe"|"ausschnitt", frei:boolean} stufenweise, je Frage
Spiel.mini.anker(id) // -> {titel, text, quelle, wiki:skillId} | null nach der Antwort
hilfeist keine Lösung: es ist der DenkanstoĂ (âWas passiert mit der running-config, wenn der Strom ausfĂ€llt?") oder ein Ausschnitt aus der Frage. Nie die richtige Option.ankererscheint nach dem Antworten, verknĂŒpft die Frage mit einer Fertigkeit und einem Wiki-Eintrag (UI.wiki.oeffnen(skill)) und schreibt ĂŒberL.ueben(skill, âŠ)mit â der bestehende Weg, kein neuer.- Der Hilfe-Knopf steht in
UI.leiste(UI.leiste.miniHilfeDOM-Bereich) und im Mini-Overlay ausUI.karriereâ beide Wege mĂŒssen ihn zeigen, sonst ist es wieder nur halb (Lehre aus dem DHCP-Pool).
§ 6 · Trainingsbereich: Spiel.training
Neue Datei src/spiel/training.js â Simulationen abseits der AuftrĂ€ge, eigenes PrĂ€fix tr-, eigene Ansicht "training" (UI.app.registrieren("training", âŠ)).
Spiel.TRAINING = [ {id, titel, beschreibung, skill, geraet, art:"basis"|"stoerung"|"pruefung", minuten, geruest:true} ⊠];
Spiel.training.liste() // -> [{âŠdef, stand:{versucht, bestanden, sterne}, offen:boolean, grund}]
Spiel.training.starten(id) // -> {ok, iid} | {ok:false, grund} legt eine Instanz mit quelle "training" an
Spiel.training.stand() // -> Spiel.st.training = {je:{[id]:{versucht,bestanden,bestes}}}
Spiel.training.abnehmen(iid, ergebnis) // schreibt Stand + L.ueben, KEIN Euro/Ruf, KEIN Karrierefortschritt
Regeln: Training zahlt kein Geld und keinen Ruf (sonst wird es zur Geldquelle statt zum Ăben), aber es zĂ€hlt fĂŒr den Lernmotor (L.ueben) und fĂŒr den Trainingsstand. Es erscheint nie im Postfach und nie in der Karriere-Wertung. Stufe meister sieht es nur als Liste ohne GerĂŒst.
§ 7 · Schreibrechte (damit sich niemand ĂŒberschreibt)
Ein Besitzer je Datei. Wer eine fremde Datei braucht, ruft ihre öffentliche FlÀche auf.
| Baustein | Dateien (Eigentum) |
|---|---|
| Kern-Stufensystem | src/spiel/stufensystem.js · src/ui/stufensystem.js |
| Terminal-Hilfe | src/daten/hilfen.js · src/spiel/hilfe.js (nur anfĂŒgen, s. u.) · src/ui/hilfe.js · src/stil/hilfe.css · src/ui/konsole.js |
| Leiste/Mini | src/spiel/mini.js · src/ui/leiste.js · src/ui/karriere.js · src/stil/leiste.css |
| Trainingsbereich | src/spiel/training.js · src/ui/training.js · src/stil/training.css |
| Doku + Bau + Abnahme | docs/Architektur.md · docs/entwicklung/Design â SpielspaĂ 2.0.md · src/ui/app.js · AGENTS.md |
src/spiel/hilfe.js gehört der bestehenden Hilfeleiter. Der Terminal-Baustein darf dort nur am Dateiende neue Funktionen anfĂŒgen (Spiel.hilfe.*), keine bestehende Zeile Ă€ndern. Das ist die einzige geteilte Datei â sie wird an genau einer Stelle angefasst.
bauen.py nimmt neue Dateien in src/daten, src/spiel, src/ui, src/stil automatisch auf (alphabetisch, mit Kopf-/Schlusslisten). Kein Eintrag nötig, keine ReihenfolgeÀnderung.
§ 8 · Was als âfertig" gilt (Abnahme, sechs Punkte)
node tests/run.jsist â„ 251/251 grĂŒn,python tools/ethos.pymeldet GRĂN.python bauen.pylĂ€uft durch (Browser-Fassung), keine Ausnahme.- Zu jedem Baustein gibt es eine neue Testdatei
tests/spiel-<baustein>.test.jsbzw.tests/cli-<baustein>.test.jsnach der Art vontests/harness.js. - Wirkung vor GrĂŒn: Der Baustein wird aus dem echten Weg aufgerufen (Terminal-Zeile, Leisten-Knopf, Trainingsansicht). Ein Test, der nur seinen eigenen Aufruf prĂŒft, gilt als nicht fertig.
- Die Stufen
azubiundmeistersind beide durchgespielt:azubikommt ohne Syntaxkenntnis voran,meistersieht keine ungefragte Hilfe. docs/Architektur.md(Stand-Tabelle) unddocs/entwicklung/Design â SpielspaĂ 2.0.md(neuer Abschnitt) zeigen den echten Zustand.
§ 9 · Offene Punkte (ehrlich, Stand 07.10.2026)
- Die Android-Fassung ist nicht Teil dieses Auftrags (Nutzerentscheidung 07.10.2026).
android/bauen.pywird nicht angefasst und nicht neu gebaut. - Die Zahlen der Tabelle in § 2 sind ein Startpunkt, kein Messergebnis. Sie werden nach dem ersten Durchspielen nachgezogen â dann aber hier, nicht nur im Code.