This document covers the release flow and maintenance protocols for Mailglass.
Release Flow
Mailglass uses Release Please to automate versioning and changelogs.
- Merge feature branches into
mainusing Conventional Commits. - Release Please will open a "Release PR" with the version bump and updated
CHANGELOG.md. - Merging the Release PR should trigger the
publish-hexworkflow from the published GitHub Release. If downstream workflow fan-out does not happen,workflow_dispatchwith the core release tag (mailglass-v<version>) is the canonical maintainer fallback. - The
publish-hexworkflow is environment-gated and requires manual approval in the GitHub Actions UI.
Snapshot Update Protocol
When the installer output or golden files change:
- Run
mix verify.installer.golden. - If the failure is expected, update the golden files in
test/fixtures/. - Commit the updated fixtures with a
chore: update installer golden filesmessage.
Required Checks
Before merging any PR, ensure:
mix verify.stability_contractscripts/verify_support_contract.shSupport Contract CoreSupport Contract AdminCompile No Optional Depsmix credo --strictmix dialyzermix docs --warnings-as-errors
The honest repo-root entrypoint is mix verify.stability_contract or
scripts/verify_support_contract.sh. They run the three required
branch-protection buckets plus the inbound sibling-package docs lane in sequence:
Support Contract CoreSupport Contract Adminmailglass_inbounddocs contract (mailglass_inbound/test/mailglass_inbound/docs_contract_test.exs)Compile No Optional Deps
When those checks pass, they prove the current compatibility contract described
in guides/compatibility-and-deprecations.md:
runtime floors, matched sibling-package docs wiring for mailglass_inbound,
matched mailglass_admin release truth, and the required-vs-advisory split
below. Do not claim broader support than those repo artifacts prove.
The following checks are advisory signal, not branch-protection truth:
Core Full Suite AdvisoryProvider Compatibility AdvisoryProvider Live Advisory
Provider Live Advisory remains a cron and workflow_dispatch canary. It is not a merge blocker.
Bus Factor & Continuity
Mailglass is single-maintainer at v0.1. The release pipeline is gated on a GitHub
Environment (hex-publish) with a single required reviewer (szTheory). When a
GitHub Environment has only one reviewer, GitHub silently disables the
prevent_self_review setting — the gate is effectively a one-eye pause, not a
two-eyes review. This is documented honestly here rather than presented as a
stronger control than it is. Multi-owner Hex transition is deferred to v0.5,
when production adopters exist (D-26 rationale: at v0.1 the asymmetry of a
co-owner being able to mix hex.publish from their own machine bypassing
GitHub governance is a worse footgun than the bus-factor risk it solves).
If szTheory is unreachable for more than 30 days, the community can request a
Hex.pm package transfer by opening a public issue titled
Maintainer-unreachable: requesting Hex transfer on
https://github.com/szTheory/mailglass/issues — Hex.pm's public maintainer-transfer
process can be initiated from there.
Retract Decision Tree
Five rules. Bias toward patch over retract — three retractions in your first six months tells evaluators "don't bet on this lib."
- Data-loss / security / signature bypass / fails to compile.
Run
mix hex.retire <pkg> <ver> security|invalid --message "<140 chars>"AND ship<ver+1>immediately. - User-visible breakage with workaround.
Do NOT retire. Patch within 7 days. Add a CHANGELOG entry.
If the fix changes a documented compatibility bridge or support claim, update
guides/compatibility-and-deprecations.mdin the same patch. - Cosmetic / docs / non-runtime. Do NOT retire. Roll into next planned patch.
- Published less than 60 minutes ago AND zero downloads.
Run
mix hex.publish --revert <ver>(only window where unpublish works — also bounded by Hex.pm's 24-hour initial-release window). - Already retired and false alarm.
Run
mix hex.retire <pkg> <ver> --unretire.
Security Response SLA
Single-maintainer numbers, written to be kept rather than aspired to.
- Acknowledgement of report: within 72 hours.
- Mitigation or workaround for critical issues: within 14 days.
- Public security advisory: published alongside the fix.
Critical issue classes are listed in SECURITY.md (## Critical Classes).
Reports go through the disclosure address documented there or via GitHub
Private Vulnerability Reporting if no email is reachable.
Release Runbook
Five steps. Step 4 has a literal 60-minute timer — that is the last revert window before the published artifact becomes permanent.
Use the Phase 38 release-day proof forms while running these steps:
.planning/phases/38-release-rehearsal-and-proof-artifacts/38-03-RELEASE-CHECKLIST.md.planning/phases/38-release-rehearsal-and-proof-artifacts/38-03-RELEASE-RECORD.md
The checklist separates repo-proved gates from manual/external proof and forces explicit capture of the tag, workflow run URLs, approver identity, fallback usage, Hex/HexDocs checks, branch-protection result, and 60-minute outcome.
Verify CI green on
mainfor the SHA to be released. Checkactions/workflows/ci.yml— required because publish-hex.yml gates on this SHA via thegate-ci-greenjob (per Plan 08, D-16). The required release-truth buckets are:Support Contract Core (Elixir 1.18 / OTP 27)Support Contract Admin (Elixir 1.18 / OTP 27)Compile No Optional Deps (Elixir 1.18 / OTP 27)- Phase 38 prepublish proof/export bundle (
38-01-PREPUBLISH-PROOF.md) - Phase 38 install/upgrade rehearsal artifact (
38-02-REHEARSAL-EVIDENCE.md)
Merge the release-please PR. Squash-merge keeps the changelog history linear. Review the release PR diff before merge. This repo uses a custom mailglass_admin dep-pin sync step, so the generated PR is load-bearing. The current release path emits package tags such as
mailglass-v<version>andmailglass_admin-v<version>. If a broad milestone PR was squash-merged under a non-releasable subject and release-please skips the cut, recover with a tiny follow-up commit that carries aRelease-As: <intended-version>footer. Do not hand-edit.release-please-manifest.jsonto force the version.Approve the
hex-publishdeployment in the GitHub Environment UI. Review the pre-publish summary in the workflow run page (rendered by theprepublish-summaryjob per D-15) BEFORE clicking Approve. Verify the file count, total size, CHANGELOG excerpt, and top files all match expectations. Record the tag, publish workflow run URL, approver identity, and approval timestamp in38-03-RELEASE-RECORD.md.- Package order: The workflow guarantees
mailglass(core) publishes first; once core is indexed,mailglass_adminandmailglass_inboundpublish in parallel against the newly live core. - Idempotency: All three publish steps check
mix hex.infofirst and skip the publish command if the version is already live, making the workflow safe to retry. - Fallback path: If the Release Please tag/release exists but
publish-hexdid not fan out, dispatch.github/workflows/publish-hex.ymlmanually (withpackage=allanddry_run=false). Do not dispatch frommain. Always use the reviewed release tag (for1.0.0:mailglass-v1.0.0) so the publish run is pinned to the exact commit Release Please tagged.
- Package order: The workflow guarantees
Within 60 minutes of publish: smoke-install in a fresh Phoenix app. Set a literal timer when approving the deployment. Run:
mix archive.install hex phx_new --force mix phx.new sandbox --no-ecto --no-mailer --install cd sandbox # add {:mailglass, "== 1.0.0"}, {:mailglass_admin, "== 1.0.0"}, {:mailglass_inbound, "== 0.1.0"} to deps mix deps.get && mix mailglass.install && mix compile --warnings-as-errors mix phx.server # visit http://localhost:4000/dev/mail/If anything fails AND the publish was less than 60 minutes ago AND zero downloads have happened, the Retract Decision Tree rule 4 (
mix hex.publish --revert) is reachable. After 60 minutes the only options are retire-then-patch (rule 1) or patch-only (rule 2).
Keep the published support story honest: if the smoke or support-contract
checks reveal a mismatch with the documented matrix or upgrade posture, fix
the guide and package metadata together rather than carrying split truth.
For inbound-slice changes, rerun mix verify.stability_contract so the
repo-root lane proves the canonical mailglass_inbound docs and support
posture before you publish.
If you need to reproduce the v0.2 codemod or rollback story during this
window, do it in a disposable fixture or git-clean worktree only. The
public rollback contract is git-based review/revert of the upgrade diff,
not cleanup of arbitrary dirty repositories.
The post-publish-smoke workflow (.github/workflows/post-publish-smoke.yml,
Plan 09) runs the same smoke automatically — but it does not respect the
60-minute window. Run the manual smoke during the window regardless.
If publish succeeds but smoke does not fan out, use workflow_dispatch on
.github/workflows/post-publish-smoke.yml with that same core tag.
Record the post-publish smoke run URL, whether fallback dispatch was used,
Hex/HexDocs URLs, and the final 60-minute decision in the Phase 38 release
record.
- Post the release link to Elixir Forum #libraries section (post-publish, optional — performed by maintainer on their own cadence; not gated by Phase 07.1's milestone-shipped marker per CONTEXT line 14 / line 351). Body equals the GitHub Release narrative (CHANGELOG entry verbatim plus one framing paragraph for 0.x.0 minor bumps; verbatim CHANGELOG only for patches).