eval_as_user_id + the create()/write() re-binding logic + the owner-resolution preamble are duplicated (~30 verbatim lines plus ~14 near-identical) between grm_routing_rule.py and grm_escalation_rule.py. The docstrings had already drifted once (re-synced in the PR #415 review fixes) — duplicated security rationale decays.
Proposal: an AbstractModel mixin (field, create, write, a _rule_owner_env() helper), with each concrete model supplying only _EVAL_TARGETING_FIELDS. Open question: where it lives so future adopters (spp_alerts-style owner-identity models) can depend on it without layer inversion.
Optional inclusion: replace the evaluate-path try/except AccessError with a _filtered_access("write") pre-check for clarity — not equivalence (the write-path try/except must stay for post-write failures; see the savepoint in apply_escalations), and note _filtered_access short-circuits under env.su.
From the PR #415 review threads on grm_routing_rule.py:131 and :308.
eval_as_user_id+ thecreate()/write()re-binding logic + the owner-resolution preamble are duplicated (~30 verbatim lines plus ~14 near-identical) betweengrm_routing_rule.pyandgrm_escalation_rule.py. The docstrings had already drifted once (re-synced in the PR #415 review fixes) — duplicated security rationale decays.Proposal: an
AbstractModelmixin (field,create,write, a_rule_owner_env()helper), with each concrete model supplying only_EVAL_TARGETING_FIELDS. Open question: where it lives so future adopters (spp_alerts-style owner-identity models) can depend on it without layer inversion.Optional inclusion: replace the evaluate-path
try/except AccessErrorwith a_filtered_access("write")pre-check for clarity — not equivalence (the write-path try/except must stay for post-write failures; see the savepoint inapply_escalations), and note_filtered_accessshort-circuits underenv.su.From the PR #415 review threads on
grm_routing_rule.py:131and:308.