Summary
Fold string equality into the typed equals matcher and deprecate the
string-only exact match type. Today equality is split: exact (string) vs.
equals (Value, every non-string scalar). Once Value gains a string
variant, all equality — string and non-string — can be expressed through a
single equals, and exact becomes a backward-compatibility alias. This issue
tracks doing that deprecation correctly.
Context: the future-direction note was added in #49
(spec.md → "Typed and Comparison Matching"; proto/tero/policy/v1/shared.proto → Value).
Target end state
equals: "checkout" is valid and means string equality (Value.string_value).
exact: "checkout" is exactly equivalent to equals: { string_value: "checkout" } and is deprecated (kept for compatibility, removed only in a future major version).
- One mental model:
exact and equals are the same concept, differing only by the value's type.
Required changes
Proto (shared.proto)
Spec (spec.md)
Semantics that must change carefully
Marshaling / canonical form
Conformance (usetero/policy-conformance)
Downstream implementations
Backward compatibility & sequencing
- Now (minor): add
string_value (additive), document exact as deprecated, accept equals strings everywhere exact worked. Old policies using exact keep working unchanged; old readers ignore string_value and would treat such a matcher as "no match condition set" → inert + reported compile error (fail-open), which is acceptable for clients that predate the change.
- Transition: tooling and generated policies prefer
equals; exact emitted only for compatibility.
- Future (major): optionally remove
exact, reserving its field numbers.
Open questions
- Do we want
string_value to also be reachable via plain scalar shorthand under equals (equals: "x"), which is the ergonomic win, vs. requiring equals: { string_value: "x" }? (Recommend: accept the bare-string shorthand — that is the whole point of unifying.)
- Should
exact get [deprecated = true] immediately (surfaces compiler warnings downstream) or only a doc-comment deprecation until the transition step?
Summary
Fold string equality into the typed
equalsmatcher and deprecate thestring-only
exactmatch type. Today equality is split:exact(string) vs.equals(Value, every non-string scalar). OnceValuegains a stringvariant, all equality — string and non-string — can be expressed through a
single
equals, andexactbecomes a backward-compatibility alias. This issuetracks doing that deprecation correctly.
Context: the future-direction note was added in #49
(
spec.md→ "Typed and Comparison Matching";proto/tero/policy/v1/shared.proto→Value).Target end state
equals: "checkout"is valid and means string equality (Value.string_value).exact: "checkout"is exactly equivalent toequals: { string_value: "checkout" }and is deprecated (kept for compatibility, removed only in a future major version).exactandequalsare the same concept, differing only by the value's type.Required changes
Proto (
shared.proto)Value:string string_value = 6;(additive → non-breaking).exactfields inLogMatcher/MetricMatcher/TraceMatcheras deprecated ([deprecated = true]and a comment pointing toequals). Do not remove them — removal is breaking and must wait for a major version; the field numbers must stay reserved if ever removed.Spec (
spec.md)string_valueto theValuedefinition and to theequalsdescription in all three match-type tables.Valuenow does have a string variant; onlyNumericValueremains restricted.equals(e.g.equals: "foo") MUST be rejected. After this change,equals: "foo"MUST be accepted and map tostring_value. (A string supplied to a comparison matcher is still rejected.)exactis deprecated in favor ofequals.Semantics that must change carefully
case_insensitive. The spec currently sayscase_insensitivehas no effect onequals(it applies only to the string match types). Once string equality lives inequals,case_insensitiveMUST apply toequalswhen the value isstring_value, and continue to have no effect for bool/int/double/bytes/hex values. This is the subtlest behavior change and needs an explicit rule + conformance coverage.exacton a bytes-typed field is currently hex-coerced (a string literal is decoded as hex). That coercion MUST move toequalsstring_value: astring_valuematched against a bytes-typed field is interpreted as hex, identical to today'sexact-on-bytes behavior. Otherwise deprecatingexactwould regress ergonomic id matching (equals: "8a3f…"onTRACE_FIELD_SPAN_ID).exact: Xandequals: { string_value: X }MUST produce identical results (includingcase_insensitive,negate, and bytes coercion), so implementations can treatexactas sugar for the string variant ofequals.Marshaling / canonical form
equals(withstring_value) when marshaling new/normalized policies, but MUST NOT rewrite storedexacton round-trip in a way that breaks older readers. Document the chosen policy so tools agree.Conformance (usetero/policy-conformance)
equals: "..."string equality;exact≡equals: {string_value}equivalence;case_insensitiveonequalsstring;equalsstring on a bytes field (hex coercion); and a deprecated-exact-still-works case.Downstream implementations
string_value, routeexactthrough the same path, applycase_insensitive/bytes-coercion toequalsstrings. (Relates to policy-go#69.)Backward compatibility & sequencing
string_value(additive), documentexactas deprecated, acceptequalsstrings everywhereexactworked. Old policies usingexactkeep working unchanged; old readers ignorestring_valueand would treat such a matcher as "no match condition set" → inert + reported compile error (fail-open), which is acceptable for clients that predate the change.equals;exactemitted only for compatibility.exact, reserving its field numbers.Open questions
string_valueto also be reachable via plain scalar shorthand underequals(equals: "x"), which is the ergonomic win, vs. requiringequals: { string_value: "x" }? (Recommend: accept the bare-string shorthand — that is the whole point of unifying.)exactget[deprecated = true]immediately (surfaces compiler warnings downstream) or only a doc-comment deprecation until the transition step?