Good question. The short answer is that anything without a bounded, enumerable value set never becomes a label, full stop. In the schema from the post, everything that stays a label comes from a closed vocabulary, things like capability, provider_alias from an internal allowlist, outcome, and validation_reason. Anything that can grow without limit, a raw URL, a query, a task ID, an exception message, never enters attributes or metrics at all, it goes to a separate access controlled system if policy even allows storing it. The other trick that helps more than people expect is bucketing instead of hashing. Benchmark age, for example, doesn't need an exact timestamp label, it becomes under_1h, 1d_to_7d, over_30d, which keeps the dimension useful for dashboards without creating a new series per value. Hashing a URL still leaves you with effectively unbounded cardinality, it just makes it unreadable too. The real fix is validating the record type at the boundary so a query or URL can't reach the exporter in the first place, which is what the ToolAttempt validation in the post is doing.