Skip to content

ci(forward-merge): notify Slack when the cascade is blocked or fails - #3123

Open
mfal wants to merge 1 commit into
mainfrom
claude/upstream-sync-slack-notification-1e263b
Open

ci(forward-merge): notify Slack when the cascade is blocked or fails#3123
mfal wants to merge 1 commit into
mainfrom
claude/upstream-sync-slack-notification-1e263b

Conversation

@mfal

@mfal mfal commented Sep 3, 2026

Copy link
Copy Markdown
Member

What & why

The forward-merge cascade escalates only as a GitHub issue — which nobody sees
until they look. This adds a Slack message alongside each escalation, so a
paused or broken cascade is loud.

Three escalation points, all covered:

Escalation Today Added
§4 conflictforward-merge.yml hits a genuine code conflict Sync issue, or a comment on an existing sync issue / sync PR Slack, with a deep link to exactly that issue, comment or PR
§10 failureforward-merge.yml fails for a non-conflict reason Issue + CODEOWNERS ping Slack
Driftforward-merge-drift.yml finds next behind past the threshold Drift issue Slack, carrying the diagnosed cause

Reuses the pattern and secret already in test-visual-scheduled.yml
(slackapi/slack-github-action, SLACK_WEBHOOK_URL). ADR 0004 §4, §5 and §10
are updated to match.

Two things worth a reviewer's attention

The notifications key on the escalation, not on the run. The §4 conflict is
the expected non-error path and exits 0, so a failure()-triggered
notification would never fire on a blocked cascade — the one case we most want
to hear about. So open_sync_issue records its message via a new
record_escalation helper and a separate step sends it.

That choice also buys idempotence for free. open_sync_issue has three quiet
return 0s for a blockage that is already reported, and the drift check exits
early on four conditions; none of them write the output, so no Slack message.
A cascade that stays blocked therefore does not re-ping the channel on
every push to main, and the hourly drift schedule does not repeat itself.

All three steps are continue-on-error — and this is load-bearing, not
politeness. Without it, a webhook outage or an unset secret would fail the §4
conflict path, which the §10 step below would then pick up via failure() and
report as a genuine forward-merge failure, opening an issue for a problem that
does not exist. The GitHub issue stays the source of truth; Slack is the
additional signal.

Notes

  • A GitHub-expression footgun, avoided: '\n' inside ${{ }} is
    backslash-n, not a newline — single-quoted expression strings carry no escape
    sequences. The two dynamic messages get real newlines from printf, which
    toJSON escapes correctly; the static §10 payload uses a JSON \n directly.
  • Pre-existing, not touched: test-visual-scheduled.yml:57 uses Markdown
    link syntax [text](url) in an mrkdwn block. Slack does not support that —
    it renders literally. The correct form is <url|text>, which the new steps
    use. Happy to fix it here or separately.
  • Deliberately out of scope: a crash of forward-merge-drift.yml itself
    stays silent — it still has no failure() path.

Verification

CI cannot exercise these paths without a real conflict, so verification was
static plus a local simulation:

  • YAML parses; every run: block passes bash -n.
  • All three Slack payloads are valid JSON after expression substitution.
  • The message construction was run locally against representative inputs
    (conflict, PR-comment refresh, drift) and the rendered text read back.
  • prettier --check clean.

Checklist

  • PR title is a Conventional Commit and matches the base branch above
    (ci:main)
  • pnpm lint is clean (pre-push hook) — no test surface: CI workflows are
    not covered by pnpm test
  • Generated code is committed — nothing generated is touched
  • User-facing strings — n/a, no UI text
  • Docs updated — ADR 0004 §4, §5, §10; no public API or visual change

🤖 Generated with Claude Code

The forward-merge cascade escalated only as a GitHub issue, which nobody sees
until they look. Post a Slack message alongside each of the three escalations:
the §4 conflict, the §10 non-conflict failure, and the drift check.

Both new notifications in `forward-merge.yml` key on the ESCALATION, not on the
run. The §4 conflict path is the expected non-error path and exits 0, so
`failure()` would never see a blocked cascade. Keying on the escalation also
inherits its idempotence: `open_sync_issue`'s quiet returns and the drift
check's early exits leave the output empty, so a cascade that stays blocked
does not re-ping the channel on every push to `main`, and the hourly drift
schedule does not repeat itself.

All three steps are `continue-on-error`. Without it a webhook outage or a
missing secret would turn the conflict path into a run failure, which the §10
step would then report as a genuine forward-merge failure and open an issue
for. The issue stays the source of truth; Slack is the additional signal.

One footgun avoided: `'\n'` in a GitHub expression is backslash-n, not a
newline — single-quoted expression strings carry no escape sequences. The two
dynamic messages get their newlines from `printf`, which `toJSON` escapes
correctly; the static §10 payload uses a JSON `\n` directly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mfal
mfal requested a review from a team September 3, 2026 12:41
@mfal mfal self-assigned this Sep 3, 2026
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for ./packages/components/

Status Category Percentage Covered / Total
🔵 Lines 78.69% 746 / 948
🔵 Statements 78.57% 763 / 971
🔵 Functions 80.09% 165 / 206
🔵 Branches 70.33% 377 / 536
File CoverageNo changed files found.
Generated in workflow #6662 for commit 7bbc3da by the Vitest Coverage Report Action

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

🚀 Preview Deployment

Preview environments are ready:

Type URL
docs pr-3123.docs.review.flow-components.de
storybook pr-3123.storybook.review.flow-components.de

Images:

  • docs: ghcr.io/mittwald/flow/docs:pr-3123
  • storybook: ghcr.io/mittwald/flow/storybook:pr-3123

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant