diff --git a/.github/ci/ci-notify.sh b/.github/ci/ci-notify.sh index 4cfd622..8caf2b3 100644 --- a/.github/ci/ci-notify.sh +++ b/.github/ci/ci-notify.sh @@ -135,7 +135,8 @@ echo "ci-notify: verdict $verdict${failing:+ ($failing)}" # is the only one matched on purpose: git's standard hyphenated # `Co-authored-by: Some Human ` names a person or a tool, not a perch, so # matching it would mint junk recipients (a real commit on this branch's history -# yields "OpenAICodex" that way). Absent or unparseable => skipped, never an error. +# yields "OpenAICodex" that way). Absent or unparseable stays non-fatal but MUST +# emit a workflow warning: silent attribution loss reads as "there was none." FROM_LABEL="CI-$(printf '%s' "$RUNNER_NAME" | tr '[:lower:]' '[:upper:]' | tr -cs '[:alnum:]' '-')" recipients="doyle" @@ -143,10 +144,12 @@ trailer="$(git log -1 --pretty=%B 2>/dev/null | sed -n 's/^Co-authored by:[[:space:]]*\([^<]*\).*$/\1/Ip' | tail -n 1 | sed 's/[[:space:]]*$//; s/^[[:space:]]*//')" -if [ -n "$trailer" ] && [ "$trailer" != "doyle" ]; then - recipients="$recipients $trailer" +if [ -z "$trailer" ]; then + echo "::warning title=CI notification attribution missing::Head commit has no parseable line-anchored 'Co-authored by: ' trailer — notifying doyle only" +elif [ "$trailer" = "doyle" ]; then + echo "ci-notify: co-author is doyle — no second recipient" else - echo "ci-notify: no distinct co-author trailer on the head commit — doyle only" + recipients="$recipients $trailer" fi # --- send ------------------------------------------------------------------ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 04984f7..6fa722c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -128,6 +128,8 @@ jobs: exit 1 fi - uses: actions/checkout@v5 + - name: Notification trailer parser regression + run: bash .github/ci/test-ci-notify.sh - name: Clippy (deny warnings) run: cargo clippy --workspace --all-targets -- -D warnings diff --git a/traceable-reqs.toml b/traceable-reqs.toml index a847b8d..b21961e 100644 --- a/traceable-reqs.toml +++ b/traceable-reqs.toml @@ -2504,6 +2504,11 @@ id = "REQ-GOLDEN-CI-LANE" title = "Builder PRs run only the thin lint/unit/traceability lane; the full cross-platform suite runs once on the gater-assembled golden branch, never cancels in progress, and main advances only to that exact tested SHA. A checked-in flake registry may authorize exactly one same-SHA rerun for an exact test id with recorded evidence and a retirement condition; a rerun red blocks the batch. workflow_dispatch remains available for an explicit candidate SHA. (ADR-0050; ACCESS-CONTROL W0.)" required_stages = ["doc", "impl"] +[[requirements]] +id = "REQ-CI-WORKFLOW-TRIGGER-LINT" +title = "Seed: workflow lint derives each workflow file's declared event trigger set and flags `github.event.` references in `if:` gate positions when that field is unreachable from every declared trigger. Scope is gate positions only: the same event reference is valid behind an explicit fallback or a consumer-side presence guard. The lint must inspect the whole workflow after any gate edit, because fixing one trigger-impossible expression does not repair sibling instances of the mechanism. Design source: ADR-0050 trigger-impossible-gate amendment." +required_stages = [] # INACTIVE seed (doyle 2026-07-29): activate only when the delivery milestone starts. + # ── REDISPATCH-TRUTH (doyle triage 2026-07-16; ADR-0038; hertz RCA .claude/reports/2026-07-16-redispatch-truth/). ── [[requirements]] id = "REQ-HAZARD-REDISPATCH-CONTROL-STEAL"