Onyx Digital Intelligence.

Demoted, but not labelled

#X #DSA #transparency #POPIA #OnyxAudit

On 13 August 2026 the account @XOpenSource announced that X was “open-sourcing the code that affects a post’s visibility in the For You timeline, and releasing a new tool that shows people labels applied to their account or posts that might limit visibility.” Elon Musk amplified the release, writing: “In making 𝕏 open source, we are actively seeking criticism to improve fairness and quality of algorithmic recommendations.”

Screenshot_20260814_162258_X

I took him at his word and read the code.

What follows is drawn from the reply-spam flow inside grox/flows/reply_spam/ at commit 47c1bcd. It is one flow among many. It decides whether a reply is treated as coordinated spam and whether a safety label is written that can limit how far that reply travels. The transparency tool released the same day surfaces labels. The code shows a class of accounts that can still be demoted without receiving the label the tool is designed to display.

Screenshot_20260814_143946_Brave


The prompts are excluded, and the code says so

The first thing worth reading in the directory is prompts.py. It is thirty lines long. It builds a Jinja environment, points it at a templates folder, and exposes three functions that render system prompts, one of which is coordinated_spam_system_prompt().

The templates folder is not in the repository. Sitting above those functions is a comment:

“As stated in README for the open source repo, prompts are excluded to reduce gameability of the system.”

So on the day X announced it was open-sourcing the code that affects a post’s visibility, the code itself documents that the instructions driving the classifier were held back. Nobody had to infer this or reason from an absence. It is written down, in the module that loads the missing file.

Screenshot_20260814_143836_Brave

I want to be careful here, because it is easy to overstate. This is not a leak, and I do not have the prompt text. What I have is the execution path: which template is loaded, by which function, feeding which model, producing which label.

I also have more of the prompt than the exclusion suggests. The system template is withheld, but the user-facing half of the conversation is assembled inline in classifier_coordinated_spam.py, and that text is public. The thread is rendered post by post, each tagged #### Post N, with an instruction that the model should “Refer to posts only by their integer index”, and post zero carried into the prompt tagged (root post — never flag). That is instruction text, in the repository, shaping the classifier’s behaviour. The exclusion is partial, and the partial part is not acknowledged.

Screenshot_20260814_144210_Brave


Who the system does not look at

Before any model is called, plan_coordinated_spam.py runs a filter task first, with no dependencies. That filter decides whether the reply is even a candidate.

task_filter.py contains three separate filter classes. The one that governs the coordinated-spam plan is TaskCoordinatedSpamFilter. Its constant and early-exit conditions are:

FOLLOWER_COUNT_THRESHOLD_FOR_SPAM_DETECTION = 1000

The material rejection reasons are:

  1. The root author has fewer than 1,000 followers. Reason logged: low_blast_radius.
  2. The thread is less than two levels deep (len(post.ancestors) < 2). Reason: one_level_deep.
  3. The replying account carries high_page_rank_v2 or grey_badge. Reason: high_page_rank_or_grey_badge.

Replies from Grok and Gork are also excluded (is_grok_reply, is_gork_reply).

Per X’s own Help Centre, the grey checkmark “indicates that an account represents a government institution or official, or a multilateral organization.” So government accounts and high-reputation accounts sit outside coordinated-spam candidacy for the model stage, and replies underneath accounts with fewer than a thousand followers are not examined at all.

Read that second one again, because it runs opposite to intuition. If you have a small account, the reply guff underneath your posts is not policed by this path. The stated logic is blast radius, and as engineering it is defensible: finite compute goes where the reach is. As policy it means enforcement quality is a function of the root author’s follower count, and it is expressed as an integer in a source file rather than in any published rule.

(The same file contains two other filters, and they are a routing scheme rather than three copies of one rule. TaskSpamFilter rejects a candidate where either participant is above 15,000 followers, logging reply_ranking_target and handing it to the reply-ranking path. TaskReplyRankingFilter rejects only where both the root author and the account being replied to sit at or below 15,000, so it is the high-reach path.

Two differences bear on the section below: TaskReplyRankingFilter carries no high_page_rank_v2 or grey_badge early exit, and no Grok or Gork exclusion, both of which the coordinated-spam filter has. Note also that the constant name FOLLOWER_COUNT_THRESHOLD_FOR_SPAM_DETECTION occurs twice in this file with different values: 15,000 in TaskSpamFilter, 1,000 in TaskCoordinatedSpamFilter. The 1,000 above is the latter. The reply-ranking filter uses a third, separately named constant, FOLLOWER_COUNT_THRESHOLD_FOR_REPLY_RANKING, also set to 15,000.)

Screenshot_20260814_144045_Brave


Demoted, but not labelled

This is the part I have not seen written down anywhere, and it is the reason this piece exists.

In task_write.py there is a function, _apply_reply_spam_label. It takes a post ID and an author ID, and before applying anything it asks two questions of that author in parallel: is_high_page_rank_v2_user and is_grey_badge_user. If either is true, it logs the skip, increments task.apply_label_from_grox.skipped.count with the attribute reason="high_page_rank_or_grey_badge", and returns. Test users are skipped the same way, under reason="test_user".

If nothing skips, it applies SafetyLabelType.RiskyHighVizReply through StratoApplyLabelFromGrox.

Now follow what happens next, because the function returns and its callers carry on.

In TaskWriteCoordinatedSpamReplyRanking._mark_spam, the label call is followed immediately by two writes: a reply ranking score of 0.0 to Strato, and the same score to Kafka. Neither is conditional. In the reply-ranking path, TaskWriteReplyRankingManhattan does the same thing: it calls the label function when the score is zero, then saves the score regardless of whether the label was applied.

So the exemption is from the label, not from the demotion. An exempted account can still have a reply ranking score of zero written against its post. What it does not get is the record.

The transparency tool announced on the same day is described as showing labels. The “Under the Hood” page lets eligible accounts see “the labels applied to your account and posts” and download the data. Labels. Which means the class of account the code exempts from labelling is precisely the class whose demotion the new transparency tool has nothing to show for.

The label side of that is not inference from a product page. visibility-filtering/get_safety_labels.rs, in the same release, is the endpoint that serves labels out.

It accepts a request carrying tweet_ids, deduplicates them, resolves them through a SafetyLabelSource, and returns a response containing results, a map of tweet ID to SafetyLabelMap, together with failed_ids. Labels in the response type. The reply ranking score written by task_write.py is not in it: that goes to a separate Strato path and to Kafka, while labels resolve through the visibility-filtering service to a distinct safety label store. The SafetyLabel struct does carry its own optional score, but that is a property of a label that exists. Where the exemption fires, no label is applied, so there is no entry in the map and nothing to return.

Whether this specific endpoint is what backs the Under the Hood export is not something the code tells me, and I have not seen the export because the rollout is a randomised test group I am not in. But the shape of the visibility-filtering service is labels keyed by post, which is consistent with a tool that can show you a label and cannot show you a score.

Two honest qualifications, because this is the claim that will be tested. First, on the coordinated-spam path the filter gate above means that where the replying account is itself exempt, the plan is unlikely to reach the write step at all. What survives on that path is mid-thread: _mark_spam iterates over every flagged post in the thread, not only the reply being evaluated, so an exempt author further up the thread can be flagged, scored zero and left unlabelled.

Second, and more directly, the reply-ranking path has no such gate. TaskReplyRankingFilter contains no high_page_rank_v2 or grey_badge early exit, so an exempt account replying in a thread where either party exceeds 15,000 followers is eligible to be scored. If that score returns zero, TaskWriteReplyRankingManhattan calls the label function, the label function skips on the exemption, and the score is written regardless. That is the asymmetry with nothing standing in front of it.

The mechanism is in the source. How often it fires is a number only X holds.


The classifier is not one model call

The README for the wider repository states that X has “eliminated every single hand-engineered feature and most heuristics from the system”, and that “the Grok-based transformer does all the heavy lifting.”

classifier_coordinated_spam.py does not read that way. After the model returns, CoordinatedSpamScorer.score runs three hand-written deterministic checks in sequence:

Each has its own metric counter. So a coordinated-spam finding requires two or more flagged posts, one of which must be the newest reply, from an author the original poster has not already engaged with. That is a considerably narrower net than “an AI decides”, and I have not located it in any public description of the system.

It is possible the README sentence refers to the Phoenix ranking model rather than the safety flows, and I would accept that reading. It is a fair question to put to X, which is what I have done.

On the model itself, restraint is warranted. The coordinated-spam classifier is constructed from a constant named GEMMA_2 through something called OaiSampler, assigned to an instance variable named oai_gemma4. OaiSampler denotes an OpenAI-compatible interface, which most inference servers expose and which implies nothing about OpenAI. The constant and the variable disagree with each other, and constants.py, where the constant resolves, is a file I have not read. The same variable name appears in an unrelated flow, grox/flows/ptos/classifier.py, where it is assigned from a constant named GEMMA rather than GEMMA_2, so oai_gemma4 is a house naming convention carrying no version information.

So the accurate sentence is that a Gemma-family constant is referenced, and no more. The separate reply-scoring path is explicit: GROK_4_MINI_CRITICAL, with GROK_4_1_FAST_HEDGEHOG_CRITICAL as fallback, scoring on a nought-to-three rubric where zero triggers the label.

One small detail with disproportionate consequences. Where a reason is stored alongside the score, it is stored as reasoning[-500:]. That is the last five hundred characters, a tail slice. If a user is ever shown why their reply was scored zero, they will be shown the end of a sentence.


What the DSA actually requires

X is a designated Very Large Online Platform under the EU Digital Services Act, and it is tempting to reach for a breach. The evidence does not support one, and saying so plainly is the point of doing this properly.

Screenshot_20260814_171353_Brave

Article 27 requires the “main parameters” of a recommender system and the “most significant” criteria; it does not require every parameter or the algorithm. Article 17 requires a clear and specific statement of reasons for restrictions on visibility, and Recital 55 is explicit that this includes demotion in ranking and shadow banning. Article 17(3)(c) requires disclosure that automated means were used, not disclosure of how they work. Article 15(1)(e) requires a qualitative description of automated moderation together with indicators of accuracy and possible error rate.

Screenshot_20260814_171348_Brave

Nowhere in the transparency provisions is a platform required to publish classifier prompts. The only circumvention or security carve-out in the text sits at Article 40(5)(b), and that governs researcher data access, not moderation transparency.

So the position is this: excluding the prompts does not breach the DSA, because the DSA never asked for them. But X cannot claim a DSA exemption for the exclusion either, because none exists for transparency duties. The gameability rationale is unaddressed by the regulation rather than authorised by it, and the gap that remains is between a voluntary claim of “a new standard for transparency” and what was actually shipped.

Two further points sit awkwardly against Article 15(1)(e). The first is that a reply scored 0.0 and labelled RiskyHighVizReply is, on the face of Recital 55, a restriction of visibility that an EU user is owed a statement of reasons for. The second is that the accuracy indicators the article asks for demonstrably exist. This code is instrumented throughout: positive and negative detection counts, counts broken out by author case, counts of malformed model output repaired by json_repair, counts of output invalid enough to raise, and skip counts broken out by reason including the exemption itself. X knows exactly how often it declines to label a government account. I have located no publication of that number.

The Commission is not a bystander here. On 5 December 2025 it issued a €120 million fine against X, its first non-compliance decision under the DSA, covering the blue checkmark, the ad repository and researcher data access. A separate proceeding opened in December 2023 was extended on 26 January 2026 to examine recommender-system risk management. That strand is live.


The part where geography decides

An EU user whose reply is labelled has, in principle, an Article 17 right to a statement of reasons, a researcher-access route under Article 40, and a regulator already inside X’s recommender system.

A South African user has none of those. POPIA section 71 restricts decisions based solely on automated processing that carry legal consequences or substantially affect the data subject. A visibility label on a reply almost certainly clears neither threshold. There is no local analogue to Article 27, Article 17 or Article 40. The Competition Commission’s Media and Digital Platforms Market Inquiry examined algorithmic distribution and reported in November 2025, but it is a competition instrument; it creates no right to be told why your post was demoted.

Screenshot_20260814_163525_Brave

There is a further wrinkle in the code itself. The SafetyLabel type carries two fields I have not mentioned so far: applicable_countries and applicable_users, the latter a list of perspectival users. A label is therefore not simply on or off against a post. It can be scoped to fire in particular jurisdictions, or to apply only when specific accounts are the viewer. The type also carries expires_at_msec, so labels can be time-bounded, and holdback_experiment, so a label can be attached to an experiment. That complicates the sentence I was about to write. It is not quite true that the same code produces the same outcome everywhere and only the remedy differs by jurisdiction. The data model has jurisdiction built into it, and the platform can and does distinguish where a viewer is sitting before deciding what to show them. Which sharpens the point rather than blunting it. A system that already scopes visibility by country is a system that already knows the answer to "which law applies here." The capability to differentiate exists and is exercised. What does not exist, outside the EU, is any obligation to tell the affected user that it was.

Same code, same label, same account. Whether a remedy exists depends on where the user is sitting. That pattern keeps recurring in this work and it has not moved.


Questions put

To X and xAI: under what policy basis were the prompt templates excluded, given the stated commitment to a new standard for transparency; why are high_page_rank_v2 and grey-badge accounts exempted from the label while the ranking score is still written; and what is the value of task.apply_label_from_grox.skipped.count under reason="high_page_rank_or_grey_badge" for the last reporting period. When a RiskyHighVizReply label is applied, are applicable_countries or applicable_users ever populated, and if so on what basis?

To the European Commission, DG Connect: does the Commission recognise gameability as a legitimate ground to limit transparency disclosure outside Article 40(5)(b); does the extended recommender-system proceeding cover reply-visibility labels; and where a reply is scored zero and labelled, is an Article 17 statement of reasons owed.

To the Information Regulator: does algorithmic visibility reduction fall within POPIA section 71, and if not, does any South African law give a user the right to know why their content was demoted.

Answers, or documented non-answers, will be published.


Method and limits

Everything above about prompts.py, plan_coordinated_spam.py, classifier_coordinated_spam.py, classifier_reply_ranking.py, task_coordinated_spam.py, task_spam_detection.py, task_write.py, strato_loader.py, state_coordinated_spam.py, generators.py, task_filter.py and visibility-filtering/get_safety_labels.rs is Tier A, read from the source at commit 47c1bcd. That commit is dated 13 August 2026, captured from the repository file header at time of reading.

The applicable_countries, applicable_users, expires_at_msec and holdback_experiment fields are Tier A from visibility-filtering/tweet_safety_label.rs at commit 47c1bcd. I have not established how, or whether, country scoping is used in the reply-spam flow specifically. The fields exist on the label type; nothing I have read shows what values StratoApplyLabelFromGrox writes into them.

The three filter constants and rejection reasons for the coordinated-spam path are Tier A from the full text of TaskCoordinatedSpamFilter.

The lineage of high_page_rank_v2 from the Tweepcred score in the 2023 release is Tier B, an inference from the naming and the identical conceptual role, not a documented fact.

The absence of prior coverage of these specifics is bounded: no reporting on the reply-spam pipeline particulars was located as of publication, which is not the same as none existing.

I have not seen the output of the Under the Hood tool; the rollout was described as a randomised test group and I am not in it. That the reply ranking score is written to a separate store from the safety labels is Tier A from task_write.py and visibility-filtering/get_safety_labels.rs at commit 47c1bcd. The SafetyLabel type carries its own optional score field; that is distinct from the reply ranking score and is only present where a label exists. That this endpoint is what backs the user-facing export is Tier B, an inference from its position in the visibility-filtering service. If the export turns out to carry ranking scores or another reach signal, the claim narrows and I will correct it.

I have not read constants.py, so I cannot say what GEMMA_2 expands to.

The repository is updated on a stated cadence. Every claim here is against a pinned commit, not a branch, and the files are archived.


Sources


Clayton Bax

Published under ONYX Digital Intelligence
Following the #OnyxAudit methodology.

"Adjacent to true is not true."

Truth has no flag nor favour, only a standard. And it's heavy

#DSA #POPIA #X #algorithms #transparency