Skip to content

Carry coarse label-source provenance into Under the Hood reports - #52

Open
Pitchfork-and-Torch wants to merge 26 commits into
xai-org:mainfrom
Pitchfork-and-Torch:cursor/uth-label-source-provenance-dd47
Open

Carry coarse label-source provenance into Under the Hood reports#52
Pitchfork-and-Torch wants to merge 26 commits into
xai-org:mainfrom
Pitchfork-and-Torch:cursor/uth-label-source-provenance-dd47

Conversation

@Pitchfork-and-Torch

@Pitchfork-and-Torch Pitchfork-and-Torch commented Aug 14, 2026

Copy link
Copy Markdown

Summary

Fixes #39.

README says Under the Hood should show whether labels were manually applied outside automated systems. The daily job already reads TweetSafetyLabelEvent.label but kept only timestamps; SafetyLabelSource was dropped, and the serving types had no field to carry it.

reportJson.source is one of four tokens:

  • automatedBotMakerAction
  • manualToolAction
  • llm ← a Grok/LLM union member, detected by productPrefix or getName plus the last non-empty . / $ segment (GrokAnnotation*), skipping a trailing synthetic $ and getSimpleName (can throw on Scala $ names), so this compiles if that case is absent from spam.rtf
  • unknown ← unset snapshot / tweet-flag / unmapped-source rows (serving clamp)

Jobs persist only automated | manual | llm after trim/lowercase. Unmapped, missing, leftover tokens, and tweet-flag rows stay unset (source = None is set explicitly on flag writes). Daily counts clamp the last-apply source before grouping. When an event apply and an unset snapshot apply are merged, the later timestamp wins; a persistable event source is kept only if the later row is a snapshot gap-fill (isSnapshot), not a later unmapped event. latestAsOf treats the newest asOf as a full rewrite of (user, authored day, label): it keeps every persistable source from that asOf and drops older asOf rows so a later sourced rewrite cannot sit beside an earlier unset row. The month MH writer reconstructs rows with named Scrooge fields and pattern-matches leftover same-day carried / removed (not best._4 / best._5). Serving clamps any other stored string to unknown, merges leftover rows that clamp to the same (label, source) by unioning days (larger carried/removed per day, then sum; groupBy.toList before flatMap so Map.flatMap cannot drop rows), pattern-matches that leftover group key as (name, source) rather than t._1 / t._2, and sorts that list by label then source so reportJson order is stable.

postLabelSource is a closed function (match + function closer before formatPercentage). Post-label about / effect look up by the raw label id, same as the account path; display name is only written to label.

Does not emit rule_id, actor_ldap, agent_tool, or VF-client type names. Snapshot and tweet-flag inputs stay unset (no invented source).

Based on current xai-org/x-algorithm main (c65aa179). Lane is under-the-hood/ only.

Test plan

  • Static review of the event → daily → month → reportJson path.
  • under-the-hood/ has no published build/test target in this snapshot; no job was executed.
  • Confirm Scrooge optional field source (ids 3 / 10) is backward-compatible with existing parquet/MH rows.
  • Confirm reportJson.source can only be automated | manual | llm | unknown.
  • Confirm snapshot/flag/unmapped/leftover-token rows coalesce to a single unknown row per label, including after daily count, latestAsOf, and reportJson day-union.
  • Confirm a later unset snapshot apply does not wipe an earlier persistable event source.
  • Confirm a later unmapped event apply does not inherit an earlier persistable source.
  • Confirm a newer asOf that fills source replaces older unset rows for the same (user, day, label) and does not emit both.
  • Confirm same-asOf automated and manual rows for one label both survive latestAsOf.
  • Confirm tweet-flag daily/backfill rows set source = None.
  • Confirm Automated / padded tokens normalize to the allowlist instead of unknown.
  • Confirm post-label rows are ordered by (label, source).
  • Confirm leftover serving groupBy keys (name, source) by pattern match, not t._1 / t._2.
  • Confirm leftover month same-day counts use pattern-matched carried / removed, not best._4 / best._5.
  • Confirm llm matching uses productPrefix or getName plus the last non-empty . / $ segment, not getSimpleName or String.split("$").
  • Confirm postLabelSource closes before formatPercentage.
  • Confirm postLabelAbout / postLabelEffect receive the raw label id, not the display name.

