Skip to content

Prepare conditional durable storage for alert publishing #5328

Description

@devantler

🤖 Generated by the Agentic Engineer

Summary

Prepare cluster-independent durable state for the existing alert publisher so a restarted or overlapping runner cannot repeat an unconfirmed incident write.

Motivation

The publisher delivered in #5294 protects a private local journal and reconciles ambiguous GitHub writes. The broader transport migration in #4683 also needs that state to survive loss of the publishing runner. A runner cache, workflow artifact or copied state file cannot safely coordinate concurrent writers and is not a transactional journal.

Proposed Solution

Retain the current local mode and add an explicit, default-off object-storage mode using the maintained S3 SDK and the existing Cloudflare configuration owner. Bind each journal to an operator-configured instance, read its state and revision together, and condition every update on that exact revision. A conflict or uncertain storage write must stop the invocation before another GitHub write. Initialization is a separate create-once operation; a missing or invalid live journal never initializes itself or falls back to local storage.

Cloudflare documents atomic conditional writes and strongly consistent S3 reads. That supports this protocol but does not prove the actual tenant's custody, retention, access or runtime behavior. Restoring an older valid journal requires operator reconciliation; conditional writes alone cannot detect a privileged rollback.

Acceptance Criteria

  • Preserve existing local locking, private file format, initialization sentinel, replay behavior, rate budgets and delivery receipts.
  • Use one invocation-scoped storage session for every durable barrier, including the pre-send intent, acknowledged receipt and deferred retry state.
  • Add bounded, explicitly configured conditional storage operations with no ambient credential discovery, redirect or automatic write retry; validate state, instance identity and acknowledged revisions.
  • Refuse missing, partial, malformed, oversized or wrong-instance state, conflicts and uncertain outcomes without a new GitHub write or local fallback.
  • Exercise concurrent invocations and interruption at durable barriers, including an accepted storage write whose response is lost; preserve marker reconciliation rather than resending.
  • Recheck expiry, cancellation and daily attempt limits after storage waits, so remote persistence cannot cross a publication boundary unnoticed.
  • Keep both normal publication and initialization disabled unless explicitly enabled. Invalid/disabled commands do not read credentials or contact a provider.
  • Document initialization, restore/reconciliation and live-verification requirements; pass current-head hosted validation and substantive review, merge and verify publication.

Alternatives Considered

Shared runner caches, workflow artifacts and downloaded/uploaded file journals cannot provide the required conditional transaction. A cluster-only volume does not preserve the independent heartbeat's failure boundary. Replacing the existing organization App is unrelated and unnecessary.

Additional Context

Part of #4683. This delivery prepares source; real provider conditional-write/crash proof, independent custody, fixed heartbeat binding, route cutovers and observed inbox/phone acceptance remain required in the parent before activation. No existing alert route, integration or credential is retired by this work. Size: Medium.

Sources: Cloudflare consistency model, conditional PutObject operations, and AWS SDK retry configuration.

Activity

  1. moved this to 🫴 Ready in 🌊 Project Boardon Oct 25, 2026
  2. coderabbitai commented on Oct 25, 2026

    @coderabbitai
    Contributor
    🔗 Related PRs

    #5329 - feat(alerts): persist incidents with conditional object storage [closed]
    #5294 - feat(alerting): prepare a replay-safe incident publisher [merged]


    📝 Issue Planner

    Check the box below or use the @coderabbitai plan command to generate an implementation plan and prompts that you can use with your favorite coding assistant.

    • Create Plan

    🧪 Issue enrichment is currently in open beta.

    You can configure auto-planning by selecting labels in the issue_enrichment configuration.

    To disable automatic issue enrichment, add the following to your .coderabbit.yaml:

    issue_enrichment:
      auto_enrich:
        enabled: false

    💬 Have feedback or questions? Drop into our discord!

  3. self-assigned this
    on Oct 25, 2026
  4. moved this from 🫴 Ready to 🏃🏻‍♂️ In Progress in 🌊 Project Boardon Oct 25, 2026
  5. added a commit that references this issue on Oct 25, 2026
  6. moved this from 🏃🏻‍♂️ In Progress to 👀 In Review in 🌊 Project Boardon Oct 25, 2026
  7. moved this from 👀 In Review to 🚀 Ready to Merge in 🌊 Project Boardon Nov 3, 2026
  8. moved this from 🚀 Ready to Merge to ✅ Done in 🌊 Project Boardon Nov 3, 2026
  9. devantler commented on Nov 3, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    Post-merge verification collected: #5329 merged the reviewed source head 19accd41ee1e12493b7d56ddf175dde3efb7ce00 as signed commit 939b2968af50aa96a54cbc096f2b56554faf1955. The alert-publisher source is unchanged between that reviewed head and the merged result. Protected merge-group CI 41641148132, its complete Go regressions, production deployment and Validate Main 41645958916 succeeded.

    This completes the source-preparation slice. The command and remote journal remain explicitly disabled by default, and no detector route invokes them yet. The actual R2 conflict/lost-response/restart proof was independently delivered through the declarative Cloudflare owner; route activation, credential renewal and the controlled notification acceptance remain open under #4683. No existing alert transport is retired by this completion.

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Fields

Priority

None yet

Start date

None yet

Target date

None yet

Effort

None yet

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions