--- name: github-repo description: Erstelle, prüfe und publiziere professionelle GitHub Repositories für technische Projekte (MCP-Server, Claude SKILLs, Raspberry Pi, Python). Verwende diesen Skill immer wenn der User (1) ein Repo erstellen, publizieren oder GitHub-ready machen will, (2) bilinguale READMEs (EN/DE) oder Standard-Dateien (LICENSE, .gitignore, CHANGELOG, SECURITY, CONTRIBUTING) braucht, (3) Repo-Metadaten wie Name, Description oder Topics definiert, (4) ein bestehendes Repo prüfen, aktualisieren oder aufräumen will, (5) einen Release oder Git-Tag erstellt, (6) ein Python-Paket oder einen MCP-Server auf PyPI bzw. in die MCP-Registry publiziert, (7) den GitHub-Workflow per gh-CLI oder MCP-Tools nutzt, (8) vor einem Push einen Secrets-, Datenschutz- oder Sicherheitscheck braucht, (9) CI-Fehler debuggt, die ohne Codeänderung auftreten. Auch bei Fragen wie «Wie stelle ich das auf GitHub?», «Kannst du das GitHub-ready machen?», «Warum ist die CI rot?» oder «Warum schlägt der Publish fehl?» diesen Skill verwenden. --- # GitHub Repo Skill Erstellt und pflegt GitHub Repositories mit bilingualem README (EN/DE), Standard-Dateien, Secrets-Check, reproduzierbarer CI und Release-Gate. ## Bundle | Datei | Wann lesen / verwenden | |---|---| | `scripts/validate_repo.py` | Immer vor Push und Release. Meldet Struktur-, README- und CI-Konfigurationsfehler. Ändert nichts. | | `scripts/check_release_artifacts.py` | Im Release-Workflow, nach `python -m build`, vor dem Upload. | | `scripts/test_c1.py` | Nach jeder Änderung an der C1-Logik in `validate_repo.py`: `python3 scripts/test_c1.py` | | `scripts/test_emoji.py` | Nach jeder Änderung an `EMOJI_RE`: `python3 scripts/test_emoji.py` | | `scripts/test_a1.py` | Nach jeder Änderung an der A1-Logik (`check_mcp_marker`): `python3 scripts/test_a1.py` | | `references/mcp-spec.md` | Sobald ein `*-mcp`-Repo angelegt, migriert oder released wird. Zielstand ist MCP-Spec `2026-07-28`. | | `references/mcp-publishing.md` | Sobald ein `*-mcp`-Repo publiziert oder released wird. | | `references/review-rules.md` | **Vor** jeder Änderung an einem bestehenden Repo. | | `references/repo-governance.md` (EN: `.en.md`) | Sobald eine Regel **portfolioweit** gelten soll: Rulesets, Custom Properties, Terraform. | | `assets/templates/` | README-Vorlagen EN/DE | | `assets/workflows/` | `ci.yml`, `publish.yml` (nach `.github/workflows/`), `dependabot.yml` (nach `.github/`) | | `assets/gitignore/`, `assets/LICENSE-MIT.txt` | Standard-Dateien | ## Vier Grundregeln 1. **Metadaten einmal erfassen** (Schritt 0). Titel, Description und Topics werden pro Projekt genau einmal erfragt — gebündelt, vor der ersten Datei, festgehalten in `.github/repo-meta.yml`. 2. **Melden statt aufräumen.** Bei bestehenden Repos zuerst `references/review-rules.md` lesen. Abweichungen sind oft bewusst (Selbstbezeichnungen, präzisere Sektionstitel, deutsche Synonyme) — sie werden nicht «vereinheitlicht». 3. **Kein Push ohne Secrets-Check** (Schritt 9). Die Git-History lässt sich nicht nachträglich privat machen. 4. **Kein Release ohne Gate** (Schritt 12). PyPI-Releases sind unveränderlich; drei der häufigsten Fehler fallen erst nach dem erfolgreichen Upload auf. --- ## GitHub-Backend bestimmen — `gh` ist nicht überall vorhanden **In Claude Code auf dem Web und in Remote-Sessions ist die `gh`-CLI nicht installiert.** Dort stehen nur die GitHub-MCP-Tools und `git` zur Verfügung. Vor der ersten GitHub-Operation feststellen, welches Backend gilt: ```bash gh --version # Fehler = kein gh → Backend B oder C gh auth status # gruen = Backend A ``` | | Backend | Erkennung | |---|---|---| | **A** | `gh`-CLI | `command -v gh` liefert einen Pfad und `gh auth status` ist grün | | **B** | GitHub-MCP-Tools | kein `gh`, aber Tools wie `create_repository`, `create_pull_request`, `push_files` verfügbar | | **C** | nur `git` | weder noch — Repo-Anlage und Settings laufen manuell über github.com | **Operationen je Backend:** | Operation | A — `gh` | B — MCP-Tools | C — nur `git` | |---|---|---|---| | Repo anlegen | `gh repo create` | `create_repository` | manuell auf github.com, dann `git remote add origin` | | Description setzen | `gh repo edit --description` | Feld von `create_repository`, sonst Settings-UI | Settings-UI | | **Topics setzen** | `gh repo edit --add-topic` | **kein Tool vorhanden** → Settings-UI, und in `repo-meta.yml` als offen markieren | Settings-UI | | Commit + Push | `git push` | `push_files` / `create_or_update_file` oder `git push` | `git push` | | Branch anlegen | `git checkout -b` | `create_branch` | `git checkout -b` | | PR erstellen | `gh pr create` | `create_pull_request` | manuell | | CI-Status prüfen | `gh run list` | `pull_request_read` (`get_check_runs`), `actions_list` | github.com | | Default-Branch / Archivstatus | `gh repo view --json isArchived,defaultBranchRef` | `list_branches`, `search_repositories` | `git ls-remote --symref origin HEAD` | | Tag pushen | `git push origin "v$VERSION"` | dito — in Web-/Remote-Sessions oft mit `403` blockiert, siehe unten | dito | | Release | `gh release create` | kein Tool → Release manuell auf github.com | Release manuell auf github.com | | Secret Scanning aktivieren | `gh api -X PATCH …` (9.6) | `run_secret_scanning` scannt, aktiviert aber nicht → Settings-UI | Settings-UI | | Rulesets lesen / setzen | `gh ruleset list\|view\|check`, sonst `gh api` | **kein Tool vorhanden** → Settings-UI, in `repo-meta.yml` als offen markieren | Settings-UI | **Tag-Pushes können gesperrt sein, obwohl Branch-Pushes funktionieren.** In Claude Code auf dem Web läuft `git` über einen Proxy, dessen Egress-Policy `refs/tags/*` ablehnen kann. Symptom: ``` error: RPC failed; HTTP 403 curl 22 The requested URL returned error: 403 fatal: the remote end hung up unexpectedly ``` Kontrolle: `git ls-remote --tags origin` bleibt leer, während `git ls-remote --heads origin` den Default-Branch liefert. Das ist eine Richtlinienentscheidung — **nicht umgehen**, sondern melden. Ein nur lokal gesetzter Tag ist zudem verloren, sobald der ephemere Container endet. Vorgehen: Release-Notes extrahieren (Schritt 12), Notes-Datei und die drei Befehle an den User übergeben, Tag und Release aus einer Umgebung mit Tag-Push-Recht anlegen. ### Shell bestimmen — die Blöcke hier sind Bash **Alle Befehlsblöcke in dieser Datei sind Bash.** Arbeitet der User unter Windows, ist es meist die PowerShell, und dort scheitern drei Dinge, die in Bash selbstverständlich sind: | Bash | Windows PowerShell 5.1 | |---|---| | `a && b` | **Parser-Fehler** — die Zeile läuft gar nicht erst an | | `… \| grep muster` | `… \| Select-String muster` | | `command -v gh` | `Get-Command gh` | Das `&&` ist der gefährliche Fall, weil er **still** ist: `git checkout main && git pull origin main` bricht ab, bevor irgendetwas passiert. Wer die Fehlermeldung übersieht, arbeitet danach auf einem veralteten Stand weiter — und merkt es erst an einer falschen Ausgabe mehrere Schritte später. Genau so ist ein Release-Tag dreimal in Folge auf einem 21 Commits alten Commit gelandet, obwohl jeder Push für sich sauber durchlief. Deshalb: **Befehle einzeln je Zeile schreiben**, nicht mit `&&` verketten. Das funktioniert in beiden Shells und ist der Grund, warum die Blöcke hier so aussehen. Ist die Shell des Users unbekannt und der Befehl folgenschwer (Tag, Push, Release), vorher fragen oder die neutrale Form wählen. Was ein Backend nicht kann, wird **als offener Punkt in `repo-meta.yml` vermerkt** und beim nächsten Durchlauf mit `gh` nachgezogen — nicht stillschweigend übergangen. Alle `gh`-Blöcke in den Schritten 10–12 setzen Backend A voraus. --- ## Schritt 0: Session-Intake — Metadaten einmal vollständig erfassen **Titel, Description und Topics werden pro Projekt genau einmal erfragt** — gebündelt in einer Nachricht, bevor die erste Datei geschrieben wird. Nicht verstreut über die Session, nicht Feld für Feld, nicht in jedem Folgeschritt neu. ### 0.1 Zuerst suchen, dann fragen | Prio | Quelle | Wie | |---|---|---| | 1 | Angaben des Users in dieser Session | aus dem Verlauf übernehmen | | 2 | `.github/repo-meta.yml` | Datei lesen → alles bereits erfasst | | 3 | Bestehendes Repo | `gh repo view {user}/{repo} --json name,description,repositoryTopics,visibility,defaultBranchRef` (Backend A) | | 4 | Projektdateien | `pyproject.toml`, `server.json`, `package.json`, SKILL-Frontmatter, bestehendes README, `LICENSE` | | 5 | Codebase | Projekttyp, Sprache und Zweck ableiten → Vorschlag bauen | ### 0.2 Intake-Block — alles in EINER Nachricht Zu jedem fehlenden Feld einen **konkreten Vorschlag** liefern, damit der User nur bestätigt oder korrigiert. Keine offene Frage, wo aus dem Code ein Vorschlag ableitbar ist. | Feld | Pflicht | Default / Ableitung | |---|---|---| | `repo_name` | ja | Namenskonvention aus Schritt 1 | | `title` | ja | Repo-Name in Title Case | | `description` | ja | Zweck des Codes, max. 100 Zeichen, EN, ohne Schlusspunkt | | `topics` | ja | 5–8, Regeln aus Schritt 1 | | `project_type` | ja | Erkennung aus Schritt 1 | | `visibility` | ja | `public` | | `license` | nein | `MIT` | | `author_legal_name` | nein | `git config user.name` — steht im Copyright (Schritt 5) | | `author_label` | nein | Selbstbezeichnung im deutschen README: `Autor` / `Autorin` / `Autor·in`. Einmal festlegen, **nie** angleichen (Regel D1) | | `github_user` | nein | `gh api user --jq .login` | | `default_branch` | nein | `main` — nicht raten, prüfen (Regel E5) | | `language` | nein | aus Projektdateien | | `version` | nein | `1.0.0` | | `pypi_package` | nur PyPI | Paketname, identisch in `pyproject.toml` | | `mcp_name` | nur MCP | `io.github./` für den README-Marker (A1) | | `mcp_spec_version` | nur MCP | `2026-07-28` — Zielstand, kein Vorschlag zur Auswahl (`references/mcp-spec.md`) | **Fragestil:** Steht `AskUserQuestion` zur Verfügung, damit die Auswahlfelder (`visibility`, `license`, `project_type`) abfragen — abgeleiteter Wert als erste Option. Freitextfelder in derselben Nachricht als bestätigbare Vorschlagsliste. Sagt der User «mach einfach»: Vorschläge übernehmen, `confirmed: false` setzen und am Schluss die Zusammenfassung aus 0.4 zeigen. ### 0.3 Antworten festhalten — `.github/repo-meta.yml` Sofort nach dem Intake schreiben. Ab dann **einzige Quelle der Wahrheit**; alle Folgeschritte lesen daraus, statt erneut zu fragen. ```yaml # .github/repo-meta.yml — von der github-repo Skill gepflegt repo_name: fedlex-mcp title: Fedlex MCP Server description: MCP server for Swiss federal law data from Fedlex topics: [mcp, model-context-protocol, swiss-open-data, python, llm] project_type: mcp-server visibility: public license: MIT author_legal_name: Vorname Nachname author_label: Autor·in github_user: malkreide default_branch: main language: python version: 1.0.0 pypi_package: fedlex-mcp mcp_name: io.github.malkreide/fedlex-mcp mcp_spec_version: 2026-07-28 confirmed: true # false = aus Vorschlägen übernommen, unbestätigt offen: # was das aktuelle Backend nicht setzen konnte - topics (Backend B: kein MCP-Tool — Settings-UI oder später mit gh) ``` Ändert sich später ein Wert: **Datei aktualisieren**, nicht neu fragen. In einer neuen Session ist das Lesen dieser Datei der erste Schritt. ### 0.4 Gate und Verwendung Erst weiter zu Schritt 2, wenn alle Pflichtfelder gesetzt sind. Danach Zusammenfassung ausgeben (Name · Description · Topics · Visibility) und die Werte konsequent wiederverwenden: | Feld | Wird verwendet in | |---|---| | `repo_name` | Verzeichnisname, README-H1, Repo-Anlage (Schritt 10) | | `title` | README-H1 falls abweichend, Release-Titel | | `description` | Repo-Description, README-One-Liner, `server.json` (≤ 100 Zeichen) | | `topics` | Topics setzen (Schritt 10) | | `visibility` | `--public` / `--private` | | `license`, `author_legal_name` | LICENSE (Schritt 5) | | `author_label` | `## Autor·in` in `README.de.md` (Schritt 4) | | `default_branch` | Push- und Workflow-Trigger (Schritt 8, 11) | | `mcp_name` | README-Marker (Schritt 3) | | `mcp_spec_version` | SDK-Untergrenze, README-Hinweis, Nachweis vor dem Release (`references/mcp-spec.md`) | | `version`, `pypi_package` | Badge, CHANGELOG, Tag, Release-Gate (Schritt 12) | --- ## Schritt 1: Projekttyp und Metadaten bestimmen Regeln für die **Vorschläge** aus dem Intake (0.2). Liegen die Werte bereits in `.github/repo-meta.yml`, ist dieser Schritt übersprungen. | Typ | Erkennungsmerkmale | |---|---| | `mcp-server` | MCP, Model Context Protocol, Tools für LLMs | | `claude-skill` | SKILL.md, Claude Skills, Prompting-Framework | | `raspberry-pi` | Pi, GPIO, Edge AI, Hailo, Sensor | | `python-lib` | Python-Package, Library, Module | | `other` | Alles andere | **Repo-Metadaten vorschlagen** (nur für Felder, die 0.1 nicht geliefert hat): - **Name**: `kebab-case`, präzise, kein `my-` Präfix - MCP: `{service}-mcp` · Skill: `{name}-skill` · Pi: `{function}-pi` - **Description**: max. 100 Zeichen, Englisch, ohne Punkt am Ende - Bei MCP-Servern gilt dieselbe Grenze für `server.json` → `description`. Die Registry lehnt längere mit `422 expected length <= 100` ab, und zwar erst **nach** dem PyPI-Upload. - **Topics/Tags**: 5–8, lowercase - Immer: Projekttyp-Tag + Sprachtag (`python`, `typescript`) - MCP: `mcp`, `model-context-protocol`, `llm` · Skill: `claude`, `anthropic`, `prompt-engineering` · Pi: `raspberry-pi`, `edge-ai`, `iot` - Domain-Tags: `swiss-open-data`, `education` --- ## Schritt 2: Dateistruktur erstellen ### Pflichtdateien ``` repo-name/ ├── README.md ← Englisch (Hauptdatei, enthält den mcp-name-Marker) ├── README.de.md ← Deutsch ├── LICENSE ← MIT (Standard), bürgerlicher Name im Copyright ├── .gitignore ← aus assets/gitignore/ └── CHANGELOG.md ``` ### Wenn die Datei erzeugt wird, gehört der Verweis ins README Häufigster Mangel im Durchlauf über 43 Repos: **10 Repos hatten eine `SECURITY.md`, auf die kein README verwies** — für Besucher der Startseite also unerreichbar. Dasselbe bei `CONTRIBUTING.md`. Datei anlegen genügt nicht. ``` ├── SECURITY.md ← verlinkt aus der Sektion Security / Sicherheit ├── CONTRIBUTING.md ← verlinkt aus Contributing / Mitwirken ├── docs/demo.png ← wenn vorhanden: in BEIDEN Sprachfassungen einbinden ├── .github/workflows/ ← ci.yml, publish.yml aus assets/workflows/ └── .github/dependabot.yml ← hält die SHA-Pins aktuell (8.5) ``` ### Zusätzlich bei MCP-Servern ``` ├── server.json ← name, version, description (≤ 100 Zeichen) ├── pyproject.toml ← readme = "README.md", version identisch zu server.json, │ SDK-Untergrenze auf eine Version, die 2026-07-28 spricht └── scripts/check_release_artifacts.py ``` **Jeder MCP-Server zielt auf Spec `2026-07-28`** — neue Server von Anfang an, bestehende als Migration. Der Stand ist zustandslos: kein `initialize`, kein `Mcp-Session-Id`, `server/discover` ist Pflicht, Listen-Results tragen `ttlMs` und `cacheScope`. Roots, Sampling, Logging und der `/sse`-Transport werden nicht mehr neu gebaut. Die Regeln, gelesen aus der Spezifikation, und der Nachweis am Draht: `references/mcp-spec.md`. --- ## Schritt 3: README.md (Englisch) Vorlage: `assets/templates/README.md`. Struktur verpflichtend, Schluss-Sektionen in dieser Reihenfolge: ``` Contributing → Security → License → Author ``` **Regeln:** - **`mcp-name`-Marker** direkt unter dem Titel, bei jedem MCP-Server: `` Er muss in der Datei stehen, die `pyproject.toml` als `readme` deklariert — ein Marker nur in `README.de.md` zählt nicht. Details: `references/mcp-publishing.md`. - **Author als Überschrift** (`## Author`), nicht als Fettdruck (`**Author**`). Fettdruck sieht gleich aus, erzeugt aber keine Gliederungsebene. - **Keine Emoji in Überschriften.** Im Fliesstext (Sprachumschalter, Warnhinweise) sind sie in Ordnung. - **Demo**: `### Demo` plus Bild. Die referenzierte Datei muss existieren, und der Abschnitt gehört in **beide** Sprachfassungen. - **Tool-Tabelle**: die **registrierten** Namen verwenden. Bei `@mcp.tool(name="gazette_get_publication")` heisst die Funktion trotzdem `get_publication` — der registrierte Name gehört ins README. **Badges:** ```markdown ![Version](https://img.shields.io/badge/version-1.0.0-blue) ![License](https://img.shields.io/badge/license-MIT-green) ![Python](https://img.shields.io/badge/python-3.10+-blue) ``` --- ## Schritt 4: README.de.md (Deutsch) Vorlage: `assets/templates/README.de.md`. Identische Struktur, aber: - Schweizer Rechtschreibung: kein `ß` → `ss` - Kopfzeile `🇬🇧 [English Version](README.md)` - Technische Begriffe, Befehle und Code-Blöcke bleiben englisch - **Überschriften werden eingedeutscht** — eine englische Überschrift in einer sonst durchgehend deutschen Datei ist ein Fehler (kam zweimal vor): | EN | DE | |---|---| | Overview | Übersicht | | Features | Funktionen | | Prerequisites | Voraussetzungen | | Usage | Verwendung | | Configuration | Konfiguration | | Project Structure | Projektstruktur | | Contributing | Mitwirken (auch: Mitmachen, Beitragen) | | Security | Sicherheit | | License | Lizenz | | Author | Autor / Autorin / Autor·in | Die Varianten in den letzten drei Zeilen sind gleichwertig. Bei einem bestehenden Repo wird die gewählte Variante **nicht** angeglichen — siehe `references/review-rules.md`. --- ## Schritt 5: LICENSE `assets/LICENSE-MIT.txt` kopieren, `{YEAR}` und `{AUTHOR_LEGAL_NAME}` ersetzen. Im Copyright steht der **bürgerliche Name**, nicht der GitHub-Handle. --- ## Schritt 6: .gitignore Passende Datei aus `assets/gitignore/` kopieren (`python`, `node`, `raspberry-pi`, `claude-skill`). Alle Varianten enthalten bereits die Secret-Muster aus Schritt 9. --- ## Schritt 7: CHANGELOG.md ```markdown # Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ## [1.0.0] - {YYYY-MM-DD} ### Added - Initial release ``` **SemVer:** PATCH = Bugfix · MINOR = neues Feature, rückwärtskompatibel · MAJOR = Breaking Change. --- ## Schritt 8: Python- und CI-Konfiguration 8.1–8.3 verursachen CI-Fehler, die **ohne Codeänderung** auftreten oder Fehler durchlassen. 8.5–8.6 betreffen nicht die Korrektheit des Laufs, sondern das, was er darf und was er kostet. 8.7 betrifft, ob ein Urteil im Workflow überhaupt prüfbar ist. ### 8.1 Linter-Regelsatz explizit pinnen Ohne `[tool.ruff.lint] select` gilt ruffs Default-Regelsatz — und der ändert sich zwischen Releases. Mit `ruff>=0.4.0` ohne Obergrenze installiert die CI die jeweils neuste Version. Ein Repo war fünf Tage nach dem letzten grünen Lauf rot, ohne dass sich eine Zeile geändert hatte. ```toml [tool.ruff.lint] select = ["E", "F", "W", "I", "UP"] ignore = ["E501"] ``` Und in der CI mit Obergrenze installieren: `pip install "ruff>=X.Y, /tmp/release-notes.md test -s /tmp/release-notes.md || { echo "FEHLER: kein CHANGELOG-Abschnitt für $VERSION"; exit 1; } # 5. GitHub Release gh release create "v$VERSION" \ --title "v$VERSION — {Titel}" \ --notes-file /tmp/release-notes.md --latest # 6. Badge in README.md UND README.de.md aktualisieren ``` **Der Tag ist nicht «gesetzt», wenn er remote existiert — sondern erst, wenn er auf dem richtigen Commit steht** (A4). `git ls-remote --tags` beantwortet nur die erste Frage. Der Unterschied ist nicht theoretisch: ein Tag, der auf einem älteren Commit sitzt, erzeugt einen Release, dessen **Text** die neuen Änderungen beschreibt und dessen **Archiv** sie nicht enthält. Bei einem Security-Release ist das die gefährlichste Variante — der Hinweis auf die Behebung wird ausgeliefert, die Behebung nicht. Der Fehler entsteht fast immer gleich: die Arbeitskopie steht nicht auf dem aktuellen Default-Branch, und `git tag` nimmt stillschweigend `HEAD`. Deshalb oben `git pull` davor, der Commit **benannt** statt aus `HEAD` übernommen, und die Gegenprobe danach. Endgültige Kontrolle nach dem Release — am Inhalt, nicht an den Refs: ```bash curl -sL "https://github.com/{user}/{repo}/archive/refs/tags/v$VERSION.tar.gz" \ | tar xzO --wildcards '*/README.md' | grep -o 'version-[0-9.]*-' ``` **Warum nicht `--notes-file CHANGELOG.md`:** das hängt den gesamten Versionsverlauf an jeden Release. Alternative ohne CHANGELOG-Pflege: `gh release create "v$VERSION" --generate-notes`. **Wenn der Publish-Workflow fehlschlägt:** einen alten Tag-Lauf **nicht** erneut starten — ein Re-Run checkt den alten Commit aus und reproduziert denselben Fehler. Stattdessen `workflow_dispatch` auf dem Default-Branch oder einen neuen Tag setzen. --- ## Bestehende Repos bearbeiten **Zuerst `references/review-rules.md` lesen.** Diese Regeln haben jeweils eine falsche Änderung oder einen Fehlalarm verhindert: | Regel | Kurzfassung | |---|---| | D1 | Selbstbezeichnungen (`Autor`, `Autorin`, `Autor·in`) nie umschreiben | | D2 | Präzisere Titel (`Software Licence`, `Security & Compliance`) behalten | | D3 | Deutsche Synonyme sind alle legitim — nur englische Titel in deutschen Dateien sind falsch | | E1 | Tool-Namen gegen den registrierten Namen prüfen, nicht gegen `def` | | E2 | Bilder in `![]()` **und** `` suchen, Badges herausfiltern | | E3 | Überschriften exakt vergleichen — `## Sicherheit & Grenzen` ≠ `## Sicherheit` | | E4 | Vor Emoji-Entfernung Anker-Links prüfen; Unicode-Bereiche eng fassen, sonst fallen Umlaute weg | | E5 | Default-Branch ist nicht immer `main` | | E6 | C1-Reihenfolgefehler zeigt oft auf eine Inhaltssektion, nicht auf den Schlussblock — vor dem Umsortieren prüfen, wo die gemeldete Sektion steht | | E7 | Emoji ≠ «Zeichen über U+2000». `↔` ist Typografie, `↔️` ein Emoji — Textdarstellungs-Zeichen zählen erst mit VS16 | | E8 | Links, die etwas über fremde Inhalte behaupten, auf einen Tag pinnen — und den Tag wöchentlich auf Aktualität prüfen | | F2 | 403 beim Push: zuerst Archiv-Status prüfen, nicht Berechtigungen | | F3 | Ein abgebrochener Sweep ist kein Teilergebnis — nicht erreichte Repos zuerst und namentlich nennen; transiente Fehler im Transport wiederholen, nie im Gate tolerieren | --- ## Qualitätscheckliste `python3 scripts/validate_repo.py .` deckt die meisten Punkte automatisch ab. **Blocker — ohne diese Punkte kein Push** - [ ] `gitleaks detect` ohne Findings - [ ] Keine Personendaten oder internen Systembezeichnungen im Repo - [ ] Commit-Identität (`user.email`) bewusst gewählt - [ ] Secret Scanning + Push Protection aktiviert **Session-Intake (Schritt 0)** - [ ] Pflichtfelder erfasst — Titel, Description, Topics genau einmal erfragt - [ ] `.github/repo-meta.yml` vorhanden und aktuell (`confirmed: true`) - [ ] Werte in README, Repo-Description und Topics identisch zur Intake-Datei - [ ] Was das Backend nicht setzen konnte, steht unter `offen` **README (beide Sprachfassungen)** - [ ] Schluss-Sektionen in der Reihenfolge Contributing → Security → License → Author - [ ] `SECURITY.md` und `CONTRIBUTING.md` verlinkt, nicht nur vorhanden - [ ] `## Author` als Überschrift, nicht als Fettdruck - [ ] Keine Emoji in Überschriften - [ ] Demo in beiden Fassungen, referenzierte Datei existiert - [ ] Sprachumschalter in beiden Richtungen - [ ] DE: deutsche Überschriften, kein `ß` - [ ] Tool-Namen = registrierte Namen **Repo-Metadaten** - [ ] Description ≤ 100 Zeichen · Topics 5–8 · LICENSE mit bürgerlichem Namen · `.gitignore` passend **CI** - [ ] `[tool.ruff.lint] select` explizit, in **jedem** pyproject - [ ] ruff gepinnt — Obergrenze genügt für `check`, **exakt** sobald ein Formatgate steht (8.1) - [ ] Pin an **einer** Stelle (`requirements-lint.txt`), Workflow liest von dort, Versions-Gate vor den Gates (8.1) - [ ] Keine ruff-Version als *Vorgabe* in README, Skill-Text oder Vorlagen — der Pin lebt nur im Zielrepo; datierte Messangaben («gemessen mit …») dürfen stehen (8.1) - [ ] `line-length` explizit gesetzt, auch wenn sie dem Default entspricht (8.1) - [ ] `ruff format --check` als eigener Schritt — und lokal vor dem Push mitgefahren - [ ] Keine `pytest.raises(Exception)` - [ ] Tests lokal mit demselben Runner wie in der CI (8.3) - [ ] Keine Prüflogik im Workflow-Heredoc (8.7) **MCP-Protokoll** - [ ] `mcp_spec_version: 2026-07-28` in `repo-meta.yml` (A5) - [ ] SDK-Untergrenze auf eine Version, die `2026-07-28` spricht, Lockfile nachgezogen (`references/mcp-spec.md` S3) - [ ] `server/discover` am Draht geprüft, `supportedVersions` enthält `2026-07-28` (S4) - [ ] Keine neuen Roots-, Sampling-, Logging- oder `/sse`-Features (S2) **Release (MCP/PyPI)** - [ ] `mcp-name`-Marker in der als `readme` deklarierten Datei - [ ] `server.json` description ≤ 100 Zeichen - [ ] Version in pyproject = server.json = Tag - [ ] Pending Publisher angelegt, Environment-Name eingetragen - [ ] Release-Notes enthalten nur den Abschnitt dieser Version --- ## Troubleshooting | Symptom | Ursache | Vorgehen | |---|---|---| | `invalid-publisher: valid token, but no corresponding publisher` | Pending Publisher fehlt oder Environment-Feld leer | `references/mcp-publishing.md` → A3 | | `422 expected length <= 100` beim Registry-Publish | `server.json` description zu lang — fällt erst nach erfolgreichem PyPI-Upload auf | A2 | | Registry findet das Paket nicht | `mcp-name`-Marker fehlt im publizierten README | A1 | | Publish erfolgreich, `/pypi//json` zeigt alte Version | JSON-API liefert gecachte Antworten | gegen `https://pypi.org/simple//` prüfen (F1) | | Re-Run schlägt identisch fehl | alter Tag-Lauf checkt alten Commit aus | `workflow_dispatch` oder neuer Tag (A4) | | CI rot ohne Codeänderung | ruff-Default-Regelsatz hat sich geändert | `select` pinnen (8.1) | | Formatgate rot ohne Codeänderung, lokal grün | lokal läuft ein anderes ruff als der Pin | `ruff --version` gegen `requirements-lint.txt`; Versions-Gate (8.1) | | Testmodul fehlt im CI-Lauf, alle anderen `ok` | Import scheitert in der Discovery (z. B. `pytest` fehlt unter `unittest`) | mit dem CI-Runner lokal prüfen (8.3) | | Workflow grün, Zusammenfassung leer oder Traceback im Log | Inline-Python im YAML; `exit $rc` meldet nur den ersten Exit-Code | Logik in ein getestetes Skript (8.7) | | `403` auf `/commits/{sha}/check-runs` mit Fine-grained-PAT | Die Checks-API ist für Fine-grained-Tokens nicht freigebbar — nur GitHub Apps | `/actions/runs?head_sha=…` mit `Actions: Read` | | MCP-Client meldet «method not found» auf `initialize` | Server ist auf `2026-07-28`, der Client noch nicht — kein Serverfehler | Client heben; Protokollstand im README nennen (`references/mcp-spec.md` S5) | | `400` mit `-32020 HeaderMismatch` | `MCP-Protocol-Version`-Header ≠ `_meta`-Wert, oder leerer `Mcp-Name` | `references/mcp-spec.md` S1 | | Lint übersieht Subprojekt | eigene `pyproject.toml` erbt nichts | eigener `select` (8.2) | | `403` beim Push, Lesen geht | Repo archiviert | `gh repo view --json isArchived` (F2) | | `gh: command not found` | Web-/Remote-Session ohne CLI | Backend B oder C, siehe Mapping-Tabelle oben | | `403` beim Tag-Push, Branch-Push geht | Egress-Policy der Session lehnt `refs/tags/*` ab | nicht umgehen: Notes extrahieren, Tag und Release lokal anlegen (Backend-Abschnitt) | | Release-Text beschreibt Änderungen, die im Archiv fehlen | Tag sitzt auf einem älteren Commit — Push lief sauber, Ziel war falsch | `git rev-parse "v$VERSION^{}"` gegen `origin/main` (A4, Schritt 12.3b) | | Tag mehrfach neu gesetzt, Ziel bleibt falsch | Arbeitskopie steht nicht auf dem Default-Branch; `git tag` nimmt deren `HEAD` | `git pull` davor, Commit benennen: `git tag -a v… ` (12.3) | | Befehl mit `&&` tut unter Windows nichts | PowerShell 5.1 kennt `&&` nicht — Parser-Fehler vor der Ausführung | Befehle einzeln je Zeile (Shell-Abschnitt) | | Topics lassen sich nicht setzen | Backend B hat kein Topic-Tool | Settings-UI, in `repo-meta.yml` unter `offen` vermerken | | Nach «Titel? Description? Tags?» in jeder Session | Schritt 0 übersprungen | `.github/repo-meta.yml` anlegen (0.3) | | Lauf über viele Repos bricht mittendrin ab | Rate-Limit — primär oder Secondary/Abuse | Nicht erreichte Repos im Bericht **zuerst und namentlich** nennen; ein Teilergebnis liest sich sonst wie ein vollständiges (F3) | --- ## Schnellreferenz: gh (Backend A) ```bash gh api rate_limit # vor jedem Lauf über mehrere Repos (F3) gh repo view {user}/{repo} gh repo view {user}/{repo} --json isArchived,defaultBranchRef gh repo edit --add-topic {topic} / --remove-topic {topic} gh repo list gh issue create --title "{Titel}" --body "{Text}" gh pr create --title "{Titel}" --body "{Text}" gh release list / gh release view gh run list --workflow publish.yml gh run rerun {run-id} # Achtung: checkt den alten Commit aus (A4) ```