M3 — Cardinality is bounded
New metric labels/tags are drawn from a bounded, enumerable set - no raw user ID, email, full URL with path params, or similar unbounded value used as a label.
Python Implementation
M3: new metric labels/tags are drawn from a bounded, enumerable set — no raw user ID, email, full URL, or similar unbounded value used as a label. rubric_store/definitions/metrics.yaml tags this `both`; the static half checks the *names* of keyword arguments passed to a `.labels(...)`/ `.tags(...)` call against a blocklist of inherently high-cardinality field names. It cannot verify the actual *value* is bounded (a variable named `status` could still hold something unbounded) — that's the LLM half's job. Only fires on diffs that actually add a `.labels(...)`/`.tags(...)` call; a diff with no new metric labels at all produces no M3 finding, matching the rubric's own "N/A" treatment for that case (rubric_store/ validation_notes.md's PAY-482 example: "pass (N/A) — No new metric labels introduced.") rather than fabricating one.
Java Implementation
M3 for Java: new metric tags are drawn from a bounded, enumerable set.
Java's Micrometer idiom (`.tag("key", value)` / `.tags(...)`) passes the tag
key as a positional string literal — there are no keyword-argument names to
check the way the Python version does, so this checks the literal key
strings themselves. See ast_helpers.has_unbounded_metric_tag.
Only fires on diffs that add a `.tag(...)`/`.tags(...)` call, matching the
Python version's "no new labels at all -> no finding" (the rubric's own
N/A-as-pass treatment), not a fabricated pass/fail.
Go Implementation
M3 for Go: new metric labels are drawn from a bounded, enumerable set.
Go's prometheus client_golang idiom (`.With(prometheus.Labels{"key":
value})`) has no keyword-argument names to check — this looks textually
across the whole call's argument text for an unbounded-looking field name.
See ast_helpers.has_unbounded_metric_label.
Only fires on diffs that add a `.With(...)` call, matching the Python/Java
versions' "no new labels at all -> no finding."