FA-10276 / Observability / Open access
Metric series identity: Counter and gauge identities collide · case 01
Counter and gauge identities collide.
ROOT CAUSE
The implementation substitutes unit, None for unit, kind, so counter and gauge identities collide.
VERIFIED REPAIR
Include the instrument kind in identity.
Unsuccessful approach: The attempted repair substitutes unit, unit. Fixture 1 still yields ('requests', (('a', '2'), ('z', '1')), 'count', 'count') instead of ('requests', (('a', '2'), ('z', '1')), 'count', 'counter').
Case contract
A series identity includes metric name, sorted label key-value pairs excluding configured dropped keys, measurement unit, and instrument kind. Dropping values never drops other equal-valued labels.
Why this case matters
An offline model of metric series identity, suitable for testing build and release tooling without external services.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(name, labels, dropped, unit, kind):
return (name, tuple(sorted((k,v) for k,v in labels.items() if k not in dropped)), unit, None)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('fixture 1', solve('requests', {'z': '1', 'a': '2'}, [], 'count', 'counter'), ('requests', (('a', '2'), ('z', '1')), 'count', 'counter'))
check('fixture 2', solve('requests', {'a': '2', 'z': '1'}, [], 'count', 'counter'), ('requests', (('a', '2'), ('z', '1')), 'count', 'counter'))
check('fixture 3', solve('requests', {'a': '2', 'z': '1'}, ['z'], 'count', 'counter'), ('requests', (('a', '2'),), 'count', 'counter'))
check('fixture 4', solve('requests', {'a': 'same', 'b': 'same'}, ['a'], 'count', 'counter'), ('requests', (('b', 'same'),), 'count', 'counter'))
check('fixture 5', solve('latency', {}, [], 'seconds', 'histogram'), ('latency', (), 'seconds', 'histogram'))
check('fixture 6', solve('latency', {}, [], 'milliseconds', 'gauge'), ('latency', (), 'milliseconds', 'gauge'))
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| fixture 1 | ['requests', [['a', '2'], ['z', '1']], 'count', None] | ['requests', [['a', '2'], ['z', '1']], 'count', 'counter'] | Failed |
| fixture 2 | ['requests', [['a', '2'], ['z', '1']], 'count', None] | ['requests', [['a', '2'], ['z', '1']], 'count', 'counter'] | Failed |
| fixture 3 | ['requests', [['a', '2']], 'count', None] | ['requests', [['a', '2']], 'count', 'counter'] | Failed |
| fixture 4 | ['requests', [['b', 'same']], 'count', None] | ['requests', [['b', 'same']], 'count', 'counter'] | Failed |
| fixture 5 | ['latency', [], 'seconds', None] | ['latency', [], 'seconds', 'histogram'] | Failed |
| fixture 6 | ['latency', [], 'milliseconds', None] | ['latency', [], 'milliseconds', 'gauge'] | Failed |
SHA-256 / 634599d736d276b888632539c80cf29f0b5361f8e8d69baaa6c6798735f5163e
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(name, labels, dropped, unit, kind):
return (name, tuple(sorted((k,v) for k,v in labels.items() if k not in dropped)), unit, unit)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('fixture 1', solve('requests', {'z': '1', 'a': '2'}, [], 'count', 'counter'), ('requests', (('a', '2'), ('z', '1')), 'count', 'counter'))
check('fixture 2', solve('requests', {'a': '2', 'z': '1'}, [], 'count', 'counter'), ('requests', (('a', '2'), ('z', '1')), 'count', 'counter'))
check('fixture 3', solve('requests', {'a': '2', 'z': '1'}, ['z'], 'count', 'counter'), ('requests', (('a', '2'),), 'count', 'counter'))
check('fixture 4', solve('requests', {'a': 'same', 'b': 'same'}, ['a'], 'count', 'counter'), ('requests', (('b', 'same'),), 'count', 'counter'))
check('fixture 5', solve('latency', {}, [], 'seconds', 'histogram'), ('latency', (), 'seconds', 'histogram'))
check('fixture 6', solve('latency', {}, [], 'milliseconds', 'gauge'), ('latency', (), 'milliseconds', 'gauge'))
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| fixture 1 | ['requests', [['a', '2'], ['z', '1']], 'count', 'count'] | ['requests', [['a', '2'], ['z', '1']], 'count', 'counter'] | Failed |
| fixture 2 | ['requests', [['a', '2'], ['z', '1']], 'count', 'count'] | ['requests', [['a', '2'], ['z', '1']], 'count', 'counter'] | Failed |
| fixture 3 | ['requests', [['a', '2']], 'count', 'count'] | ['requests', [['a', '2']], 'count', 'counter'] | Failed |
| fixture 4 | ['requests', [['b', 'same']], 'count', 'count'] | ['requests', [['b', 'same']], 'count', 'counter'] | Failed |
| fixture 5 | ['latency', [], 'seconds', 'seconds'] | ['latency', [], 'seconds', 'histogram'] | Failed |
| fixture 6 | ['latency', [], 'milliseconds', 'milliseconds'] | ['latency', [], 'milliseconds', 'gauge'] | Failed |
SHA-256 / 5fbae1a343ef42e7c8a0dcdfaca221146ef11289e606b6f721b5f5f09a78f12a
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(name, labels, dropped, unit, kind):
return (name, tuple(sorted((k,v) for k,v in labels.items() if k not in dropped)), unit, kind)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('fixture 1', solve('requests', {'z': '1', 'a': '2'}, [], 'count', 'counter'), ('requests', (('a', '2'), ('z', '1')), 'count', 'counter'))
check('fixture 2', solve('requests', {'a': '2', 'z': '1'}, [], 'count', 'counter'), ('requests', (('a', '2'), ('z', '1')), 'count', 'counter'))
check('fixture 3', solve('requests', {'a': '2', 'z': '1'}, ['z'], 'count', 'counter'), ('requests', (('a', '2'),), 'count', 'counter'))
check('fixture 4', solve('requests', {'a': 'same', 'b': 'same'}, ['a'], 'count', 'counter'), ('requests', (('b', 'same'),), 'count', 'counter'))
check('fixture 5', solve('latency', {}, [], 'seconds', 'histogram'), ('latency', (), 'seconds', 'histogram'))
check('fixture 6', solve('latency', {}, [], 'milliseconds', 'gauge'), ('latency', (), 'milliseconds', 'gauge'))
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| fixture 1 | ['requests', [['a', '2'], ['z', '1']], 'count', 'counter'] | ['requests', [['a', '2'], ['z', '1']], 'count', 'counter'] | Passed |
| fixture 2 | ['requests', [['a', '2'], ['z', '1']], 'count', 'counter'] | ['requests', [['a', '2'], ['z', '1']], 'count', 'counter'] | Passed |
| fixture 3 | ['requests', [['a', '2']], 'count', 'counter'] | ['requests', [['a', '2']], 'count', 'counter'] | Passed |
| fixture 4 | ['requests', [['b', 'same']], 'count', 'counter'] | ['requests', [['b', 'same']], 'count', 'counter'] | Passed |
| fixture 5 | ['latency', [], 'seconds', 'histogram'] | ['latency', [], 'seconds', 'histogram'] | Passed |
| fixture 6 | ['latency', [], 'milliseconds', 'gauge'] | ['latency', [], 'milliseconds', 'gauge'] | Passed |
SHA-256 / 9e2ad6d4ea502456262bee663f6abc3f165cc50c3dc77f1f1d028f1f461a694b
Verification & scope
This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:38:37.493318+00:00.
Case digest / 7a304957defa75b8d88482b1f28d5e3ccf81642da07b42ae5a5e88bc8f6ae983