Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
af74a3d
feat: add SpecReference type and T.Specification() method
aaron-zeisler May 21, 2026
726e573
feat: propagate specifications from T to TestResult
aaron-zeisler May 21, 2026
9620a11
feat: surface specifications in JUnit XML output
aaron-zeisler May 21, 2026
9cf9243
feat: add specifications field to YAML test model schema
aaron-zeisler May 21, 2026
3b2eabb
chore: convert MIGRATIONS spec comments to t.Specification() calls
aaron-zeisler May 21, 2026
918cc41
chore: convert PS spec comments to t.Specification() calls
aaron-zeisler May 21, 2026
b0fd9df
chore: convert HOOK spec comments to t.Specification() calls
aaron-zeisler May 21, 2026
f56d9c8
chore: convert AUTOENVATTR spec comments to t.Specification() calls
aaron-zeisler May 21, 2026
19ed09b
chore: convert BIGSEG spec comments to t.Specification() calls
aaron-zeisler May 21, 2026
0a3d1d0
chore: convert DATASYSTEM and CSFDV2 spec comments to t.Specification…
aaron-zeisler May 21, 2026
283d818
chore: convert ENVFILTER and EVENTS spec comments to t.Specification(…
aaron-zeisler May 21, 2026
50a9b85
chore: convert CSSE and CSPE spec comments to t.Specification() calls
aaron-zeisler May 21, 2026
e90b544
chore: convert FLGMES and CSPE spec comments to t.Specification() calls
aaron-zeisler May 21, 2026
9808bca
chore: convert FLGEDETAIL and FLGEA spec comments to t.Specification(…
aaron-zeisler May 21, 2026
ade3546
chore: convert CONTEXT and CTXBLD spec comments to t.Specification() …
aaron-zeisler May 21, 2026
745c2b7
chore: convert FLGEA, FLGEDETAIL, and ATREF YAML comments to specific…
aaron-zeisler May 21, 2026
0313b38
fix: move t.Specification() calls before t.LongRunning()
aaron-zeisler May 21, 2026
da52305
fix: wrap long t.Specification() lines to satisfy 120-char linter
aaron-zeisler May 21, 2026
a80eb6b
fix: remove trailing empty comment lines before func declarations
aaron-zeisler May 21, 2026
fa42afc
fix: correct misattributed spec references caught by Bugbot
aaron-zeisler May 21, 2026
55149b3
docs: add AGENTS.md with spec traceability convention
aaron-zeisler May 21, 2026
f57b4d1
fix: remove t.Specification() from assertion helpers, reorder in YAML…
aaron-zeisler May 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# SDK Test Harness -- AI Agent Guide

## Spec traceability

Every contract test that exercises a formal SDK specification requirement **must** call
`t.Specification(specID, number, summary)` to record the link at runtime.

### Rules

1. **Placement.** `t.Specification()` calls go first in the test function body, before
`t.LongRunning()`, `t.RequireCapability()`, or any other `t.*` method.
2. **Multiple specs.** A single test may call `t.Specification()` more than once if it
exercises multiple requirements.
3. **YAML eval suites.** Use the `specifications` field at suite level instead of Go calls.
The parameterized test runner propagates these to `t.Specification()` automatically.
4. **Spec source.** Spec IDs (e.g. `HOOK`, `DATASYSTEM`, `FDV2PL`) and requirement numbers
come from the `sdk-specs` repo's `main` branch.
5. **Line length.** Keep the summary string short enough that the full `t.Specification()`
call stays under 120 characters. Break onto a second line if needed.
6. **Helper functions.** Functions that don't accept `*ldtest.T` cannot call
`t.Specification()`. Use a `// SPEC_ID X.Y.Z: summary` comment as a fallback.

### Why

Spec references are surfaced as `<property>` elements in JUnit XML output, making
specification coverage visible in CI dashboards and test reports. This is the Go equivalent
of OpenFeature's `@Specification` annotation (Java) / `[Specification]` attribute (.NET).

### Examples

Go test:

```go
func (c CommonStreamingTests) RecoverableFallback(t *ldtest.T) {
t.Specification("DATASYSTEM", "1.2.6", "recoverable error triggers fallback")
t.Specification("CSFDV2", "8.2.1", "recovery timeout default is 300 seconds")
t.LongRunning()
// ... test body ...
}
```

YAML eval suite:

```yaml
name: attribute references
specifications:
- spec: "ATREF"
number: "1.2.1"
summary: "plain references treated as literal attribute name"
- spec: "ATREF"
number: "1.2.2"
summary: "~1 escape sequence interpreted as /"
```

## Linting

Run `golangci-lint` after modifying Go files. The repo enforces a 120-character line limit
(`lll`) among other checks. See `.golangci.yml` for the full configuration.
7 changes: 7 additions & 0 deletions data/data-files/client-side-eval/basic-values.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
---
name: basic values - <TYPE_NAME>
specifications:
- spec: "FLGEA"
number: "1.1.2"
summary: "client-side SDKs surface pre-evaluated outputs (value, variationIndex, reason)"
- spec: "FLGEA"
number: "1.6.3"
summary: "post-evaluation returns correct variation value or default"

parameters:
- TYPE_NAME: bool
Expand Down
13 changes: 13 additions & 0 deletions data/data-files/client-side-eval/errors-wrong-type.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@ name: wrong type errors

# These test are only relevant for SDKs that have variation methods for specific types
requireCapability: strongly-typed
specifications:
- spec: "FLGEA"
number: "1.6.3"
summary: "post-evaluation type mismatch returns WRONG_TYPE error and default value"
- spec: "FLGEA"
number: "1.3"
summary: "evaluation reason ERROR with errorKind WRONG_TYPE"
- spec: "FLGEDETAIL"
number: "1.3.1"
summary: "errorKind WRONG_TYPE when flag value does not match requested type"
- spec: "FLGEDETAIL"
number: "1.1.2"
summary: "default value returned on type mismatch"

# There is no value type expectation when evaluating all flags
skipEvaluateAllFlags: true
Expand Down
13 changes: 13 additions & 0 deletions data/data-files/client-side-eval/errors.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
---
name: errors - <TYPE_NAME>
specifications:
- spec: "FLGEA"
number: "1.6.1"
summary: "flag not found returns ERROR with errorKind FLAG_NOT_FOUND"
- spec: "FLGEDETAIL"
number: "1.2.2"
summary: "reason kind ERROR when flag could not be evaluated"
- spec: "FLGEDETAIL"
number: "1.3.1"
summary: "errorKind FLAG_NOT_FOUND when flag key unknown"
- spec: "FLGEDETAIL"
number: "1.1.2"
summary: "default value returned when evaluation fails"

skipEvaluateAllFlags: true

Expand Down
13 changes: 13 additions & 0 deletions data/data-files/client-side-eval/reasons.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
---
name: evaluation reasons - <NAME>
specifications:
- spec: "FLGEA"
number: "1.3"
summary: "all evaluation reason kinds (OFF, FALLTHROUGH, TARGET_MATCH, RULE_MATCH, PREREQUISITE_FAILED, ERROR)"
- spec: "FLGEDETAIL"
number: "1.2.2"
summary: "reason kind values OFF, FALLTHROUGH, TARGET_MATCH, RULE_MATCH, PREREQUISITE_FAILED, ERROR"
- spec: "FLGEDETAIL"
number: "1.2.3"
summary: "ruleIndex, ruleId fields for RULE_MATCH; prerequisiteKey for PREREQUISITE_FAILED; inExperiment for experiments"
- spec: "FLGEDETAIL"
number: "1.3.1"
summary: "errorKind MALFORMED_FLAG and EXCEPTION"

parameters:
- NAME: off
Expand Down
10 changes: 10 additions & 0 deletions data/data-files/server-side-eval/attribute-references.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
---
name: attribute references
specifications:
- spec: "ATREF"
number: "1.2.1"
summary: "plain references (no leading slash) treated as literal attribute name"
- spec: "ATREF"
number: "1.2.2"
summary: "~1 escape sequence interpreted as /"
- spec: "ATREF"
number: "1.2.3"
summary: "~0 escape sequence interpreted as ~"

constants:
IS_MATCH:
Expand Down
4 changes: 4 additions & 0 deletions data/data-files/server-side-eval/builtin-attrs.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
---
name: built-in attributes other than kind
specifications:
- spec: "FLGEA"
number: "1.2"
summary: "context attribute lookup for built-in attributes"

constants:
IS_MATCH:
Expand Down
4 changes: 4 additions & 0 deletions data/data-files/server-side-eval/clause-kind-matching.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
---
name: clause kind matching
specifications:
- spec: "FLGEA"
number: "1.6.2.6"
summary: "clause evaluation for kind attribute"

constants:
IS_MATCH:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
---
name: bad attribute reference errors - <NAME>
specifications:
- spec: "ATREF"
number: "1.1.1"
summary: "non-conforming strings are not valid attribute references"

parameters:
- NAME: clause with no attribute
Expand Down
10 changes: 10 additions & 0 deletions data/data-files/server-side-eval/errors-wrong-type.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ name: wrong type errors

# These test are only relevant for SDKs that have variation methods for specific types
requireCapability: strongly-typed
specifications:
- spec: "FLGEA"
number: "1.6.3.3"
summary: "post-evaluation WRONG_TYPE error"
- spec: "FLGEDETAIL"
number: "1.3.1"
summary: "errorKind WRONG_TYPE when flag value does not match requested type"
- spec: "FLGEDETAIL"
number: "1.1.2"
summary: "default value returned on type mismatch"

# There is no value type expectation when evaluating all flags
skipEvaluateAllFlags: true
Expand Down
22 changes: 22 additions & 0 deletions data/data-files/server-side-eval/errors.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
---
name: evaluation failures (<TYPE_NAME>)
specifications:
- spec: "FLGEA"
number: "1.6.1"
summary: "FLAG_NOT_FOUND error"
- spec: "FLGEA"
number: "1.6.2.3"
summary: "MALFORMED_FLAG for invalid variation index"
- spec: "FLGEA"
number: "1.6.2"
summary: "off with undefined offVariation returns default"
- spec: "FLGEDETAIL"
number: "1.2.2"
summary: "reason kind ERROR when flag could not be evaluated"
- spec: "FLGEDETAIL"
number: "1.3.1"
summary: "errorKind FLAG_NOT_FOUND and MALFORMED_FLAG"
- spec: "FLGEDETAIL"
number: "1.1.2"
summary: "default value returned when evaluation fails"
- spec: "FLGEDETAIL"
number: "1.1.3"
summary: "variationIndex absent when default value returned"

constants:
CONTEXT: { kind: "user", key: "user-key" }
Expand Down
4 changes: 4 additions & 0 deletions data/data-files/server-side-eval/json-variations.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
---
name: JSON variations
specifications:
- spec: "FLGEA"
number: "1.1.2"
summary: "evaluation outputs with JSON variation values"

constants:
CONTEXT: { kind: "user", key: "x"}
Expand Down
4 changes: 4 additions & 0 deletions data/data-files/server-side-eval/negation-and-iteration.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
---
name: clause negation and value iteration
specifications:
- spec: "FLGEA"
number: "1.6.2.6"
summary: "clause negation and value iteration"

constants:
IS_MATCH:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
---
name: operators - date - bad syntax
specifications:
- spec: "FLGEA"
number: "1.6.2.7.3"
summary: "date-time operators with invalid syntax"

constants:
IS_NOT_MATCH:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
---
name: operators - date - bad type
specifications:
- spec: "FLGEA"
number: "1.6.2.7.3"
summary: "date-time operators with invalid type"

constants:
IS_NOT_MATCH:
Expand Down
4 changes: 4 additions & 0 deletions data/data-files/server-side-eval/operators-date-equal.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
---
name: operators - date - equal
specifications:
- spec: "FLGEA"
number: "1.6.2.7.3"
summary: "date-time operators with equal values"

constants:
IS_NOT_MATCH:
Expand Down
4 changes: 4 additions & 0 deletions data/data-files/server-side-eval/operators-date-unequal.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
---
name: operators - date - unequal
specifications:
- spec: "FLGEA"
number: "1.6.2.7.3"
summary: "date-time operators with unequal values (before, after)"

constants:
IS_MATCH:
Expand Down
4 changes: 4 additions & 0 deletions data/data-files/server-side-eval/operators-equality.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
---
name: operators - equality (<TYPE_NAME>)
specifications:
- spec: "FLGEA"
number: "1.6.2.7.1"
summary: "equality (in operator)"

constants:
IS_MATCH:
Expand Down
4 changes: 4 additions & 0 deletions data/data-files/server-side-eval/operators-numeric.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
---
name: operators - numeric
specifications:
- spec: "FLGEA"
number: "1.6.2.7"
summary: "numeric operators (lessThan, lessThanOrEqual, greaterThan, greaterThanOrEqual)"

constants:
IS_MATCH:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
---
name: operators - semver - bad syntax
specifications:
- spec: "FLGEA"
number: "1.6.2.7.4"
summary: "semantic version invalid syntax"

constants:
IS_NOT_MATCH:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
---
name: operators - semver - bad type
specifications:
- spec: "FLGEA"
number: "1.6.2.7.4"
summary: "semantic version invalid type"

constants:
IS_NOT_MATCH:
Expand Down
4 changes: 4 additions & 0 deletions data/data-files/server-side-eval/operators-semver-equal.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
---
name: operators - semver - equal
specifications:
- spec: "FLGEA"
number: "1.6.2.7.4"
summary: "semantic version equality (semVerEqual)"

constants:
IS_MATCH:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
---
name: operators - semver - unequal
specifications:
- spec: "FLGEA"
number: "1.6.2.7.4"
summary: "semantic version comparison (semVerLessThan, semVerGreaterThan)"

constants:
IS_MATCH:
Expand Down
4 changes: 4 additions & 0 deletions data/data-files/server-side-eval/operators-string.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
---
name: operators - string
specifications:
- spec: "FLGEA"
number: "1.6.2.7"
summary: "string operators (startsWith, endsWith, contains, matches)"

constants:
IS_MATCH:
Expand Down
4 changes: 4 additions & 0 deletions data/data-files/server-side-eval/prerequisites.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
---
name: prerequisites
specifications:
- spec: "FLGEA"
number: "1.6.2.4"
summary: "prerequisite evaluation"

constants:
CONTEXT: { key: "user-key" }
Expand Down
4 changes: 4 additions & 0 deletions data/data-files/server-side-eval/rollout-or-experiment.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
---
name: rollout or experiment - <NAME>
specifications:
- spec: "FLGEA"
number: "1.6.2.9"
summary: "rollouts and experiments"

constants:
MATCH_FALLTHROUGH_VARIATION_A:
Expand Down
4 changes: 4 additions & 0 deletions data/data-files/server-side-eval/rule-match.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
---
name: rule match (<TYPE_NAME>)
specifications:
- spec: "FLGEA"
number: "1.6.2.6"
summary: "flag rule evaluation"

constants:
VALUE_OFF: "off"
Expand Down
4 changes: 4 additions & 0 deletions data/data-files/server-side-eval/segment-match.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
---
name: segment match
specifications:
- spec: "FLGEA"
number: "1.6.2.8"
summary: "segment matching"

constants:
IS_MATCH:
Expand Down
Loading
Loading