FerrFlow already ran shell hooks at pre_bump, post_bump, pre_commit, pre_publish, and post_publish. That left gaps around the most delicate part of a release — the commit, the tag, and the push. This release fills them.

  • post_commit — after the release commit, before tagging.
  • pre_tag — after the commit, immediately before git tag. Smoke-test the bumped tree before the tag lands.
  • post_tag — after tags are created, before push. This is the natural place to cargo publish: the tag exists locally but isn't pushed yet, so a failed publish is recoverable by dropping the local tag instead of rewriting remote history.
  • pre_release — after the release pull request opens, before merge (releaseCommitMode: pr only). Post a review ping or run cross-repo validation.
  • on_success — once, after the whole release finishes cleanly. Trigger a downstream deploy.
  • on_error — once, when a release fails. It receives the failing error code in FERRFLOW_ERROR_CODE, so a Slack alert can link straight to the error reference.
{
  "workspace": {
    "hooks": {
      "postTag": "cargo publish",
      "onSuccess": "curl -X POST $DEPLOY_WEBHOOK",
      "onError": "curl -X POST $SLACK_WEBHOOK -d \"release failed: $FERRFLOW_ERROR_CODE\""
    }
  }
}

Per-package hooks (post_commit, pre_tag, post_tag) run once per released package; the once-per-run hooks (pre_release, on_success, on_error) fire a single time for the whole invocation. Note that on_error (the reactive hook) is distinct from on_failure, which stays what it always was — the abort / continue strategy for how a failing hook is handled.