Am 2026-08-30 hat ci.yml ein paths-ignore bekommen: Inhaltsänderungen lösen kein CI mehr aus. Das war richtig — publish fasst bei jedem Ingest kb/, raw/ und work/ an, und dafür die gesamte Suite zu fahren ist Lärm.
Es hat aber eine Lücke aufgemacht, und dieses Issue schließt sie. Vorher lief lint --fail-on-error bei jeder Inhaltsänderung mit. Jetzt läuft es nur noch, wenn jemand den Stack anfasst. Ein Korpus, der durch eine Reihe von Publishes langsam inkonsistent wird, fällt niemandem mehr auf.
Dazu kommt Drift, die ohne jeden Commit entsteht: sources coverage meldet eine unabgedeckte Datei, sobald etwas in raw/ liegt, das keine Source-Seite hat; version check sagt erst dann etwas über ein neues Release, wenn jemand fragt.
Was der Lauf tun soll
Ein neuer Workflow .gitea/workflows/nightly.yml, on: schedule, ohne Push-Trigger:
tools/wikitool doctor
tools/wikitool docs verify
tools/wikitool instructions verify
tools/wikitool lint --fail-on-error
tools/wikitool sources coverage
tools/wikitool migrate status
lint ist der eigentliche Grund; der Rest kostet Sekunden und schließt den Kreis.
Bewusst nicht drin:migrate verify --from <rev>. Das braucht eine sinnvolle Vergleichsrevision, und „gestern" ist keine — der Invarianten-Diff beantwortet die Frage „hat diese Migration etwas verloren", nicht „hat sich seit gestern etwas geändert". Eine Änderung an einer Seite ist im Normalbetrieb das gewünschte Verhalten, kein Befund.
Die Form, die dieser Runner braucht
Nicht neu herleiten — sie ist erarbeitet und in ci.yml dokumentiert:
nodejs ist nicht für uns, sondern für act_runner: es führt JavaScript-Actions wie checkout mit nodeim Job-Container aus. Fehlt es, stirbt der Job mit exec: "node": executable file not found in $PATH, exit 127, bevor irgendein eigener Schritt läuft. Das hat sechs fehlgeschlagene Runs gekostet (46–51).
Ebenso setzen: WIKITOOL_SESSION_ID: nightly-${{ github.run_id }} (sonst fällt das Iteration-Budget-Gate auf die Parent-PID zurück) und WIKI_TRACE_DIR: /tmp/wikitool-trace (Trace außerhalb des Checkouts, damit die Ignore-Canary-Prüfungen einen sauberen Arbeitsbaum sehen).
Zuerst zu klären
Ob dieser Gitea-Stand on: schedule überhaupt auswertet. Das ist die Voraussetzung für alles Weitere und in keiner Wiki-Seite belegt. Gitea wertet Schedules nur auf dem Default-Branch aus — der Workflow muss also auf main liegen, ein Test-Branch beweist nichts. Erster Schritt: minimaler Schedule-Workflow, einen Tag beobachten, actions_run_read/list_runs prüfen.
Falls nein: Fallback ist ein externer Trigger (Cron auf hephaestus gegen die Actions-API, oder workflow_dispatch von außen). Dann gehört die Entscheidung dokumentiert, nicht der Workflow gelöscht.
Akzeptanzkriterien
Der Lauf ist grün und läuft tatsächlich ohne Push.
Ein absichtlich gebrochener Korpus (ein [[Ziel]], das nicht auflöst) macht den Lauf rot — sonst ist er Dekoration.
Fehlschlag ist sichtbar, ohne dass jemand die Actions-Seite besucht. Was hier das richtige Mittel ist (Gitea-Notification, Issue, Push-Nachricht), ist offen und Teil des Issues.
Kein Version-Bump nötig: .gitea/ steht außerhalb des Version-Gates (instructions/dev/stack-dev/SKILL.md).
Hinweis zur Wechselwirkung
Wenn sich herausstellt, dass Giteas paths-ignorenicht so greift wie gedacht (siehe das separate Issue dazu), verliert dieser Lauf einen Teil seiner Begründung — dann liefe lint ja wieder bei jedem Publish mit. Die Drift ohne Commit bliebe trotzdem unbeobachtet. Reihenfolge ist also egal, aber der Zusammenhang gehört gesehen.
## Warum das jetzt gebraucht wird
Am 2026-08-30 hat `ci.yml` ein `paths-ignore` bekommen: **Inhaltsänderungen lösen kein CI mehr aus.** Das war richtig — `publish` fasst bei jedem Ingest `kb/`, `raw/` und `work/` an, und dafür die gesamte Suite zu fahren ist Lärm.
Es hat aber eine Lücke aufgemacht, und dieses Issue schließt sie. Vorher lief `lint --fail-on-error` bei jeder Inhaltsänderung mit. Jetzt läuft es nur noch, wenn jemand den Stack anfasst. Ein Korpus, der durch eine Reihe von Publishes langsam inkonsistent wird, fällt niemandem mehr auf.
Dazu kommt Drift, die *ohne jeden Commit* entsteht: `sources coverage` meldet eine unabgedeckte Datei, sobald etwas in `raw/` liegt, das keine Source-Seite hat; `version check` sagt erst dann etwas über ein neues Release, wenn jemand fragt.
## Was der Lauf tun soll
Ein neuer Workflow `.gitea/workflows/nightly.yml`, `on: schedule`, ohne Push-Trigger:
```
tools/wikitool doctor
tools/wikitool docs verify
tools/wikitool instructions verify
tools/wikitool lint --fail-on-error
tools/wikitool sources coverage
tools/wikitool migrate status
```
`lint` ist der eigentliche Grund; der Rest kostet Sekunden und schließt den Kreis.
**Bewusst nicht drin:** `migrate verify --from <rev>`. Das braucht eine sinnvolle Vergleichsrevision, und „gestern" ist keine — der Invarianten-Diff beantwortet die Frage „hat *diese Migration* etwas verloren", nicht „hat sich seit gestern etwas geändert". Eine Änderung an einer Seite ist im Normalbetrieb das gewünschte Verhalten, kein Befund.
## Die Form, die dieser Runner braucht
Nicht neu herleiten — sie ist erarbeitet und in `ci.yml` dokumentiert:
```yaml
runs-on: linux-docker
container:
image: debian:trixie-slim
steps:
- name: System dependencies
run: |
apt-get update -qq
apt-get install -y --no-install-recommends python3 python3-venv git nodejs ripgrep ca-certificates
- uses: actions/checkout@v7
```
`nodejs` ist **nicht** für uns, sondern für act_runner: es führt JavaScript-Actions wie `checkout` mit `node` *im Job-Container* aus. Fehlt es, stirbt der Job mit `exec: "node": executable file not found in $PATH`, exit 127, bevor irgendein eigener Schritt läuft. Das hat sechs fehlgeschlagene Runs gekostet (46–51).
Ebenso setzen: `WIKITOOL_SESSION_ID: nightly-${{ github.run_id }}` (sonst fällt das Iteration-Budget-Gate auf die Parent-PID zurück) und `WIKI_TRACE_DIR: /tmp/wikitool-trace` (Trace außerhalb des Checkouts, damit die Ignore-Canary-Prüfungen einen sauberen Arbeitsbaum sehen).
## Zuerst zu klären
- [ ] **Ob dieser Gitea-Stand `on: schedule` überhaupt auswertet.** Das ist die Voraussetzung für alles Weitere und in keiner Wiki-Seite belegt. Gitea wertet Schedules nur auf dem Default-Branch aus — der Workflow muss also auf `main` liegen, ein Test-Branch beweist nichts. Erster Schritt: minimaler Schedule-Workflow, einen Tag beobachten, `actions_run_read`/`list_runs` prüfen.
- [ ] Falls nein: Fallback ist ein externer Trigger (Cron auf hephaestus gegen die Actions-API, oder `workflow_dispatch` von außen). Dann gehört die Entscheidung dokumentiert, nicht der Workflow gelöscht.
## Akzeptanzkriterien
- [ ] Der Lauf ist grün und läuft tatsächlich ohne Push.
- [ ] Ein absichtlich gebrochener Korpus (ein `[[Ziel]]`, das nicht auflöst) macht den Lauf rot — sonst ist er Dekoration.
- [ ] Fehlschlag ist sichtbar, ohne dass jemand die Actions-Seite besucht. Was hier das richtige Mittel ist (Gitea-Notification, Issue, Push-Nachricht), ist offen und Teil des Issues.
- [ ] Kein Version-Bump nötig: `.gitea/` steht außerhalb des Version-Gates (`instructions/dev/stack-dev/SKILL.md`).
## Hinweis zur Wechselwirkung
Wenn sich herausstellt, dass Giteas `paths-ignore` **nicht** so greift wie gedacht (siehe das separate Issue dazu), verliert dieser Lauf einen Teil seiner Begründung — dann liefe `lint` ja wieder bei jedem Publish mit. Die Drift ohne Commit bliebe trotzdem unbeobachtet. Reihenfolge ist also egal, aber der Zusammenhang gehört gesehen.
Die Vorfrage aus dem Hinweis am Ende ist beantwortet:#11 ist belegt, paths-ignore greift. Commit f916376 — ein reiner Inhalts-Publish — hat keinen Run erzeugt, die Stack-Commits davor und danach je zwei.
Für dieses Issue heißt das: die Lücke ist real und nicht kleiner als angenommen. lint --fail-on-error läuft bei einem Inhalts-Publish tatsächlich nicht mehr mit, ein durch eine Reihe von Publishes langsam inkonsistent werdender Korpus fällt also niemandem auf. Die Begründung des nächtlichen Laufs steht damit vollständig.
Die Vorfrage im Issue selbst — ob dieser Gitea-Stand on: schedule überhaupt auswertet — ist davon unberührt und weiterhin offen. Sie bleibt der erste Schritt.
Ein Detail, das seit 1.2.0 den Workflow-Entwurf betrifft: lint schreibt jetzt immer einen Bericht nach reports/Lint Report <date>.md und druckt nur die Abschnitte mit Befunden. Für den nächtlichen Lauf ändert das nichts an der Zeile (lint --fail-on-error bleibt), aber reports/ ist gitignored — der Bericht ist also nach dem Job weg, sofern er nicht als Artefakt eingesammelt wird. Falls das gewünscht ist: actions/upload-artifact@v3, nicht v4 (siehe #10).
**Die Vorfrage aus dem Hinweis am Ende ist beantwortet:** #11 ist belegt, `paths-ignore` greift. Commit `f916376` — ein reiner Inhalts-Publish — hat keinen Run erzeugt, die Stack-Commits davor und danach je zwei.
Für dieses Issue heißt das: die Lücke ist real und nicht kleiner als angenommen. `lint --fail-on-error` läuft bei einem Inhalts-Publish tatsächlich nicht mehr mit, ein durch eine Reihe von Publishes langsam inkonsistent werdender Korpus fällt also niemandem auf. Die Begründung des nächtlichen Laufs steht damit vollständig.
Die Vorfrage im Issue selbst — **ob dieser Gitea-Stand `on: schedule` überhaupt auswertet** — ist davon unberührt und weiterhin offen. Sie bleibt der erste Schritt.
Ein Detail, das seit 1.2.0 den Workflow-Entwurf betrifft: `lint` schreibt jetzt immer einen Bericht nach `reports/Lint Report <date>.md` und druckt nur die Abschnitte mit Befunden. Für den nächtlichen Lauf ändert das nichts an der Zeile (`lint --fail-on-error` bleibt), aber `reports/` ist gitignored — der Bericht ist also nach dem Job weg, sofern er nicht als Artefakt eingesammelt wird. Falls das gewünscht ist: `actions/upload-artifact@v3`, nicht v4 (siehe #10).
.gitea/workflows/nightly.yml liegt seit 49bd7d4 auf main.
Was gebaut wurde
on: schedule (17 3 * * *, UTC — Gitea wertet Cron in UTC aus) plus workflow_dispatch, kein Push-Trigger. Die Runner-Form ist unverändert aus ci.yml übernommen, nicht neu hergeleitet: linux-docker, debian:trixie-slim, apt nodejsvor dem Checkout, actions/checkout@v7. WIKITOOL_SESSION_ID: nightly-${{ github.run_id }} und WIKI_TRACE_DIR: /tmp/wikitool-trace sind gesetzt.
Die sechs Kommandos wie im Issue, in vier Schritten gruppiert, damit ein roter Lauf schon in der Schrittliste zeigt, welche Ebene gebrochen ist. migrate verify --from <rev> ist wie vorgesehen nicht drin.
Ein Zusatz, der im Entwurf fehlte:WIKI_AUTHOR: Nightly. doctor gibt FAIL author, wenn weder $WIKI_AUTHOR noch git config user.name auflöst, und ein Container hat keine git-Konfiguration. Der Lauf schreibt keine Seite, die Identität ist also Formsache — aber ohne sie wäre doctor unabhängig vom Zustand der Instanz immer rot und damit wertlos. Dieselbe Lücke wie auf der Testseite (#8), an derselben Stelle geschlossen.
Kein Artefakt-Upload für den Lint-Bericht.reports/ ist gitignored, der Bericht ist nach dem Job weg. Der Kommentar oben schlug upload-artifact@v3 vor; ich habe es weggelassen, weil lint --fail-on-error die Befunde bereits ins Job-Log druckt und ein Artefakt eine zweite Stelle wäre, an der man nachsieht. Nachrüstbar, wenn sich zeigt, dass das Log nicht reicht.
Fehlersichtbarkeit — entschieden
Entscheidung des Nutzers: Gitea-eigene Notification, kein Meldeschritt im Workflow. Ein Job, der sein eigenes Issue anlegt, braucht einen Actions-Token mit issues: write und eine Dedup-Regel — mehr Maschinerie, als ein roter Lauf ohnehin trägt. Der Grund steht im Kopf des Workflows, damit die Auslassung nicht als Vergessen gelesen wird.
Akzeptanzkriterien
Ein absichtlich gebrochener Korpus macht den Lauf rot. Lokal belegt: ein nicht auflösendes [[Diese Seite Existiert Nicht]] in Nathan.md → lint --fail-on-error exit 1; nach dem Zurücknehmen exit 0. Der Lauf ist keine Dekoration.
Kein Version-Bump nötig — .gitea/ steht außerhalb des Version-Gates. (Der Commit trägt trotzdem 1.7.2, aber wegen #20/#22.)
Fehlschlag ist ohne Besuch der Actions-Seite sichtbar — über Giteas Run-Notification, wie entschieden.
Der Lauf ist grün und läuft tatsächlich ohne Push. Zwei Hälften, beide noch offen:
Was noch aussteht
Ob dieser Gitea-Stand on: schedule auswertet — die Vorfrage des Issues, weiterhin unbeantwortet. Neu belegt ist nur die Voraussetzung: die Instanz läuft Gitea 1.26.1, also weit hinter der 1.20, in der Actions-Schedules eingeführt wurden. Das macht es plausibel, nicht wahr. Der Workflow liegt jetzt auf dem Default-Branch, wo Gitea Schedules überhaupt erst auswertet — die Beobachtung kann also ab heute Nacht laufen. Prüfen mit list_runs auf ein Event schedule mit Datum von morgen.
Ob der Job durchläuft — per workflow_dispatch angestoßen (Run 83). Ergebnis steht bei diesem Kommentar noch aus; der Runner arbeitet die drei Läufe auf 49bd7d4 seriell ab. Das beweist den Job, nicht den Zeitplan.
Das Issue bleibt deshalb offen. Es schließt, wenn ein Lauf mit event: schedule in der Liste steht.
`.gitea/workflows/nightly.yml` liegt seit `49bd7d4` auf `main`.
## Was gebaut wurde
`on: schedule` (`17 3 * * *`, UTC — Gitea wertet Cron in UTC aus) plus `workflow_dispatch`, kein Push-Trigger. Die Runner-Form ist unverändert aus `ci.yml` übernommen, nicht neu hergeleitet: `linux-docker`, `debian:trixie-slim`, apt `nodejs` **vor** dem Checkout, `actions/checkout@v7`. `WIKITOOL_SESSION_ID: nightly-${{ github.run_id }}` und `WIKI_TRACE_DIR: /tmp/wikitool-trace` sind gesetzt.
Die sechs Kommandos wie im Issue, in vier Schritten gruppiert, damit ein roter Lauf schon in der Schrittliste zeigt, welche Ebene gebrochen ist. `migrate verify --from <rev>` ist wie vorgesehen nicht drin.
**Ein Zusatz, der im Entwurf fehlte:** `WIKI_AUTHOR: Nightly`. `doctor` gibt `FAIL author`, wenn weder `$WIKI_AUTHOR` noch `git config user.name` auflöst, und ein Container hat keine git-Konfiguration. Der Lauf schreibt keine Seite, die Identität ist also Formsache — aber ohne sie wäre `doctor` unabhängig vom Zustand der Instanz immer rot und damit wertlos. Dieselbe Lücke wie auf der Testseite (#8), an derselben Stelle geschlossen.
**Kein Artefakt-Upload für den Lint-Bericht.** `reports/` ist gitignored, der Bericht ist nach dem Job weg. Der Kommentar oben schlug `upload-artifact@v3` vor; ich habe es weggelassen, weil `lint --fail-on-error` die Befunde bereits ins Job-Log druckt und ein Artefakt eine zweite Stelle wäre, an der man nachsieht. Nachrüstbar, wenn sich zeigt, dass das Log nicht reicht.
## Fehlersichtbarkeit — entschieden
Entscheidung des Nutzers: **Gitea-eigene Notification**, kein Meldeschritt im Workflow. Ein Job, der sein eigenes Issue anlegt, braucht einen Actions-Token mit `issues: write` und eine Dedup-Regel — mehr Maschinerie, als ein roter Lauf ohnehin trägt. Der Grund steht im Kopf des Workflows, damit die Auslassung nicht als Vergessen gelesen wird.
## Akzeptanzkriterien
- [x] **Ein absichtlich gebrochener Korpus macht den Lauf rot.** Lokal belegt: ein nicht auflösendes `[[Diese Seite Existiert Nicht]]` in `Nathan.md` → `lint --fail-on-error` exit 1; nach dem Zurücknehmen exit 0. Der Lauf ist keine Dekoration.
- [x] Kein Version-Bump nötig — `.gitea/` steht außerhalb des Version-Gates. (Der Commit trägt trotzdem 1.7.2, aber wegen #20/#22.)
- [x] Fehlschlag ist ohne Besuch der Actions-Seite sichtbar — über Giteas Run-Notification, wie entschieden.
- [ ] **Der Lauf ist grün und läuft tatsächlich ohne Push.** Zwei Hälften, beide noch offen:
### Was noch aussteht
**Ob dieser Gitea-Stand `on: schedule` auswertet** — die Vorfrage des Issues, weiterhin unbeantwortet. Neu belegt ist nur die Voraussetzung: die Instanz läuft **Gitea 1.26.1**, also weit hinter der 1.20, in der Actions-Schedules eingeführt wurden. Das macht es plausibel, nicht wahr. Der Workflow liegt jetzt auf dem Default-Branch, wo Gitea Schedules überhaupt erst auswertet — die Beobachtung kann also ab heute Nacht laufen. Prüfen mit `list_runs` auf ein Event `schedule` mit Datum von morgen.
**Ob der Job durchläuft** — per `workflow_dispatch` angestoßen (Run 83). Ergebnis steht bei diesem Kommentar noch aus; der Runner arbeitet die drei Läufe auf `49bd7d4` seriell ab. Das beweist den Job, nicht den Zeitplan.
Das Issue bleibt deshalb offen. Es schließt, wenn ein Lauf mit `event: schedule` in der Liste steht.
Nachtrag: der Job läuft, nachdem der erste Versuch zu Recht rot war.
Was der erste Lauf gefunden hat
Run 83 (workflow_dispatch auf 49bd7d4) brach im Schritt doctor mit exit 1 ab. Zwei FAILs, beide kein Wiki-Drift, sondern eine Lücke im Workflow-Entwurf:
FAIL git-identity: `git config user.name` is not set
FAIL skills: No skills published yet
doctor fragt, ob eine arbeitsfähige Instanz korrekt konfiguriert ist. Ein blanker Checkout ist noch keine — er ist der frische Clone, den instructions/bootstrap.md beschreibt: .agents/skills/ und .claude/skills/ sind generiert und bewusst nicht committet, also gibt es sie erst nach instructions sync. Und ein Container hat keine git-Identität; WIKI_AUTHOR deckte die author-Prüfung ab, git-identity ist eine zweite und blieb offen.
instructions verify wäre am selben Punkt gescheitert (es prüft ebenfalls „published copies match their source"), hat den Schritt aber nicht mehr erreicht — der Fehlschlag hat den echten Prüfteil des Laufs komplett verdeckt. Genau das ist der Wert dieses ersten Fehlschlags: er stand vor dem, was der Lauf eigentlich beobachten soll.
Korrektur
397d8af: im Schritt Tool environment jetzt git-Identität setzen und tools/wikitool instructions sync ausführen, bevor doctor läuft. WIKI_AUTHOR ist entfallen — die git-Identität deckt beide Prüfungen mit einem Mechanismus ab, statt mit zweien. Die Begründung steht als Kommentar am Schritt, damit der Bootstrap nicht als überflüssiger Ballast wieder herausgekürzt wird.
Ergebnis
Run 85 (workflow_dispatch auf 397d8af): alle sieben Schritte success.
Schritt
Ergebnis
System dependencies
success
actions/checkout@v7
success
Tool environment (inkl. Bootstrap)
success
doctor
success
docs verify + instructions verify
success
lint --fail-on-error
success
sources coverage + migrate status
success
Die Runner-Form aus ci.yml trägt also auch hier; CI-Lauf 84 auf demselben Commit ist ebenfalls grün.
Akzeptanzkriterien
Ein absichtlich gebrochener Korpus macht den Lauf rot — lokal belegt (lint --fail-on-error exit 1 / exit 0 nach Rücknahme).
Fehlschlag ist ohne Actions-Seite sichtbar — Giteas Run-Notification, wie entschieden.
Kein Version-Bump nötig.
Der Lauf ist grün und läuft tatsächlich ohne Push. Erste Hälfte erledigt: der Job ist grün (Run 85). Zweite Hälfte offen: dass er ohne Push startet.
Was allein noch offen ist
Ob dieser Gitea-Stand on: schedule auswertet. Beide Läufe bisher waren workflow_dispatch — sie beweisen den Job, nicht den Zeitplan. Der Workflow liegt jetzt auf main, wo Gitea Schedules überhaupt erst auswertet, und der Cron steht auf 17 3 * * * UTC. Prüfung morgen: list_runs auf einen Eintrag mit "event":"schedule". Bleibt der aus, greift der Fallback aus dem Issue — externer Trigger gegen die Actions-API, mit dokumentierter Entscheidung statt gelöschtem Workflow.
Nachtrag: der Job läuft, nachdem der erste Versuch zu Recht rot war.
## Was der erste Lauf gefunden hat
Run 83 (`workflow_dispatch` auf `49bd7d4`) brach im Schritt `doctor` mit exit 1 ab. Zwei FAILs, beide **kein** Wiki-Drift, sondern eine Lücke im Workflow-Entwurf:
```
FAIL git-identity: `git config user.name` is not set
FAIL skills: No skills published yet
```
`doctor` fragt, ob eine *arbeitsfähige Instanz* korrekt konfiguriert ist. Ein blanker Checkout ist noch keine — er ist der frische Clone, den `instructions/bootstrap.md` beschreibt: `.agents/skills/` und `.claude/skills/` sind generiert und bewusst nicht committet, also gibt es sie erst nach `instructions sync`. Und ein Container hat keine git-Identität; `WIKI_AUTHOR` deckte die `author`-Prüfung ab, `git-identity` ist eine zweite und blieb offen.
`instructions verify` wäre am selben Punkt gescheitert (es prüft ebenfalls „published copies match their source"), hat den Schritt aber nicht mehr erreicht — der Fehlschlag hat den echten Prüfteil des Laufs komplett verdeckt. Genau das ist der Wert dieses ersten Fehlschlags: er stand vor dem, was der Lauf eigentlich beobachten soll.
## Korrektur
`397d8af`: im Schritt *Tool environment* jetzt git-Identität setzen und `tools/wikitool instructions sync` ausführen, bevor `doctor` läuft. `WIKI_AUTHOR` ist entfallen — die git-Identität deckt beide Prüfungen mit einem Mechanismus ab, statt mit zweien. Die Begründung steht als Kommentar am Schritt, damit der Bootstrap nicht als überflüssiger Ballast wieder herausgekürzt wird.
## Ergebnis
**Run 85** (`workflow_dispatch` auf `397d8af`): alle sieben Schritte `success`.
| Schritt | Ergebnis |
|---|---|
| System dependencies | success |
| `actions/checkout@v7` | success |
| Tool environment (inkl. Bootstrap) | success |
| `doctor` | success |
| `docs verify` + `instructions verify` | success |
| `lint --fail-on-error` | success |
| `sources coverage` + `migrate status` | success |
Die Runner-Form aus `ci.yml` trägt also auch hier; CI-Lauf 84 auf demselben Commit ist ebenfalls grün.
## Akzeptanzkriterien
- [x] Ein absichtlich gebrochener Korpus macht den Lauf rot — lokal belegt (`lint --fail-on-error` exit 1 / exit 0 nach Rücknahme).
- [x] Fehlschlag ist ohne Actions-Seite sichtbar — Giteas Run-Notification, wie entschieden.
- [x] Kein Version-Bump nötig.
- [ ] **Der Lauf ist grün und läuft tatsächlich ohne Push.** Erste Hälfte erledigt: der Job ist grün (Run 85). Zweite Hälfte offen: dass er *ohne Push* startet.
## Was allein noch offen ist
Ob dieser Gitea-Stand `on: schedule` auswertet. Beide Läufe bisher waren `workflow_dispatch` — sie beweisen den Job, nicht den Zeitplan. Der Workflow liegt jetzt auf `main`, wo Gitea Schedules überhaupt erst auswertet, und der Cron steht auf `17 3 * * *` UTC. Prüfung morgen: `list_runs` auf einen Eintrag mit `"event":"schedule"`. Bleibt der aus, greift der Fallback aus dem Issue — externer Trigger gegen die Actions-API, mit dokumentierter Entscheidung statt gelöschtem Workflow.
Die letzte offene Vorfrage ist beantwortet: Run 90 — event: "schedule", head_branch: main, head_sha905f1f2 — ist am 2026-09-01 um 03:17:35 UTC gestartet und um 03:17:38 UTC durchgelaufen, exakt zur konfigurierten Cron-Zeit 17 3 * * * UTC. Alle sieben Schritte erfolgreich: System dependencies, actions/checkout@v7, Tool environment, doctor, docs verify/instructions verify, lint --fail-on-error, sources coverage/migrate status.
Damit ist belegt: dieser Gitea-Stand (1.26.1) wertet on: schedule auf dem Default-Branch tatsächlich aus. Der im Issue vorgesehene Fallback (externer Trigger von hephaestus gegen die Actions-API) ist nicht nötig.
Akzeptanzkriterien — jetzt vollständig
Ein absichtlich gebrochener Korpus macht den Lauf rot (lokal belegt, siehe Kommentar oben)
Kein Version-Bump nötig — .gitea/ steht außerhalb des Version-Gates
Fehlschlag ist ohne Besuch der Actions-Seite sichtbar — Giteas Run-Notification, wie entschieden
Der Lauf ist grün und läuft tatsächlich ohne Push — Run 85 bewies den Job (workflow_dispatch), Run 90 beweist jetzt den Zeitplan (schedule)
Schließe das Issue.
Die letzte offene Vorfrage ist beantwortet: **Run 90** — `event: "schedule"`, `head_branch: main`, `head_sha` `905f1f2` — ist am 2026-09-01 um 03:17:35 UTC gestartet und um 03:17:38 UTC durchgelaufen, exakt zur konfigurierten Cron-Zeit `17 3 * * *` UTC. Alle sieben Schritte erfolgreich: System dependencies, `actions/checkout@v7`, Tool environment, `doctor`, `docs verify`/`instructions verify`, `lint --fail-on-error`, `sources coverage`/`migrate status`.
Damit ist belegt: dieser Gitea-Stand (1.26.1) wertet `on: schedule` auf dem Default-Branch tatsächlich aus. Der im Issue vorgesehene Fallback (externer Trigger von hephaestus gegen die Actions-API) ist nicht nötig.
## Akzeptanzkriterien — jetzt vollständig
- [x] Ein absichtlich gebrochener Korpus macht den Lauf rot (lokal belegt, siehe Kommentar oben)
- [x] Kein Version-Bump nötig — `.gitea/` steht außerhalb des Version-Gates
- [x] Fehlschlag ist ohne Besuch der Actions-Seite sichtbar — Giteas Run-Notification, wie entschieden
- [x] **Der Lauf ist grün und läuft tatsächlich ohne Push** — Run 85 bewies den Job (`workflow_dispatch`), Run 90 beweist jetzt den Zeitplan (`schedule`)
Schließe das Issue.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Warum das jetzt gebraucht wird
Am 2026-08-30 hat
ci.ymleinpaths-ignorebekommen: Inhaltsänderungen lösen kein CI mehr aus. Das war richtig —publishfasst bei jedem Ingestkb/,raw/undwork/an, und dafür die gesamte Suite zu fahren ist Lärm.Es hat aber eine Lücke aufgemacht, und dieses Issue schließt sie. Vorher lief
lint --fail-on-errorbei jeder Inhaltsänderung mit. Jetzt läuft es nur noch, wenn jemand den Stack anfasst. Ein Korpus, der durch eine Reihe von Publishes langsam inkonsistent wird, fällt niemandem mehr auf.Dazu kommt Drift, die ohne jeden Commit entsteht:
sources coveragemeldet eine unabgedeckte Datei, sobald etwas inraw/liegt, das keine Source-Seite hat;version checksagt erst dann etwas über ein neues Release, wenn jemand fragt.Was der Lauf tun soll
Ein neuer Workflow
.gitea/workflows/nightly.yml,on: schedule, ohne Push-Trigger:lintist der eigentliche Grund; der Rest kostet Sekunden und schließt den Kreis.Bewusst nicht drin:
migrate verify --from <rev>. Das braucht eine sinnvolle Vergleichsrevision, und „gestern" ist keine — der Invarianten-Diff beantwortet die Frage „hat diese Migration etwas verloren", nicht „hat sich seit gestern etwas geändert". Eine Änderung an einer Seite ist im Normalbetrieb das gewünschte Verhalten, kein Befund.Die Form, die dieser Runner braucht
Nicht neu herleiten — sie ist erarbeitet und in
ci.ymldokumentiert:nodejsist nicht für uns, sondern für act_runner: es führt JavaScript-Actions wiecheckoutmitnodeim Job-Container aus. Fehlt es, stirbt der Job mitexec: "node": executable file not found in $PATH, exit 127, bevor irgendein eigener Schritt läuft. Das hat sechs fehlgeschlagene Runs gekostet (46–51).Ebenso setzen:
WIKITOOL_SESSION_ID: nightly-${{ github.run_id }}(sonst fällt das Iteration-Budget-Gate auf die Parent-PID zurück) undWIKI_TRACE_DIR: /tmp/wikitool-trace(Trace außerhalb des Checkouts, damit die Ignore-Canary-Prüfungen einen sauberen Arbeitsbaum sehen).Zuerst zu klären
on: scheduleüberhaupt auswertet. Das ist die Voraussetzung für alles Weitere und in keiner Wiki-Seite belegt. Gitea wertet Schedules nur auf dem Default-Branch aus — der Workflow muss also aufmainliegen, ein Test-Branch beweist nichts. Erster Schritt: minimaler Schedule-Workflow, einen Tag beobachten,actions_run_read/list_runsprüfen.workflow_dispatchvon außen). Dann gehört die Entscheidung dokumentiert, nicht der Workflow gelöscht.Akzeptanzkriterien
[[Ziel]], das nicht auflöst) macht den Lauf rot — sonst ist er Dekoration..gitea/steht außerhalb des Version-Gates (instructions/dev/stack-dev/SKILL.md).Hinweis zur Wechselwirkung
Wenn sich herausstellt, dass Giteas
paths-ignorenicht so greift wie gedacht (siehe das separate Issue dazu), verliert dieser Lauf einen Teil seiner Begründung — dann liefelintja wieder bei jedem Publish mit. Die Drift ohne Commit bliebe trotzdem unbeobachtet. Reihenfolge ist also egal, aber der Zusammenhang gehört gesehen.Die Vorfrage aus dem Hinweis am Ende ist beantwortet: #11 ist belegt,
paths-ignoregreift. Commitf916376— ein reiner Inhalts-Publish — hat keinen Run erzeugt, die Stack-Commits davor und danach je zwei.Für dieses Issue heißt das: die Lücke ist real und nicht kleiner als angenommen.
lint --fail-on-errorläuft bei einem Inhalts-Publish tatsächlich nicht mehr mit, ein durch eine Reihe von Publishes langsam inkonsistent werdender Korpus fällt also niemandem auf. Die Begründung des nächtlichen Laufs steht damit vollständig.Die Vorfrage im Issue selbst — ob dieser Gitea-Stand
on: scheduleüberhaupt auswertet — ist davon unberührt und weiterhin offen. Sie bleibt der erste Schritt.Ein Detail, das seit 1.2.0 den Workflow-Entwurf betrifft:
lintschreibt jetzt immer einen Bericht nachreports/Lint Report <date>.mdund druckt nur die Abschnitte mit Befunden. Für den nächtlichen Lauf ändert das nichts an der Zeile (lint --fail-on-errorbleibt), aberreports/ist gitignored — der Bericht ist also nach dem Job weg, sofern er nicht als Artefakt eingesammelt wird. Falls das gewünscht ist:actions/upload-artifact@v3, nicht v4 (siehe #10)..gitea/workflows/nightly.ymlliegt seit49bd7d4aufmain.Was gebaut wurde
on: schedule(17 3 * * *, UTC — Gitea wertet Cron in UTC aus) plusworkflow_dispatch, kein Push-Trigger. Die Runner-Form ist unverändert ausci.ymlübernommen, nicht neu hergeleitet:linux-docker,debian:trixie-slim, aptnodejsvor dem Checkout,actions/checkout@v7.WIKITOOL_SESSION_ID: nightly-${{ github.run_id }}undWIKI_TRACE_DIR: /tmp/wikitool-tracesind gesetzt.Die sechs Kommandos wie im Issue, in vier Schritten gruppiert, damit ein roter Lauf schon in der Schrittliste zeigt, welche Ebene gebrochen ist.
migrate verify --from <rev>ist wie vorgesehen nicht drin.Ein Zusatz, der im Entwurf fehlte:
WIKI_AUTHOR: Nightly.doctorgibtFAIL author, wenn weder$WIKI_AUTHORnochgit config user.nameauflöst, und ein Container hat keine git-Konfiguration. Der Lauf schreibt keine Seite, die Identität ist also Formsache — aber ohne sie wäredoctorunabhängig vom Zustand der Instanz immer rot und damit wertlos. Dieselbe Lücke wie auf der Testseite (#8), an derselben Stelle geschlossen.Kein Artefakt-Upload für den Lint-Bericht.
reports/ist gitignored, der Bericht ist nach dem Job weg. Der Kommentar oben schlugupload-artifact@v3vor; ich habe es weggelassen, weillint --fail-on-errordie Befunde bereits ins Job-Log druckt und ein Artefakt eine zweite Stelle wäre, an der man nachsieht. Nachrüstbar, wenn sich zeigt, dass das Log nicht reicht.Fehlersichtbarkeit — entschieden
Entscheidung des Nutzers: Gitea-eigene Notification, kein Meldeschritt im Workflow. Ein Job, der sein eigenes Issue anlegt, braucht einen Actions-Token mit
issues: writeund eine Dedup-Regel — mehr Maschinerie, als ein roter Lauf ohnehin trägt. Der Grund steht im Kopf des Workflows, damit die Auslassung nicht als Vergessen gelesen wird.Akzeptanzkriterien
[[Diese Seite Existiert Nicht]]inNathan.md→lint --fail-on-errorexit 1; nach dem Zurücknehmen exit 0. Der Lauf ist keine Dekoration..gitea/steht außerhalb des Version-Gates. (Der Commit trägt trotzdem 1.7.2, aber wegen #20/#22.)Was noch aussteht
Ob dieser Gitea-Stand
on: scheduleauswertet — die Vorfrage des Issues, weiterhin unbeantwortet. Neu belegt ist nur die Voraussetzung: die Instanz läuft Gitea 1.26.1, also weit hinter der 1.20, in der Actions-Schedules eingeführt wurden. Das macht es plausibel, nicht wahr. Der Workflow liegt jetzt auf dem Default-Branch, wo Gitea Schedules überhaupt erst auswertet — die Beobachtung kann also ab heute Nacht laufen. Prüfen mitlist_runsauf ein Eventschedulemit Datum von morgen.Ob der Job durchläuft — per
workflow_dispatchangestoßen (Run 83). Ergebnis steht bei diesem Kommentar noch aus; der Runner arbeitet die drei Läufe auf49bd7d4seriell ab. Das beweist den Job, nicht den Zeitplan.Das Issue bleibt deshalb offen. Es schließt, wenn ein Lauf mit
event: schedulein der Liste steht.Nachtrag: der Job läuft, nachdem der erste Versuch zu Recht rot war.
Was der erste Lauf gefunden hat
Run 83 (
workflow_dispatchauf49bd7d4) brach im Schrittdoctormit exit 1 ab. Zwei FAILs, beide kein Wiki-Drift, sondern eine Lücke im Workflow-Entwurf:doctorfragt, ob eine arbeitsfähige Instanz korrekt konfiguriert ist. Ein blanker Checkout ist noch keine — er ist der frische Clone, deninstructions/bootstrap.mdbeschreibt:.agents/skills/und.claude/skills/sind generiert und bewusst nicht committet, also gibt es sie erst nachinstructions sync. Und ein Container hat keine git-Identität;WIKI_AUTHORdeckte dieauthor-Prüfung ab,git-identityist eine zweite und blieb offen.instructions verifywäre am selben Punkt gescheitert (es prüft ebenfalls „published copies match their source"), hat den Schritt aber nicht mehr erreicht — der Fehlschlag hat den echten Prüfteil des Laufs komplett verdeckt. Genau das ist der Wert dieses ersten Fehlschlags: er stand vor dem, was der Lauf eigentlich beobachten soll.Korrektur
397d8af: im Schritt Tool environment jetzt git-Identität setzen undtools/wikitool instructions syncausführen, bevordoctorläuft.WIKI_AUTHORist entfallen — die git-Identität deckt beide Prüfungen mit einem Mechanismus ab, statt mit zweien. Die Begründung steht als Kommentar am Schritt, damit der Bootstrap nicht als überflüssiger Ballast wieder herausgekürzt wird.Ergebnis
Run 85 (
workflow_dispatchauf397d8af): alle sieben Schrittesuccess.actions/checkout@v7doctordocs verify+instructions verifylint --fail-on-errorsources coverage+migrate statusDie Runner-Form aus
ci.ymlträgt also auch hier; CI-Lauf 84 auf demselben Commit ist ebenfalls grün.Akzeptanzkriterien
lint --fail-on-errorexit 1 / exit 0 nach Rücknahme).Was allein noch offen ist
Ob dieser Gitea-Stand
on: scheduleauswertet. Beide Läufe bisher warenworkflow_dispatch— sie beweisen den Job, nicht den Zeitplan. Der Workflow liegt jetzt aufmain, wo Gitea Schedules überhaupt erst auswertet, und der Cron steht auf17 3 * * *UTC. Prüfung morgen:list_runsauf einen Eintrag mit"event":"schedule". Bleibt der aus, greift der Fallback aus dem Issue — externer Trigger gegen die Actions-API, mit dokumentierter Entscheidung statt gelöschtem Workflow.Die letzte offene Vorfrage ist beantwortet: Run 90 —
event: "schedule",head_branch: main,head_sha905f1f2— ist am 2026-09-01 um 03:17:35 UTC gestartet und um 03:17:38 UTC durchgelaufen, exakt zur konfigurierten Cron-Zeit17 3 * * *UTC. Alle sieben Schritte erfolgreich: System dependencies,actions/checkout@v7, Tool environment,doctor,docs verify/instructions verify,lint --fail-on-error,sources coverage/migrate status.Damit ist belegt: dieser Gitea-Stand (1.26.1) wertet
on: scheduleauf dem Default-Branch tatsächlich aus. Der im Issue vorgesehene Fallback (externer Trigger von hephaestus gegen die Actions-API) ist nicht nötig.Akzeptanzkriterien — jetzt vollständig
.gitea/steht außerhalb des Version-Gatesworkflow_dispatch), Run 90 beweist jetzt den Zeitplan (schedule)Schließe das Issue.