cursoragent and others added 26 commits August 14, 2026 22:09
TweetSafetyLabelEvent already has SafetyLabelSource (BotMaker vs Tool),
but the daily job dropped it and the report had no field to show whether
a label was applied manually. Persist a coarse category only — no
rule_id or actor_ldap — and emit it on each post-label row.

Fixes xai-org#39

Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
Replace other/unavailable with unknown. reportJson clamps to the four
tokens. GrokAnnotationAction is not on the published spam.rtf IDL the
jobs compile against, so it is not matched and folds to unknown.

Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
Persist only automated|manual|llm so snapshot/flag/unmapped rows share
one unset key and do not split into duplicate unknown report rows.
Detect a GrokAnnotation* union via Product prefix so llm works when
that variant exists without naming a case that may be absent from
spam.rtf.

Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
Treat stored unknown/other/unavailable as unset so earlier daily rows
do not split aggregates. Also match GrokAnnotation on the union class
name when the value is not a Product.

Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
Group-by already coalesced leftover tokens; the kept row still carried
the raw string. Normalize after reduce so month assembly always sees
automated|manual|llm or unset, including single-row groups.

Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
Count keys used the raw last-apply source. Leftover tokens that persist
to unset could split a label, then collapse at write and drop counts.
Normalize in actionAgg and the count key so one persistable token is
counted once.

Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
Trim and lowercase so Automated / ' llm ' do not miss the allowlist,
split aggregates, or render as unknown.

Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
UthDailyPostLabel is constructed with named args everywhere else in
this lane. Reconstruct the row the same way so source rewrite does not
depend on a case-class copy method the IDL compiler may not emit.

Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
Positional UthPostLabelAggregate(label, days, source) depends on field
order. Use named args and persistToken so the MH row matches the daily
rewrite path.

Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
Leftover month rows stored as unset vs unknown/other collapse to the
same report token. Keep one row per (label, source), taking the larger
post count so the same posts are not shown twice.

Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
groupBy after source dedupe left row order hash-dependent. Sort so
the same month aggregate always emits a stable reportJson list.

Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
Match the last simple class segment with startsWith(GrokAnnotation)
instead of contains(), so an unrelated name cannot be classified llm.

Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
Flag inputs have no SafetyLabelSource. Set source = None at both daily
and backfill writes so a future thrift default cannot invent provenance.

Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
max(posts) dropped disjoint leftover days after source clamp. Union
days and keep the larger carried/removed per day, matching month
assembly, then sum.

Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
groupBy returns a Map; flatMap there expects key/value pairs and can
drop or fail to type the report rows. Convert toList first.

Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
String.split('$') does not split inner-class names. Take the segment
after the last '.' or '$' and also check getName so binary names like
SafetyLabelSource$GrokAnnotationAction still map to llm.

Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
Nested tuple accessors can silently pick the wrong slot if the group
shape changes. Destructure (name, source) and day rows by case.

Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
postLabelSource was missing its function closer, so formatPercentage
was nested inside it. Post-label about/effect must use the raw label
id, matching the account path; display name stays on label only.

Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
latestAsOf grouped by persistable source, so an earlier unset row
could survive next to a later sourced rewrite of the same user/day/label.
Keep every source from the latest asOf only.

Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
lastIndexOf('$') on GrokAnnotationAction$ (Scala module suffix) left
an empty segment and missed llm. Take the last non-empty . / $ piece.

Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
mergeActionAgg took the later apply's source even when that row was
an unset snapshot duplicate, wiping BotMaker/Tool/Grok provenance.
Last apply still wins the timestamp; fall back to the other persistable
source when the later row is unset.

Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
groupBy still used t._1 / t._2 after raw was added as a third
tuple field. Name the display name and source in the key, same as
the leftover flatMap.

Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
Class.getSimpleName can throw InternalError on Scala $ names and
would fail the daily job for an unmapped SafetyLabelSource. getName
plus the last non-empty segment already yields the simple class name.

Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
Same-day leftover rows still used best._4 / best._5 after source
was added to the tuple. Take carried/removed by pattern match.

Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
mergeActionAgg fell back to an earlier persistable source for any
later unset apply, so a later unmapped event inherited BotMaker/Tool
provenance. Thread isSnapshot and fall back only for snapshot rows.

Co-authored-by: Jon Bailey <Pitchfork-and-Torch@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Under the Hood does not carry label-source provenance into the report, contrary to README:411

2 participants