From 411d7761d9c1613ddf3a70b0dd42002caca4926b Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Sat, 17 Oct 2026 12:49:43 +0200 Subject: [PATCH] CI workflow: a manual proof that the failure mail can be sent (R-1129) workflow_dispatch with input force_alarm (default false). A dispatch-only step prints the RESEND_API_KEY length (never the key) and, when forced, fails so the alarm step sends its real mail, subject marked FORCED TEST. Push runs are unchanged. No controller release, no code change. Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS --- .gitea/workflows/gates.yml | 33 +++++++++++++++++++++++++++++++-- CHANGELOG.md | 9 +++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/gates.yml b/.gitea/workflows/gates.yml index 95731f0..3c14be2 100644 --- a/.gitea/workflows/gates.yml +++ b/.gitea/workflows/gates.yml @@ -16,7 +16,16 @@ # A failing run must reach a person — a detector nobody hears is the defect R-29 filed, rebuilt one # layer up. That is the last step, and it runs ONLY on failure. name: gates -on: [push] +# R-1129: `workflow_dispatch` is the manual PROOF that the failure mail can be sent. A push run is +# unchanged by it: the proof step below is skipped on a push, and `force_alarm` is then empty. +on: + push: + workflow_dispatch: + inputs: + force_alarm: + description: "Fail this run on purpose so the alarm step sends its real mail (a proof, not a gate result)" + type: boolean + default: false jobs: gates: @@ -45,6 +54,22 @@ jobs: # exit code IS the result: no `|| true`, no pipe that could swallow it. run: cd ws/felhom-controller/controller && python3 scripts/controller_gates.py --fast + - name: Manual proof - alarm key present, optional forced failure (dispatch only, R-1129) + # Runs ONLY on a manual dispatch, never on a push. It prints the key's LENGTH, never the key, + # so "CI can see the secret" is an observable rather than an assumption. With + # force_alarm=true it then fails on purpose so the alarm step below sends its real mail; + # the commit itself stays green, because its push run skips this step. + if: github.event_name == 'workflow_dispatch' + env: + RESEND_API_KEY: ${{ secrets.RESEND_API_KEY }} + FORCE_ALARM: ${{ github.event.inputs.force_alarm }} + run: | + python3 -c 'import os;print("RESEND_API_KEY length: %d" % len(os.environ.get("RESEND_API_KEY","")))' + if [ "$FORCE_ALARM" = "true" ]; then + echo "force_alarm=true - failing on purpose so the alarm step runs (R-1129 proof)" + exit 1 + fi + - name: Alarm on failure # THE POINT OF THE WHOLE THING. Probe P5 measured that a failed run produces NO mail, NO # notification row and NO log line from Gitea itself — a red tick in a web UI nobody watches @@ -58,6 +83,8 @@ jobs: if: failure() env: RESEND_API_KEY: ${{ secrets.RESEND_API_KEY }} + # Empty on a push, so a push run's mail is exactly what it was before R-1129. + FORCE_ALARM: ${{ github.event.inputs.force_alarm }} run: | python3 - <<'PY' import json, os, sys, urllib.request, urllib.error @@ -75,7 +102,9 @@ jobs: body = json.dumps({ "from": "Felhom CI ", "to": ["admin@felhom.eu"], - "subject": "[felhom CI] gates FAILED in %s" % repo, + "subject": ("[felhom CI] FORCED TEST, not a real failure - gates FAILED in %s" + if os.environ.get("FORCE_ALARM") == "true" + else "[felhom CI] gates FAILED in %s") % repo, "text": ( "The gate entry point exited non-zero.\n\n" "Repository : %s\n" diff --git a/CHANGELOG.md b/CHANGELOG.md index 87a789c..dfbfce8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## Unreleased (2026-10-17) — CI workflow: a manual proof that the failure mail can be sent (R-1129); no code change + +`.gitea/workflows/gates.yml` gains a manual `workflow_dispatch` trigger with one input, `force_alarm` (default false), and +one step that runs ONLY on a dispatch: it prints the length of `RESEND_API_KEY` (never the key) and, with +`force_alarm=true`, fails on purpose so the existing alarm step sends its real mail; the mail's subject then starts +„FORCED TEST, not a real failure". A push run is unchanged: the new step is skipped and the subject is the old one. Same +change in all four repos' workflows. Purpose: prove the failure mail now goes out after the operator set the key +(every failed run until today ended „ALARM FAILED: RESEND_API_KEY is empty"). + ## v0.326.0 — FileBrowser's default password is checked at every FileBrowser start; the decoy suite is a gate (2026-10-17) **MinAgent: 0.158.1** — the floor's, carried. Nothing here calls the agent. No golden (`09` §3 decision 250). Task: