Afresto Next deployen
Stack: Backend Go (push→GHCR→Watchtower ~5 Min.) · Web React (Vite→Cloudflare Worker, manuell wrangler deploy) · Mobile RN/Expo in separatem Repo <repo-mobile> (EAS OTA). Produktions-Deployment = immer zuerst Benutzer um Erlaubnis fragen (sie stimmen jedes Mal zu).
🔴 Regel #1 — Dieses Repo wird oft von VIELEN TAB-AGENTEN gleichzeitig verwendet
git status zeigt oft modifizierte Dateien von anderen Sitzungen an. Deshalb:
- NIEMALS
git add -a/git commit -a/git add .— das würde unfertige Arbeiten anderer Sitzungen in deinen Commit ziehen. - SELEKTIV stagen nur deine Dateien, dann prüfen:
git diff --cached --name-only— stelle sicher, dass keine fremden Dateien dabei sind (häufige Irrgäste:web/src/pages/ChatPage.tsx,backend/internal/db/dbgen/models.go, wenn nicht von dir regeneriert). - Dateien, die von zwei Sitzungen gemeinsam genutzt werden (z. B. ExamPage) und du nur deine Hunks willst: nicht
stash/reset(das würde die Datei unter dem Fuß des anderen Agenten wegziehen). Bearbeite stattdessen den Git-Index über Blobs:git show :path > tmp→ deren Hunks austmpentfernen →SHA=$(git hash-object -w tmp)→git update-index --cacheinfo 100644,$SHA,path. Die Datei auf der Platte bleibt unberührt. - 🚨 cwd des Bash-Tools kann „kleben bleiben“ vom vorherigen
cd .../web(oder Worktree) aus einem vorherigen Aufruf. Dann schlägtgit add backend/...fehl (pathspec did not match) oder staged den falschen Baum. Vor selektivem Stagen:cd <repo-root> &&(oder absolute Pfade verwenden). Prüfe mitgit diff --cached --name-only, ob die relativen Pfade korrekt sind (nicht../backend,src/...). - Nach Deployment:
git statusprüfen — sicherstellen, dass Arbeitsdateien anderer Sitzungen noch intakt sind.
Go-Qualitätsgate (vor Backend-Commit)
Entspricht Pint + PHPStan für Go bei Laravel. Lokal ausführen, bevor Backend-Dateien gestaged werden:
- Format:
cd backend && gofmt -l .→ muss LEER sein. Gibt es Ausgabe →gofmt -w .(automatische Korrektur), das Ergebnis stagen. (Entsprichtpint --test.) - Build + Vet:
go build ./... && go vet ./...grün. - Statische Analyse (Ziel):
golangci-lint run ./...(fasst gofmt/govet/staticcheck/errcheck/ineffassign/unused/misspell zusammen). Entsprichtphpstan analyse. - ⚠️ Status: CI-Gate noch nicht aktiv. Weder
backend/.golangci.ymlnoch.github/workflows/ci.ymlvorhanden, undstaticcheckist veraltet → daher Format+Build+Vet vorerst manuell. Geplant: Erzwingen in CI + vollständige Konfiguration:docs/plan/00-tooling-quality-gate.md. Generierte Code ininternal/db/dbgenvom Lint ausgenommen. - Tests begleiten die Änderungen → Skill afresto-testing (
go test ./...; Integration überspringt ohneTEST_DATABASE_URL).
🔴 Regel #00 — Bedeutungsvolle Arbeit kommt ZUERST in ein GitHub Issue (seit 5. Sep. 2026)
Gilt vor Regel #0 (Branch + PR): Bevor du überhaupt Code anfasst.
Das Ziel ist keine Formalität. Anfragen, die nur im Chat kommen, sind nur zwei Parteien bekannt — das Team kann sie nicht lesen, der Eigentümer hat keinen Ort zum Zuweisen, und das Modul hinterlässt keine Spur, wer was gemacht hat.
Wann Issue PFLICHT ist
Neue Features · Änderungen, die von der Schule gesehen werden · DB-Schema-Änderungen · alles, was eine Produktentscheidung erfordert · Arbeit, die mehr als einen PR umfasst.
Wann NICHT nötig
Klar abgegrenzte Bugs · Text-/Label-Korrekturen · Dokumentation · direkte Folgen aus PR-Review-Kommentaren.
Im Zweifel → Issue anlegen. Ist billig und kann wieder geschlossen werden. Diese Schwelle hält die Regel am Leben: Wenn jede Kleinigkeit zum Issue wird, fangen die Leute an, sie stillschweigend zu umgehen, und einmal umgangen ohne Konsequenz stirbt die Regel.
Was der Agent tut
- Code ausreichend durchsuchen, um „erste Erkenntnisse“ zu sammeln und Blocker zu finden — nicht raten.
- Issue anlegen mit der Vorlage
.github/ISSUE_TEMPLATE/pekerjaan.md, mit Labelmodul:*(das bildet die Aufzeichnung pro Modul), plusbutuh-keputusan, falls etwas offen ist. - AUFHÖREN. Nicht coden, keinen Branch erstellen, sich nicht selbst zuweisen. Zuweisung ist Sache des Eigentümers; das Team braucht Raum für Input/Revision der Idee.
- Melde die Issue-Nummer dem Eigentümer, zusammen mit noch offenen Entscheidungen (falls vorhanden).
🔑 Der Eigentümer darf das überspringen mit einem „Mach jetzt weiter“ — das Issue wird trotzdem als Notiz angelegt, die Arbeit läuft weiter. Diese Regel steuert den Standard, sie ist kein Gefängnis.
Blocker, die erst WÄHREND der Bearbeitung auffallen
Vor dem Weitermachen Bescheid geben: Kommentar im Issue (damit das Team es sieht) und dem Eigentümer mitteilen. Falls die Entscheidung die Arbeit stoppt → Label butuh-keputusan hinzufügen.
Nicht stillschweigend eine eigene Interpretation wählen und weitermachen — das führt zu Arbeit, die „fertig“ ist, aber falsch.
Den Kreis schließen
PR muss Closes #<nummer> im Body enthalten. Das verbindet Issue → PR → Person,
so dass gh issue list --label modul:ujian --state all zur Aufzeichnung wird, wer was in diesem Modul gemacht hat. Ohne diese Zeile ist die Spur unterbrochen.
🔴 Regel #0 — NIEMALS direkt auf main commiten/pushen (Team von 4, seit 25. Jul. 2026)
Alle Arbeit läuft über Branch + Pull Request; nur Reviewer mergen in main (Merge = Deployment-Trigger).
- Vor Beginn:
git checkout main && git pull— falls neue Migrationen da sind, Migrationen zuerst ausführen. - Themenbezogenen Branch erstellen/auschecken, BEVOR Code angefasst wird:
- Feature →
feat/<name>· Bug →bugfix/<name>· Bug mit Issue → neuen Branch + Issue studieren.
- Feature →
- Selektiv commiten →
git push -u origin <branch>(NICHT auf main). - PR zu
mainöffnen:gh pr create, fallsgheingeloggt; sonst dem Benutzer die URL geben:https://github.com/Afresto-Next/next/compare/main...<branch>?expand=1. - Reviewer approved & merged → Deployment. PR niemals selbst mergen.
- Der Abschnitt „Backend pushen / WEB deployen“ unten = Mechanik, die NACH PR-Merge in main läuft (normalerweise von der deployenden Person), keine Erlaubnis, direkt auf main zu pushen.
- Skill-Änderungen ebenfalls per PR (CODEOWNERS).
Schritt-für-Schritt-Anleitung
1. Commit (Bash-Tool = Git Bash / sh, NICHT PowerShell)
- Mehrzeilige Nachricht: Heredoc
git commit -q -F - <<'EOF' … EOF. NICHT@'…'@(das ist PowerShell-Here-String → Betreff wird zu@ ...). - Nachricht beenden:
Co-Authored-By: Claude Opus 4.8 <[email protected]>. - Warnung „CRLF wird durch LF ersetzt“ = normal, ignorieren.
- Commit in deinen Aufgabenbranch (
feat/*·bugfix/*), nichtmain(siehe Regel #0). Benutzer committet auf Anfrage.
🔴 NIEMALS git add -A / git add <dir>/ — hat bereits 3x Benutzerdateien mitgenommen
Dieses Repo enthält viele ungetrackte Benutzer-Arbeitsdateien: docs/bskap/ (Bilder+PDFs),
docs/analisis butir soal/, elibrary/, cloudflare/**/.wrangler/ (Account-Cache). Ein einziges -A
und alles ist dabei.
Tatsächliche Vorfälle: git add docs/ hat BSKAP-PDFs/xls/Bilder mitgenommen (25. Jul & 2. Aug); git add -A
hat 446 Dateien mitgenommen, darunter wrangler-account.json (9. Aug).
git add pfad/zu/datei1 pfad/zu/datei2 # EINE NACH DER ANDEREN, immer
git commit -q -F - <<'EOF'
...
EOF
git show --name-only --format="" HEAD | wc -l # stimmt mit dem überein, was du beabsichtigt hast?
Weicht die Anzahl ab: git reset --soft HEAD~1 && git reset, richtig neu stagen.
Bereits auf eigenen Branch gepusht: git push --force-with-lease ist sicher.
2. DB-Migration — MUSS im TIP-commit sein
.github/workflows/migrate.yml erkennt Migrationen über git diff --name-only HEAD~1 HEAD (nur letzter Commit), läuft nach erfolgreichem „Build & Push Docker Images“ (workflow_run).
- Enthält der Push eine Migration → Migration muss im TIP-commit sein (ein Commit mit Migration+Code = sicher).
- Migration läuft automatisch auf dem Self-Hosted-Runner-VM nach dem Build.
run --rm migratesynchronisiert → Job SCHLÄGT FEHL, wenn Fehler auftreten (nicht still). - Migration prüfen: Tab Actions → „Migrate DB“ grün für diesen Commit (
ghCLI verfügbar:gh run list --workflow="Migrate DB (self-hosted)" --limit 1— Agent kann selbst prüfen, ohne den Benutzer zu bitten). Watchtower tauscht dasapi-Image beim nächsten Poll (~5 Min.), fast immer nach Abschluss der Migration. - VOR dem Pushen der Migration: SQL über psql
BEGIN; … ROLLBACK;in der lokalen DB prüfen (v102 = prod). Siehe Skillafresto-db-change/ Engineering-Fallen §4.
🔴 Migrationsnummer: GENAU VOR DEM MERGE prüfen, nicht beim Branch-Erstellen
Teammitglieder können während dein Branch offen ist eine andere Migrationsserie mergen. Doppelte Nummern lassen
goose in sortAndConnectMigrations PANIKEN — kein SQL-Fehler, also leicht falsch zu deuten.
✅ Keine Datenbeschädigung: Die Panik tritt beim Sammeln der Liste auf, bevor eine einzige ausgeführt wird.
Tatsächlicher Vorfall (9. Aug. 2026): 00138 beim Branch-Erstellen gewählt; beim Merge hatte das Team bereits
00138–00140 verwendet. Erste Korrektur benannte es in 00140 und immer noch Kollision — wieder geraten.
Zählen, nicht raten:
git pull # ZUERST, um Migrationen der Kollegen zu sehen
MAX=$(ls backend/migrations/ | sed 's/_.*//' | sort -n | tail -1)
NEXT=$(printf "%05d" $((10#$MAX + 1)))
ls backend/migrations/ | sed 's/_.*//' | sort | uniq -d # MUSS LEER sein
Falls schon passiert: git mv auf die neue Nummer, kleinen PR, mergen, dann Workflow „Migrate DB (self-hosted)“ aus dem Actions-Tab erneut ausführen.
3. Backend pushen (NACH PR-Merge in main — siehe Regel #0)
PR-Merge → main aktualisiert → GHCR → Watchtower ~5 Min. tauscht das Image. Keine Backend-Änderung = kein Warten auf Watchtower nötig. (Während der Entwicklung: git push -u origin <branch> dann PR — nicht auf main pushen.)
⚠️ NACH Backend-Merge: AUF 502 PRÜFEN (bereits 6x aufgetreten)
Neuerstellen des api/worker-Containers verursacht oft einen Totalausfall — alle Container Up aber
sämtliche Routen 502. Praktisch jeder Backend-Merge ist ein Würfelwurf.
curl -s -o /dev/null -w "%{http_code}\n" --max-time 15 https://next.afresto.co/api/v1/auth/me
# 401 = GESUND (fordert Authentifizierung) · 502 = Ausfall
Bei 502 → Actions → „Ops — API wiederherstellen“ → restart-docker (ohne SSH, <1 Min. Wiederherstellung).
Nicht das neu deployte Feature analysieren: 502 + Container Up = Docker-Daemon-Neustart.
Details: docs/ops-auto-pulih.md.
4. WEB deployen — AUS SAUBEREM WORKTREE (wegen vieler Tab-Agenten)
npm run build liest den GESAMTEN Working Tree → unfertige Arbeiten anderer Sitzungen werden mit veröffentlicht. Also Build und Deployment aus einem Worktree deines Commits:
rm -rf /c/wtd 2>/dev/null; git worktree add --detach /c/wtd <SHA-deines-Commits>
cd /c/wtd && git status --short # muss LEER sein (sauber)
cd /c/wtd/web && npm ci && npm run build
cd /c/wtd/cloudflare/web-worker && npx wrangler deploy
cd <repo-projectTwo> && git worktree remove --force /c/wtd && git worktree prune
- 🔥 Worktree-Pfad MUSS KURZ sein (
/c/wtd) — das Scratchpad + langer DKI-Kalender-PDF-Name → Windows-Fehler „Dateiname zu lang“ beim Checkout. - Bundle bei Bedarf prüfen:
grep -rl "<merkmal-deines-features>" /c/wtd/web/dist/assets/*.js— aber Vorsicht vor falsch Positiven (allgemeine Merkmale könnten von anderen Features stammen). - Cloudflare-Cache der Assets ~20 Sek. → Test mit Strg+F5. (200 aber Größe ~397 Byte = Fallback, nicht echtes Asset.)
5. Mobile OTA — separates Repo, --environment PFLICHT
cd "<repo-mobile>"
npx eas update --branch preview --environment preview --message "…" --non-interactive
- 🔥
--environmentPFLICHT (preview/production) — ohne wird das Update in der falschen Umgebung veröffentlicht/schlägt fehl. - Mobile Repo hat ein GitHub-Remote (
Afresto-Next/afresto-next-mobile) → Regel #0 gilt: Branch → PR → Merge, nicht direkt aufmainpushen. - OTA nur JS/Assets. Änderungen an Native-Modulen → CRASH bei OTA → Neubuild erforderlich.
🔥 eas update sendet den WORKING TREE, nicht den Commit
Genau wie npm run build in Schritt 4 — das gesamte Arbeitsverzeichnis wird gebündelt,
einschließlich unfertiger Arbeiten anderer Sitzungen. Das Zeichen * in der Zeile Commit <hash>*
im Veröffentlichungsergebnis = unsauberer Tree.
UNBEDINGT direkt vor der Veröffentlichung — ausgeben, dann abgleichen mit der Zeile Commit im Ergebnis:
echo "veröffentliche von: $(git branch --show-current) @ $(git log --oneline -1 --format=%h)"
git log --oneline HEAD..origin/main # LEER = keine unerledigte Arbeit anderer zurückgelassen
- Von
mainveröffentlichen, nicht von Feature-Branches. Bei einem Branch gehen bereits gemergte Arbeiten anderer im Channel VERLOREN — OTA ersetzt das gesamte Bundle, es wird nicht geflickt. - Produktion: uncommittete Änderungen anderer Sitzungen kurz weglegen
(
git stash push -- <dateien>…git stash pop). Produktion darf keinen ungeprüften Code enthalten.
🚨 Branches können sich WÄHREND der Arbeit verschieben (viele Tab-Agenten, eine Klon)
Vorfall 20. Aug. 2026: Preview-Veröffentlichung eindeutig von main @ d0938fa; der nächste Befehl
stellte fest, dass HEAD bereits auf einem anderen Branch @ 8126a50 war — eine andere Sitzung hatte im
selben Klon den Branch gewechselt. Produktion lief zeitweise mit Code von 3 PRs länger, ohne einen einzigen Fehler.
➡️ Nicht annehmen, der Branch sei zwischen Befehlen gleich geblieben. Jedes Mal neu ausgeben (obiger Befehl). ➡️ Eigentliche Prävention: OTA aus WORKTREE, genau wie Web-Deployment in Schritt 4 — andere Sitzungen können den Branch nicht verschieben:
rm -rf /c/wtm 2>/dev/null; git worktree add --detach /c/wtm <SHA-deines-Commits>
cd /c/wtm && git status --short # muss LEER sein
npm ci && npx eas update --branch <ch> --environment <env> --message "…" --non-interactive
cd <repo-mobile> && git worktree remove --force /c/wtm && git worktree prune
🪤 Worktree-Pfad kurz (/c/wtm) — gleicher Grund wie Schritt 4.
🪤 npm ci im Worktree braucht Zeit; für kleine OTA-Änderungen kann man im Hauptklon bleiben, solange
die obige Branch+Commit-Prüfung durchgeführt wird und das Ergebnis übereinstimmt.
🏢 GHCR & GitHub-Organisation — Repo jetzt in der Org Afresto-Next
Repos (next, afresto-next-mobile, afresto-claude-skills) sind von ristology/* → Org Afresto-Next umgezogen (20. Jul. 2026). Vollständiges Runbook zum Transfer: docs/runbook-transfer-next-org.md.
- 🚨 Repo-Transfer in Org = GHCR-Pfad ändert den Owner → Watchtower BLEIBT STILL STEHEN. CI taggt Images als
ghcr.io/${GITHUB_REPOSITORY_OWNER}/afresto-next-{api,web,migrate}— Owner folgt dem Repo-Eigentümer. Seit dem Transfer ist der Org-Name der Owner. Watchtower, der noch im alten Pfad läuft, zieht keine neuen Images mehr. Behoben durchdocker service update --image …auf dem Server (Details im Runbook). Die Änderung der CI-Tag-Struktur (${GITHUB_REPOSITORY_OWNER}→ dynamisch) ist nicht rückgängig zu machen — selbst wenn das Repo in eine andere Org/eigenes Konto wechselt, passt sich der Pfad automatisch an. - ⚠️ Org-Account hat Login-Limit: 1 Sitzung → Ein anderer Entwickler, der in der Org arbeitet, kann dich abmelden.
gh auth statusprüfen, bevor es wichtig wird.
🔴 Regel der letzten Ausfahrt — Möglichst ALLE Schritte in EINEM Befehl
Jeder Befehl ist eine Gelegenheit für eine andere Sitzung, git status zu verändern, den Branch zu verschieben, git add für andere Dateien mitten drin auszuführen. Kein Teilbefehl ist vor dem nächsten sicher.
&&-Ketten, Heredocs, Subshells. Wenn du cd musst, mach es in einer Subshell (cd … && …).
Schlecht (anfällig für Unterbrechungen):
git checkout main
git pull
# → Unterbrechung hier: andere Sitzung erstellt Datei X
git checkout -b feat/my-feature
Besser:
git checkout main && git pull && git checkout -b feat/my-feature
# alles in einer Zeile — keine Unterbrechung
Am besten — Minimales Blocken:
- Vor Selektivem Stagen:
cd <repo-root> && git add …(sicherer cwd) - Vor
gh pr create:git push -u origin <branch>im selben Befehl - Vor
npm run build:git worktree addundcd worktree && …– Worktree ist immun gegen Branch-Wechsel
Alte Vorfälle (für Agenten-Lernkontext)
| Datum | Vorfall | Auswirkung |
|---|---|---|
| 25 Jul 2026 | git add docs/ zog BSKAP-PDFs mit | 15 unerwünschte Dateien im Commit |
| 9 Aug 2026 | git mv einer Migration in Kollisionsnummer (00140) — Behebung im selben PR | 2 CI-Fehlschläge + manuelles erneutes Ausführen der Aktion |
| 9 Aug 2026 | git add -A zog wrangler-account.json und 445 andere Dateien mit | Commit zurückgesetzt, Entschuldigung ans Team |
| 20 Aug 2026 | OTA-Veröffentlichung von main, aber nächster Befehl war auf anderem Branch (andere Sitzung) | Produktion 30 Min. mit Code von 3 PRs zu weit — ohne Fehler, also unbemerkt |
Letzte Worte
Jeder Befehl kann die letzte Aktion sein, bevor eine andere Sitzung dazwischenfunkt. && verhindert, dass die nächste Zeile auf falschem Stand läuft. Worktrees machen Builds immun. Branch-Prüfungen machen OTA immun. Die 502-Prüfung macht Backend-Merges immun. Benutzerfreigabe = Firewall.
Wenn das Rückgängigmachen eines Fehlers mehr als 5 Minuten dauert, wurde eine Regel verletzt.