Skip to content

v1.1 review follow-ups: delegation-aware notifications, admin input limits, mailer boot-time config #8

Open

Description

@Harry-kp

Findings from the pre-release review of v1.1 that were deliberately not fixed in that PR — each is real and reproducible, but none blocks the release. Recording them so they are not lost.

Notifications and delegation

  • Notifications ignore active delegations. Notifier resolves the recipient from step.assigned_actor, which Delegation never rewrites — only reassign! does. So while Alice has delegated to Bob, Bob can act on the step (actionable_by includes delegates) but Alice is the one who gets emailed. Either resolve recipients through the delegation chain, or document that delegation is an authorization concept and notifications intentionally follow assignment.

    app/services/approval_engine/notifier.rb (recipient resolution)

  • changes_requested mail names the assignee, not the actor who acted. When a delegate requests changes, the mail attributes it to the person the step is assigned to. The audit log records the real actor (actual_actor / intended_actor); the mail does not read it.

    app/mailers/approval_engine/notification_mailer.rb

  • "Never mails the same person twice" is stronger than the outbox can promise. reminded_at guarantees the reminder is emitted once; the outbox is at-least-once, so relay retries can still deliver a duplicate. The README states the at-least-once tradeoff for notifications generally, but the reminder paragraph reads as an absolute.

Admin input limits

  • The condition size cap is enforced only on the raw-JSON path. The simple field/operator/value builder can store an unbounded condition, and conditions are evaluated on the hot path of a record save. Move the cap to the point where the condition is assigned, so both paths share it.

    app/controllers/approval_engine/admin/trigger_rules_controller.rb

Boot-time configuration

  • NotificationMailer resolves config.parent_mailer when the class body is evaluated, and the mailer tree is eager-loaded in production even when notifications_enabled is false. That makes parent_mailer initializer-only in a way that is easy to trip over, and couples an unused feature to boot. Worth either documenting explicitly or deferring the constant lookup.

    app/mailers/approval_engine/notification_mailer.rb

Docs

  • The 60-second quickstart raises if no user is in the group. ApprovalBuilder raises BuilderError when resolve_approval_group returns nothing, so a reader who has not yet created a user with role: "manager" gets an exception from Invoice.create! rather than a no-op. The error message is good; the quickstart should say to create the approvers first.

Found by an adversarial review pass over the v1.1 diff (five lenses, each finding independently attacked by a skeptic and a line-by-line tracer). The defects that were fixed in the release are listed in that PR.

Activity

  1. Harry-kp commented on Aug 29, 2026

    @Harry-kp
    OwnerAuthor

    Four of these are fixed in #9:

    • Notifications ignore active delegations — Notifier now mails the assignee and any delegate whose delegation is in effect, matching who Step.actionable_by says can act. Covered by notifier_test.rb, including a test that an expired delegation is not notified.
    • changes_requested names the assignee, not the actor — reads actual_actor off the ledger entry, falling back to the assignee.
    • Condition cap enforced only on the raw path — moved to where the condition is assembled, so the simple builder is capped too.
    • "Never mails the same person twice" overstated — the README now says the guarantee is on emission, and that the at-least-once outbox can still deliver a duplicate.
    • Quickstart raises when a group resolves to nobody — documented in the quickstart itself.

    Still open:

    • NotificationMailer resolves config.parent_mailer when the class body is evaluated, and the mailer tree is eager-loaded in production even when notifications are off. Worth deferring the constant lookup, or documenting that parent_mailer is initializer-only.

    Also added while in here: ApprovalEngine::TestHelpers, so a host app can drive an approval in its own tests without hand-rolling fixtures.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions