Skip to content

First-match-wins ordering: hold_cluster_spam shadows the base cluster_spam suspend/bounce/label rules, making them unreachable #67

Description

@keithadler

Reading enforcement_user.yaml at c65aa17, the base cluster_spam enforcement rules appear to be unreachable: a broad skip rule is ordered above the specific action rules that share its predicate, and the engine is first-match-wins.

The engine is first-match-wins

decide_with returns on the first rule whose when evaluates true:

https://github.com/xai-org/x-algorithm/blob/c65aa17/abuse-enforcement-service/service-lib/src/rules.rs#L256-L277

Value::Bool(true) => return Ok(outcome_to_decision(&rule.outcome)),
Value::Bool(false) => continue,

So a broad rule placed above narrower rules that share its predicate shadows them.

The ordering

https://github.com/xai-org/x-algorithm/blob/c65aa17/abuse-enforcement-service/service-lib/rules/enforcement_user.yaml#L179-L202

- id: hold_cluster_spam
  when: 'score.model_version.startsWith("cluster_spam")'          # broad, SKIP
  then: { kind: skip, reason: cluster_spam_held }

- id: act_cluster_spam_suspend
  when: '...startsWith("cluster_spam") && "cluster_spam_suspend" in score.labels'
  then: { kind: act_suspend_user, perm: false, policy: PlatformManipulation }

- id: act_cluster_spam_bounce
  when: '...startsWith("cluster_spam") && "cluster_spam_bounce" in score.labels'
  then: { kind: act_arkose }

- id: act_cluster_spam_label
  when: '...startsWith("cluster_spam") && "cluster_spam_label_shr" in score.labels'
  then: { kind: act_add_labels_v2, labels: ["SpamHighRecall"], ... }

hold_cluster_spam matches every model version starting with cluster_spam and returns skip. It sits above the three act_cluster_spam_* rules, which share the same startsWith("cluster_spam") prefix. For any base cluster_spam model version, hold_cluster_spam matches first and the three action rules below are never reached. An account the model flags with cluster_spam_suspend (or _bounce, or _label_shr) is held rather than actioned.

Confirming it is genuinely unreachable, not just suspicious

Walking a base cluster_spam decision from the top of the file:

  • The inauthentic_detection_v45_* rules require startsWith("inauthentic_detection_v45"), no match.
  • The cluster_spam_extended_* action rules (above hold_cluster_spam) require startsWith("cluster_spam_extended"); a base cluster_spam version does not match.
  • The label-only rules near the top match on score.labels unrelated to cluster spam, so a normal cluster_spam decision does not hit them.
  • Execution therefore reaches hold_cluster_spam, matches, and returns skip. Lines for suspend/bounce/label never execute.

Note the contrast: the cluster_spam_extended family is ordered correctly, its action rules precede any hold. Only the base cluster_spam family is shadowed, which suggests this is an ordering mistake rather than an intended hold-all.

Suggested fix

Move hold_cluster_spam below the three act_cluster_spam_* rules, so it acts as the catch-all for cluster_spam decisions that carry no actionable label, mirroring how the cluster_spam_extended rules are already arranged.

Caveat

The file header reads # mirrored from GrowthBook dynamic config; last sync 2026-08-12, and the file contains acknowledged mock values (for example high_follower_count >= 12.34, commented as a mock). So this YAML may be a sanitised mirror, and the ordering here might be an artefact of that rather than a reflection of production. I cannot tell from the repository whether the live GrowthBook config carries the same order. If it does, base cluster_spam enforcement (suspend, bounce, label) is disabled. Worth confirming against the production config, and either way the published rules read as a shadowing bug.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions