**Ticket: Support pre-expanded ValueSets and flatten expansion output to leaf codings**

The `ValueSet/$expand` operation currently does not use a stored expansion when a ValueSet is included via `compose.include.valueSet`. If you include a ValueSet that already contains a pre-calculated expansion, the operation ignores it and either fails (because no `compose` is present) or computes an expansion from the included ValueSet’s `compose` rules, which is unnecessary. We should respect pre-expanded content: when a ValueSet has a complete, unparameterized expansion, use its `contains` list directly as a source of concepts.

A related issue: when expanding a ValueSet that includes codes from a hierarchical code system (e.g., the `v3-ActMood` code system), the expansion currently returns a nested `contains` structure that includes abstract grouping concepts (e.g., `_ActMoodPredicate`). The output should instead be a flat array of fully-qualified codings (`system`, `code`, `display`), containing only **concrete leaf concepts** — the concepts that are actually selectable as values. Abstract internal nodes must be omitted.

**Expected behavior change:**

1. **Pre-expanded ValueSets** – If the ValueSet being expanded (or a ValueSet referenced by `compose.include.valueSet`) has an `expansion` with a non-empty `contains` list, no `parameter` block, and `total` equal to the length of `contains` (indicating a complete pre-expansion), that expansion should be used. The concepts from the pre-expansion should be recursively flattened into a flat list of codings. When a `contains` child itself has nested `contains`, only the leaf codings should be emitted, properly inheriting the `system` from the parent where necessary.

2. **Flattened leaf-only output** – For all expansion sources (explicit `include.concept`, code system subsumption, pre-expanded ValueSets), the final `expansion.contains` must be a flat list. Concepts that are not leaf nodes (i.e., that exist only to group other concepts in a hierarchy) must not appear in the flat output. This applies, for example, to codes like `_ActMoodPredicate`.

3. **Filter compatibility** – The existing `filter` query parameter (matching against `display` text) must continue to work across all concept sources, including those from pre-expanded ValueSets and hierarchical code systems after flattening.

**Edge cases to handle:**
- A pre-expanded ValueSet’s `contains` array might have a nested hierarchy several levels deep.
- When a `contains` element lacks its own `system`, the system from an enclosing element or from the `include.system` must be populated.
- The fast-path that handles a ValueSet with a pre-existing expansion (bypassing re-computation) must still respect the leaf-only flattening and filtering.
- The change must not break `$expand` for ValueSets that already work correctly without pre-expansions.

Include pre-expanded ValueSets and flatten expansion for output (#5414)


## Implement retry for transient `ResourceConflictException` when deploying bots

When a bot is deployed via the `/$deploy` operation, the server updates the AWS Lambda function’s configuration and then its code. In practice, `UpdateFunctionCode` can fail with a `ResourceConflictException` (“The operation cannot be performed at this time. An update is in progress…”) even after we’ve waited for the configuration update to settle. This causes the entire deploy to fail transiently; users or operators must manually retry the deployment.

We should make deploys self-healing:

- **Retry on `ResourceConflictException`** for the code update step. When the call to `UpdateFunctionCode` throws an `@aws-sdk/client-lambda` `ResourceConflictException`, wait a short period (e.g., 1 second) and retry, up to a reasonable limit (e.g., 5 attempts). Other exceptions must be re‑thrown immediately.
- **Remove the existing Active‑state polling** from the configuration update step. The polling loop currently calls `GetFunctionConfiguration` repeatedly until `State === 'Active'` before proceeding to update the code. That polling does not eliminate the conflict and adds unnecessary delay; the retry-on-conflict approach handles in‑progress updates more directly.

**Scope:** The change belongs in the server’s AWS deploy logic (`packages/server/src/cloud/aws/deploy.ts`). The corresponding test suite (`packages/server/src/cloud/aws/deploy.test.ts`) should be extended to simulate the conflict and verify that the code update is retried and eventually succeeds. The deploy endpoint must still return a success response after a retried conflict.

Automatically retry deploy bot on ResourceConflictException (#5388)


**Describe the bug**
The generic overloads `Is.AssignableFrom<T>()` and `Is.AssignableTo<T>()` do not behave the same as their non‑generic counterparts (`Is.AssignableFrom(Type)` and `Is.AssignableTo(Type)`). Two aspects are broken:

1. **Compile‑time type used instead of runtime type** – When the actual value is a derived type stored in a base‑typed variable, the generic constraints sometimes use the compile‑time type parameter rather than the object’s actual runtime type. For example, an assertion like:
   ```
   object x = new Derived();
   Assert.That(x, Is.AssignableTo<Derived>());
   ```
   can fail even though the non‑generic `Is.AssignableTo(typeof(Derived))` would pass.

2. **Null actual value causes incorrect behavior** – Passing a null value to the generic constraints can result in a `NullReferenceException` or an incorrect constraint result, whereas the non‑generic constraints handle null gracefully (the constraint should evaluate to false).

**Expected behavior**
The generic assignable constraints should mirror the logic of the non‑generic constraints:
- They must use the runtime type of the actual object (e.g., via `actual.GetType()`) for the type‑check, not the declared compile‑time type.
- When the actual value is `null`, the constraint must evaluate to false without throwing an exception.
- The direction of the assignability test must be correct: `AssignableFrom<T>` checks whether `T` is assignable *from* the actual’s type, and `AssignableTo<T>` checks whether the actual’s type is assignable *to* `T`.

**Impact**
This prevents users from relying on the generic form for common scenarios (e.g., nullable‑annotated tests, polymorphic objects), pushing them back to the less type‑safe non‑generic API. It also introduces inconsistency that can be confusing when migrating existing test code to generic syntax.

Fix generic assignable constraints to match non-generic behavior (#5277)


```
The `PropertyConstraint` reports the wrong `ActualValue` in its `ConstraintResult` when it wraps another `PropertyConstraint` (i.e., chained property access like `Has.Property("X").Property("Y")`). Instead of the value of the final (innermost) property, it currently hands out the value of the immediate property. This makes failure messages misleading: they display the intermediate object rather than the actual value that failed the inner constraint.

Fix the computation of `ActualValue` for `PropertyConstraint` so that it propagates whatever the inner constraint result considers its own actual value. No other constraints should be affected, and the semantics of `And` / `With` combinations (e.g., `Has.Property("A").And.Property("B")`) do not need to change.
```

Show correct actual value for chained property constraints


Allow category names to include `+` and `!` characters. Currently the framework marks a test as not runnable whenever any of its categories contains `!` or `+`. Remove this restriction and treat those characters as ordinary valid characters in category names. The corrected behavior must propagate through test discovery and filtering, so that filters (including those entered via XML) can correctly select tests whose categories contain `+` or `!`.


```
De-duplicate the ignore-until skip-reason message

The `IgnoreAttribute.ApplyToTest` method and the `IgnoredTestCaseData.Until` method each build the “Ignoring until <date>. <reason>” string and assign it to `PropertyNames.SkipReason`. The two copies differ only in how they obtain the reason, but the formatting and property-setting are identical. This duplication is a maintenance risk.

Extract the shared logic so that both call sites produce the same message and property updates while using a common helper. The final behavior must match exactly:
- When the ignore-until date is in the future, set `RunState` to `Ignored` (if appropriate).
- Set `PropertyNames.SkipReason` to `"Ignoring until {date}. {reason}"`, where the date is formatted with the `"u"` specifier (UTC sortable).
- Set `PropertyNames.IgnoreUntilDate` to the same UTC date string (already done at both sites; ensure this is preserved).
```

De-duplicate the ignore until message code


The `SetUpTearDownItem` currently bypasses `IMethodInfo` and calls `Reflect.InvokeMethod` with the raw `MethodInfo`. This means any custom `IMethodInfo` implementation (e.g., wrappers used for repeat or parameterized tests) is ignored during `SetUp` and `TearDown`, breaking tests that rely on those wrappers for setup behavior.

Make the `SetUp`/`TearDown` path invoke through `IMethodInfo.Invoke` consistently with how test bodies are invoked. To do this correctly:
- `IMethodInfo` must expose an `IsStatic` property, since callers need to decide whether to pass the fixture instance.
- `SetUpTearDownItem` must call `IMethodInfo.Invoke` instead of `Reflect.InvokeMethod`.
- All built-in `IMethodInfo` implementors (`MethodWrapper`) must provide the new `IsStatic` property.

No test-specific changes needed—just fix the contract and the call site.

Make SetUp/TearDown invoke via IMethodInfo


**Feature request: Expose current retry attempt via TestContext properties**

### Summary
When a test method carries a `[Retry(N)]` attribute, make the current retry attempt number available to the test body and to result reporters through the test properties dictionary.

### Acceptance criteria
1. For any test decorated with `[Retry(N)]`, the `TestContext.CurrentContext.Test.Properties` dictionary SHALL contain a new property whose value is the 1‑based attempt count.
   - First execution: value = `"1"`.
   - Second execution: value = `"2"`, etc.

2. The property MUST be set **before** the test method body runs on each attempt, so that test code can read it and alter its behavior (e.g., skip setup on a retry).

3. The property MUST be accessible even if a previous attempt threw an unhandled exception (the property is set before each attempt regardless).

4. After the test has completed (whether all retries are exhausted or a pass occurs), `result.Test.Properties` SHALL retain the value corresponding to the last attempt that executed. This allows test result reporters/CI integrations to inspect how many attempts were actually performed.

5. Use a property key whose name is discoverable by consumers. A constant such as `PropertyNames.CurrentAttempt` would be ideal so that users can import `NUnit.Framework.Internal.PropertyNames` and reference it safely.

### Non‑goal
- The property is **only** set for retried tests. Tests without `[Retry]` are unaffected; their properties collection does not need to include this property.

modification to RetryAttribute such that it exposes the current attempt in the Test properties as per issue #2647


**Title:** `[SetCulture]` / `[SetUICulture]` are unavailable in .NET Standard 1.4 builds

**Description:**
When targeting .NET Standard 1.4 (e.g., from a .NET Core test project), the `[SetCulture]` and `[SetUICulture]` attributes are not present in the NUnit framework assembly. This makes it impossible to set the culture for individual test methods or fixtures when running on .NET Core / .NET Standard test projects. The attributes work correctly on full .NET Framework, but the entire attribute classes are excluded from the `netstandard1.4` build.

**Expected behavior:**
The `[SetCulture("...")]` and `[SetUICulture("...")]` attributes should be available and functional on .NET Standard 1.4, so that tests can specify the culture under which they execute, regardless of the target platform.

**Additional notes on the implementation:**
- In .NET Standard 1.4, `Thread.CurrentThread.CurrentCulture` / `CurrentUICulture` are not available (or are deliberately avoided). The correct API to set the current culture on this platform is `CultureInfo.CurrentCulture` and `CultureInfo.CurrentUICulture`. The implementation should use that approach when running on .NET Standard 1.4.
- The `CultureInfo(string, bool)` constructor (with `useUserOverride`) may also be missing in .NET Standard 1.4; a fallback to the simpler `CultureInfo(string)` constructor is needed there.

This is a blocking issue for our .NET Core test suites that rely on culture-sensitive testing.

Enable SetCulture and SetUICulture attributes for .NET-Standard 1.4


We need to support a per-test-case fixture lifecycle in addition to the current single-instance-per-suite behavior. Currently, the framework creates one fixture object for all test methods in a fixture. We want fixtures decorated with `[LifeCycle(LifeCycle.InstancePerTestCase)]` (or that otherwise set `LifeCycle = InstancePerTestCase`) to get a new instance for each test method.

**Behavioral changes required:**
1. **Per-test construction**: When the fixture lifecycle is `InstancePerTestCase`, each test method should get a fresh fixture instance constructed immediately before the test runs. Constructor arguments (for parameterized fixtures) must be passed correctly.
2. **Per-test disposal**: If the fixture type implements `IDisposable`, that instance must be disposed immediately after the test completes. After disposal, clear the test object from the context so no reuse occurs.
3. **Suite-level construction/disposal suppression**: For `InstancePerTestCase` fixtures, the suite-level setup and teardown must *not* construct or dispose a fixture instance. The one-time setup and teardown methods themselves (if any) should still execute once per suite.
4. **Static fixtures must be unaffected**: Static fixture classes are never constructed; this logic should remain unchanged.
5. **Command flexibility**: The existing `ConstructFixtureCommand` currently assumes it operates on a `TestSuite`. When used per test case, its `Test` will be a leaf test (e.g., `TestMethod`). Adapt the command so it can locate the containing fixture and suite, regardless of the node it wraps.
6. **New property**: Add a `LifeCycle` property to `TestFixture` so the engine can inspect the lifecycle mode set by attributes.

**Invariants and edge cases:**
- The fixture instance must honor any constructor arguments from `TestSuite.Arguments`.
- Disposal should happen even if the test throws, and must not leave a dangling reference on the context.
- Fixture setup/teardown (if any) must still run in the correct order relative to the test, but the fixture object itself must be created after suite-level one-time setup and destroyed before suite-level one-time teardown when lifecycle is per-test-case.
- The change must not affect the default lifecycle (single instance per suite).


**Summary:**
Parameterized test fixtures generate display names that include argument values, but those values are formatted differently than when the same arguments appear in parameterized test method names. This leads to inconsistent and sometimes unreadable output in test runners.

**Steps to reproduce (user-visible):**
- Create a parameterized test fixture whose constructor takes a `string` containing special characters (e.g., newlines, backslashes).
- Also define a parameterized test method inside that fixture that receives the same string value.
- Observe the display names shown in the runner: the fixture name may show raw control characters without quotes or escaping, while the method name properly wraps the string in double quotes and escapes special characters.

**Expected behavior:**
The formatting of argument values in fixture display names (the part inside parentheses) should be identical to the formatting already used for test method arguments. In particular:
- Strings should be enclosed in double quotes and have special characters escaped (`\n`, `\r`, `\\`, etc.).
- Characters should be enclosed in single quotes and escaped.
- Numeric types should include appropriate suffixes (`5.0d` for double, `5.0f` for float, `5m` for decimal, `5L` for long, `5UL` for ulong).
- Null should be shown as `"null"`.
- Empty and small arrays should be displayed with `[...]` notation.
- The same string truncation rules should apply for overly long arguments.

The goal is to **harmonize** the two display-name code paths so that a single, consistent implementation determines how argument values are rendered, regardless of whether they appear in a fixture-level or method-level context. No public API changes are required—only internal consistency is needed.

**Constraints / Safety:**
- The existing formatting of method argument values must be preserved exactly as-is.
- Fixture argument formatting must be brought up to match that same level of detail and correctness, including all the type-specific logic already present for method arguments.
- No new public configuration or customization options are required.
- The fix should not alter the overall structure of the display name (i.e., still `ClassName(args)`)—only the formatting of each individual argument value should change.

Harmonize DisplayName for argument values on Fixture and Method


**Title:** Platform name `NET` should refer to .NET (Core), not .NET Framework

**Problem**
When a user writes `IncludePlatform = "NET"` (or uses `PlatformNames.NET`), NUnit currently treats that as targeting the .NET Framework runtime. In modern .NET terminology, “.NET” means the cross-platform runtime built on .NET Core (.NET 5+). The current mapping is misleading and forces users who want to target the new runtime to use `NETCore` instead.

**Required behavior change**

1. **Remap `NET`** – the platform name `NET` must resolve to the .NET Core runtime, not .NET Framework.
2. **Add `NETFramework`** – introduce a new, explicit platform name `NETFramework` for targeting the .NET Framework runtime.
3. **Keep `NETCore` working** – `NETCore` should continue to resolve to .NET Core for backwards compatibility.
4. **Update aliases** – the legacy aliases (`DotNET`, `DotNETCore` …) must follow the same semantics:
   - `DotNET` → .NET Core
   - `DotNETCore` → .NET Core
   - Add `DotNETFramework` → .NET Framework

**Scope**
- Update the `PlatformNames` constants so they reflect these names.
- Update the runtime resolution logic (where platform names are matched to `RuntimeType`) so that `NET`, `NETCORE`, and their aliases all map to the `NetCore` runtime, and `NETFRAMEWORK` / `DOTNETFRAMEWORK` map to `NetFramework`.
- Make sure the mapping is case-insensitive as it is today.
- Behavior when a version suffix is appended (e.g. `NET-2.0`, `NETFRAMEWORK-4.5`) must remain consistent: version specifications on a `NET` (Core) platform name should only be valid for framework versions that actually exist for that runtime, and attempting to specify a version on `NET` that does not apply to Core (like a framework-style version) should be rejected appropriately.

Update recognized platform names so that "NET" means .NET instead of .NET Framework (#5300)


**Bug: `[Retry]` attribute stops retrying when test fails via assertion failure (no exception thrown)**

When a test marked with `[Retry]` fails because of an `Assert` failure (e.g., `Assert.Fail()` or an assertion that evaluates to false) **without throwing an unhandled exception**, the retry loop exits after the first attempt instead of retrying up to the configured number of times. Tests that instead throw an exception that is recognized as a “retry” exception (e.g., `AssertionException`) are retried correctly.

**Steps to reproduce**
1. Write a test method with `[Retry(3)]` that uses `Assert.Fail()` (or any failing assertion that does not cause a top-level exception).
2. Run the test.

**Expected behavior**
The test executes up to 3 times (or the specified retry count), recording failures along the way, and reports the final result (likely Failed after all retries).

**Actual behavior**
The test executes only once, and the result immediately reflects a single failure.

**Impact**
This prevents users from using the `[Retry]` attribute for tests that fail due to ordinary assertion failures, which is the most common kind of test failure. It forces users to rely on throwing exceptions explicitly in order to benefit from automatic retries.

**Suspected cause**
After a test execution that **does not** throw an unhandled exception, the retry logic inspects the captured `caughtException` (which is `null`) and incorrectly concludes it is not a retry exception (`IsRetryException(null)` is `false`). This causes an early `break` even though the current result state is `Failure` and the recorded exception (if any) would otherwise indicate a retry should be performed. The decision to stop retrying when no exception was caught should be based on the result state and the recorded exception, not the null `caughtException`.

**Acceptance criteria**
- A test with `[Retry(n)]` that fails via assertion failure (no exception thrown) must be retried up to `n` times.
- If an exception is caught during execution, the existing behavior for non-retry exceptions should remain unchanged (i.e., stop retrying immediately for exceptions that are not recognized as retry exceptions).
- The fix must not break scenarios where the test succeeds on a later retry or where it fails with a retry-recognized exception.


**Title:** `Has.Member` and `AssertionHelper.Member`/`Contains` must return `SomeItemsConstraint` instead of `EqualConstraint`

**Background**
In previous releases `Has.Member(expected)` and the equivalent `AssertionHelper.Member(expected)` / `AssertionHelper.Contains(expected)` returned a `SomeItemsConstraint` that wrapped an `EqualConstraint`. A recent change inadvertently changed the return type to `EqualConstraint` directly.

**Problem**
1. **Collection semantics are lost.** The returned constraint now compares the *whole collection* against the expected object using `EqualConstraint`'s logic, rather than checking whether *any single item* matches.
2. **Fluent `Using` chain is broken (or wrongly scoped).** Users who write `Has.Member(expected).Using(comparer)` need the comparer to be applied to the item-equality check. Because the methods now return a bare `EqualConstraint`, either the call is not available on the returned type or it changes the wrong equality check (the top-level collection comparison).

**Desired behaviour**
- `Has.Member`, `AssertionHelper.Member`, and `AssertionHelper.Contains` must again return a `SomeItemsConstraint` wrapping an `EqualConstraint`.
- The returned `SomeItemsConstraint` must expose all the `Using` overloads that `EqualConstraint` normally offers (`IComparer`, `IComparer<T>`, `Comparison<T>`, `IEqualityComparer`, `IEqualityComparer<T>`, and the `Func<TCollectionType, TMemberType, bool>` overload). Each overload must forward the call to the **underlying `EqualConstraint`** so the comparer affects item matching.
- When the underlying constraint is **not** an `EqualConstraint`, calling any `Using` overload must throw an `ArgumentException` with a message that clearly explains the restriction (something like “Using can only be called when the underlying constraint is an instance of EqualConstraint”). This prevents misuse when the helper is used with a different underlying constraint in the future.
- The string description (`ToString()`) of the constraint produced by `Contains(42)` should be `"<some <equal 42>>"`, consistent with the wrapping.

**Acceptance criteria**
- `Has.Member(42).Using(myComparer)` compiles and the comparer is used while checking whether any item equals 42.
- `AssertionHelper.Contains(42).Using(myEqualityComparer)` works equivalently.
- `new SomeItemsConstraint(someNonEqualConstraint).Using(anyComparer)` throws `ArgumentException`.
- Existing tests that assert the type and `ToString()` of the result from `Contains(42)` must be updated to expect `SomeItemsConstraint` and `"<some <equal 42>>"`.


```
Optimize test-suite and test-method work item creation to skip completely excluded suites, and fix test-case counting to only count methods that actually match the active filter.

Background:
When a filter excludes a large portion of the test tree, the runner still constructs CompositeWorkItem for every excluded suite and SimpleWorkItem for every excluded test method. This wastes memory and time, especially for deep namespaces or fixtures where no child test matches. Additionally, the reported test-case count includes every leaf test regardless of the filter, which is misleading.

Desired behaviour:
- CompositeWorkItem (for a test suite) should only be created if at least one child test (or descendant) matches the filter.  The top-level assembly work item is an exception: it must always be created.
- SimpleWorkItem (for a test method) should only be created if the method passes the filter.
- The test-case count must reflect only tests that pass the filter, not the total number of test methods.
- Filter evaluation in hot paths (e.g. OrFilter.Pass, MatchDescendant) should avoid unnecessary allocations (e.g. LINQ, foreach enumerators) and leverage the full-name matching shortcuts when available.
- The optimisation must preserve existing behaviour for test ordering and apartment-state propagation.

Constraints / edge cases:
- If a suite has matching descendants, the composite work item for that suite must still be created and children added to it.
- Root (assembly-level) work item must never be skipped, because it owns the overall execution context.
- Tests with explicit ordering (Order attribute) must still be inserted at the front and sorted correctly.
- The changes must not alter the outcome of any existing test execution, only improve performance.
```

optimize Pass counting and work item building


We need a new test filter that shards tests into partitions, so that tests can be distributed across multiple machines. The filter should take a partition number and a partition count, expressed as a string like `"2/5"` (partition 2 of 5).

- A test’s partition is determined by hashing its full name (use a stable, high-quality hash such as SHA‑256) and mapping the hash to a 1‑based index.
- Only leaf tests (individual test methods) should ever be **matched** directly by the filter; test suites themselves must **not** be an exact match. However, suites should **pass** the filter if any of their descendant leaf tests match – following the same semantics as other `TestFilter` implementations.
- The filter must support the standard XML serialization/deserialization used by NUnit filters:
  - `AddToXml` should produce an element `<partition>number/count</partition>`.
  - `TestFilter.FromXml` must correctly parse such an element back into a partition filter with the right number and count.
- Invalid partition strings (e.g., non‑numeric parts, missing slash, number less than 1 or greater than count, zero count) should be handled gracefully by making filter creation fail (do not throw an exception from parsing).

Added a new filter, PartitionFilter, that allows you to filter to a subset of arbitrary tests based on a partition number and count


```
I'd like to request async versions of `Assert.That` and `Assert.Multiple` to allow seamless testing of async code with NUnit.

Currently, when I have an async method that returns a result, I have to do:

    var result = await SomeAsyncMethod();
    Assert.That(result, Is.EqualTo(42));

But inside a multiple assertion block (`Assert.Multiple`), I cannot use `await` inside the delegate because the block expects synchronous execution. I also cannot easily apply constraints like `Throws.Exception` to async operations without converting them to synchronous first (e.g., using `Task.GetAwaiter().GetResult()`, which loses the original stack trace and can cause deadlocks on some runtimes).

I would like new overloads that accept async delegates and integrate with the existing constraint pipeline:

1. An async `Assert.That` method (perhaps named `ThatAsync`) that accepts an async delegate returning `Task` or `Task<T>`, a constraint, and optionally a failure message and format arguments. It should:
   - Await the delegate.
   - If the task completes successfully, apply the constraint to the returned value (for `Task<T>`) or to the fact that no exception was thrown (for `Task`), depending on the constraint.
   - If the delegate throws, capture the exception with the original stack trace (e.g., via `ExceptionDispatchInfo`) and make it available for constraint evaluation, behaving like the existing `Assert.That(TestDelegate, Throws.…)` but for async code.
   - When invoked inside a multiple assert block, report failures without throwing immediately, matching the behavior of the synchronous `Assert.That` overloads.

2. An async `Assert.Multiple` method (perhaps named `MultipleAsync`) that accepts an async delegate, manages the multiple assertion nesting level, awaits the delegate, and—if there are pending failures at the outermost block—throws a `MultipleAssertException` that aggregates them, just like the synchronous `Assert.Multiple` does.

Edge cases to cover:
- The task is canceled; the resulting `TaskCanceledException` should be capturable and testable via `Throws` constraints.
- The task is faulted; the thrown exception should be captured intact (preserving stack trace) for constraint evaluation.
- The `Task<T>` overload should allow constraints like `Is.EqualTo` to work directly on the awaited value.

This would greatly simplify writing async tests, especially when multiple async assertions need to run together inside a single `Multiple` block without deadlock or loss of diagnostic information.
```


Bug: `Is.Ordered` throws `ArgumentNullException` when items contain null

**Observed behavior**
Calling `Assert.That(collection, Is.Ordered)` (or `Is.Ordered.Descending`) on an `IEnumerable` that contains `null` elements throws an `ArgumentNullException` ("Null value at index …").

**Expected behavior**
For simple ordering (no property specified), `null` should be treated as a valid value that compares as **less than every non‑null object** – consistent with .NET comparers (e.g. `Comparer<object>.Default`).
Thus:
- `[null, "a", "b"]` should satisfy `Is.Ordered`
- `["b", "a", null]` should satisfy `Is.Ordered.Descending`
- `["a", null, "b"]` should **not** satisfy `Is.Ordered`

When ordering **by a property** (e.g. `Is.Ordered.By("Name")`), the constraint should still throw when it encounters a `null` element, because accessing a property on `null` is invalid.

**Additional notes**
This is a behavioral change from the current “always throw on null”.  If a user needs the old “disallow nulls” policy, they can combine constraints:
`Assert.That(collection, Has.None.Null.And.Ordered)`.

Allow null values in CollectionOrderedConstraint


supervision_value: 3
difficulty: 3
reasoning: This is a solid ordinary SFT task. The core issue is clear from the title, but the fix is not a one-line edit: the agent needs to inspect `TestResult`, recognize that child storage only makes sense for suite results, compare `TestCaseResult` and `TestSuiteResult`, and update callers like `CompositeWorkItem` that were relying on the old base-class `AddResult` behavior. That teaches a useful mini-process of localizing the abstraction leak, pushing behavior down to the right subtype, and validating affected call sites. Difficulty is medium rather than easy because the change spans several related files and requires understanding the result hierarchy and state flow, but it is still bounded and inferable from repo context without deep domain knowledge.

---

**Implementation request** (plausibly written before the patch):

`TestResult` currently lazily creates a `List<ITestResult>` when `Children` is accessed, even for `TestCaseResult` which will never hold any children. This wastes allocations and makes it impossible to tell if a result genuinely has no children or just hasn’t been queried.

Remove the lazy initialization entirely. Make `Children` and `HasChildren` abstract on `TestResult`, and let each concrete result type provide the right implementation:

- `TestCaseResult` must report `HasChildren == false` and `Children` as an empty collection, without allocating a backing list.
- `TestSuiteResult` should own its child list from construction and expose it directly.

Move the `AddResult` logic (adding a child and updating summary counters/parent state) out of `TestResult` into `TestSuiteResult`, since only suite results have children. Update any code that currently calls `Result.AddResult(...)` to go through the suite result directly—for example, `CompositeWorkItem` should store its own `TestSuiteResult` reference and use that.

Ensure existing behavior is preserved: parent result states are updated when children are added (failure/ignored/inconclusive cascading), and summary counts remain correct.

Eliminate lazy initialization of TestResult.Children


**Subject:** Explore with a filter ignores the filter and lists all tests

When test exploration is performed with a filter (for example via `--explore --where`, an XML filter file, or the `FrameworkController.ExploreTests` API with a non‑empty filter string), the output always contains the entire test tree of the assembly. The filter appears to be ignored during discovery.

### Steps to reproduce (NUnitLite)
1. Build a test assembly that contains tests in at least two distinct categories.
2. Run: `testrunner.exe --explore --where "cat == Fast"`
3. Observe the XML output.

### Actual result
The output includes tests from *all* categories, not just those matching the `--where` predicate.

The same behaviour occurs when using `FrameworkController.ExploreTests(string filter)` with any non‑empty filter: the returned XML represents the whole assembly test tree.

### Expected result
The filter should be applied during test discovery so that the explore output only lists tests that satisfy the filter. The regular `Load` operation (no filter) should still return the full test tree; only the exploration step should be filtered. This would make the explored test set consistent with the set that would actually be executed when the same filter is passed to `RunTests`.

### Impact
Any tooling or CI pipeline that relies on `--explore` with a filter to inspect a subset of tests sees incorrect results, potentially leading to wrong reporting or skipping logic.

Updated implementation of the explore test filter to move filter to the runner rather than the output writers


**Unwrap TargetInvocationException consistently**

Several catch blocks across the framework and tests manually check for `TargetInvocationException` (and sometimes `NUnitException`) to extract `InnerException`. This logic is duplicated, inconsistent, and fragile: some sites only unwrap `TargetInvocationException`, others also unwrap `NUnitException`; some only do a single level of unwrapping, not handling cases where the inner exception is itself a `TargetInvocationException`.

Add a shared utility in `ExceptionHelper` that reliably unwraps both `TargetInvocationException` and `NUnitException`, iterating until neither wrapper remains, and replace every ad‑hoc unwrapping pattern in production code and tests with a call to this utility. The utility must:

- First unwrap an outer `NUnitException` if present.
- Then loop to strip any successive `TargetInvocationException` layers, as long as `InnerException` is non‑null.
- Return the original exception if no unwrapping applies.
- Be usable everywhere the library catches exceptions and wants the real cause, including in `DefaultSuiteBuilder`, `CompositeWorkItem`, `Reflect`, `TestResult`, and the test helpers in `AdhocTestExecutionTests` and `LowTrustFixture`.


Add `--mainthread` flag to NUnitLite runner

NUnitLite already supports several boolean-style flags (e.g., `--teamcity`, `--stoponerror`, `--noheader`). We need to add a `--mainthread` flag with similar semantics: when present, the runner should instruct the NUnit engine to run tests on the main thread (the same thread as the runner itself) rather than using worker threads.

The engine already respects a run setting for this behavior; the key is `RunOnMainThread` in `FrameworkPackageSettings`. The implementation should:

- Parse `--mainthread` (no value required) as a boolean option.
- When the option is specified, include the `RunOnMainThread` run setting set to `true` in the run settings dictionary that NUnitLite passes to the engine.
- Follow the same patterns used for existing boolean options (both parsing and run-settings mapping).
- Add corresponding unit tests that verify the option is recognized and correctly mapped to the run setting.

Add `mainthread` argument parsing to nunitlite


```
The OSPlatformTranslator currently translates each .NET 5+ SupportedOSPlatformAttribute or
UnsupportedOSPlatformAttribute into a separate NUnit PlatformAttribute. When multiple such
attributes are present on a test, fixture, or assembly, this leads to multiple PlatformAttribute
instances, which does not correctly combine platform requirements.

Modify the translator to aggregate all OS platform attributes on an element (including any existing
NUnit PlatformAttribute) into a single NUnit PlatformAttribute with merged Include and Exclude
strings. The aggregation should:

- Collect all include platforms and exclude platforms from all SupportedOSPlatform,
  UnsupportedOSPlatform, and NUnit PlatformAttribute attributes.
- Eliminate duplicate platform names (case-insensitive).
- Preserve any other attributes that are not platform-related, returning them unchanged.
- Produce exactly one PlatformAttribute when at least one OS platform requirement exists,
  or none otherwise.

The translator should remain internal (not public API for external callers) and the method that
performs this translation should accept all attributes from a test element and return the resulting
attribute collection.

This fixes scenarios where multiple [SupportedOSPlatform] decorations on a test are not properly
combined.
```

Need to combine separate .NET5.0 OSPlatformAttribute into single NUnit PlatformAttribute


**Title:** `RangeConstraint` should validate range ordering when `ApplyTo` is called after a custom comparer is set via `Using`

**Background**
The `RangeConstraint` constructors (both the `IComparable` overload and the `object`+`IComparer` overload) verify that `from <= to` using the comparer that is active at construction time. The `Using` method allows replacing that comparer with a different one, but the range ordering is never re‑checked after the comparer is changed. This means a user can construct a valid range (e.g., `from = 5, to = 42`), then call `Using` with a comparer that reverses ordering, making `from > to`. The constraint will then be applied with an invalid range, leading to confusing results or silently incorrect comparisons.

**Desired behavior**
When `ApplyTo` is executed, it must verify that the current range is still valid according to the comparer that is currently configured on the constraint (i.e., `comparer.Compare(from, to) <= 0`). If `from > to` under that comparer, `ApplyTo` should throw an `ArgumentException` with an appropriate message (the existing message “from must be less than to” is acceptable). This check should happen both on `ApplyTo` and keep the existing validation in the constructors, so that an invalid range is caught as early as possible but is also re‑checked when the comparer changes and the constraint is later used.

**Additional notes**
- The `Using` method permanently replaces the comparer on the constraint instance, so after reversing it the constraint remains in an invalid state for subsequent calls to `ApplyTo` until the comparer is reset to a forward‑ordering one.
- The XML doc comments on the `InRange` overloads in `ConstraintExpression` and `Is`, as well as on the `RangeConstraint` constructors, currently contain the erroneous text `"from must be less than or equal to true"`. While touching this area, please correct those remarks to something sensible, such as `"The <paramref name="from"/> value must be less than or equal to the <paramref name="to"/> value."`.


```
FixtureLifeCycleAttribute with LifeCycle.InstancePerTestCase is not honored when applied at assembly level or when a test class uses TestFixtureSourceAttribute.

The fixture lifecycle determines whether a new fixture instance is created for each test case versus once per fixture. Currently, setting [assembly: FixtureLifeCycle(LifeCycle.InstancePerTestCase)] is ignored, and placing [FixtureLifeCycle(LifeCycle.InstancePerTestCase)] on a class that uses TestFixtureSourceAttribute does not affect the generated fixtures.

Fix the attribute application so that:
- Assembly-level FixtureLifeCycleAttribute affects all test fixtures in the assembly.
- FixtureLifeCycleAttribute on a test class with TestFixtureSourceAttribute applies to each individual fixture generated by the source.
- Existing lifecycle behavior for ordinary [TestFixture] classes and other attributes is preserved.
```


**Ticket: `Is.Ordered.By()` should throw `ArgumentException` when a property is missing**

### Context
The NUnit `CollectionOrderedConstraint` (`Is.Ordered.By("PropertyName")`) compares elements by getting a named property via reflection on each item. When an element type lacks that property, the current code fails with an unclear exception (e.g., `NullReferenceException` or a generic `ArgumentException` that doesn’t pinpoint the problem).

### Desired behaviour
When `Is.Ordered.By("Prop")` is applied to a collection where **any element does not have a public instance property `Prop`**, the constraint must throw an `ArgumentException` whose message includes:

- the property name,
- the zero‑based index of the element that is missing the property.

This check must happen **before** any attempt to read the property value (to avoid downstream null‑reference errors). Both the “previous” element and the “current” element must be validated.

### Edge cases & invariants
- The new check must not change behaviour when the property *exists* but its value is `null` — that case already throws a separate `ArgumentException` with a “Null property value at index …” message, which should remain untouched.
- The new check must also not affect ordering‑comparison logic: if the property exists and the values are non‑null, the existing ordering semantics must be preserved.
- All existing tests that expect different exceptions (e.g., for null elements or unordered collections) must continue to pass without modification.

### Scope
Implement the improved property‑existence validation in the `CollectionOrderedConstraint.Matches` method. The fix should cover both the “previous” and “current” items in the foreach over the actual collection.

Throw argument exception if property not found in Is.Ordered.By()


```
Subject: Empty <reason> element in test result XML for skipped tests with no reason

When a test is skipped (e.g., due to an [Explicit] attribute without an explicit reason), the resulting
test result XML contains an empty <reason> element:

  <reason></reason>

Additionally, the _SKIPREASON (SkipReason) internal property is set to an empty string.

This makes the output unnecessarily verbose, produces technically invalid empty elements, and may
confuse downstream consumers of the XML that expect a reason element to contain non-empty text.

Expected behavior: When no reason/message was provided for a skip, there should be no <reason>
element in the XML at all, and the SkipReason property should not be set (or should be absent).
The element should only appear when a non-empty reason was specified, e.g. [Explicit("long running")].

This applies to all forms of skipped tests where the result message is null or empty — not just
ExplicitAttribute — though that is the most common scenario. The NUnit2 XML output has a similar issue
and should be fixed consistently.
```

Eliminate empty XML reason element and _SKIPREASON attribute when skipped test has no reason


```
Feature: Add a OneOf constraint to the NUnit fluent assertion syntax

We need a constraint that verifies whether an actual value is present in an expected collection. This would be used like other Is constraints, e.g.:
    Assert.That(value, Is.OneOf(collection))

Acceptance criteria:

1) Static factory methods on Is
   - Is.OneOf(IEnumerable expected) returns a constraint that passes when the actual value equals at least one element of expected, using NUnit's default equality comparison.
   - Is.OneOf(params object[] expected) is a convenience overload for passing values inline.

2) Modifier chaining (fluent API)
   The constraint returned by Is.OneOf must support these modifier methods, each returning the constraint itself for chaining:
   - .IgnoreCase – enables case-insensitive string comparison.
   - .Using(IComparer), .Using(IComparer<T>), .Using(Comparison<T>),
     .Using(IEqualityComparer), .Using(IEqualityComparer<T>),
     .Using(Func<T, T, bool>) – replaces the equality logic with the specified comparer/comparison.

3) Description for messages
   The constraint's Description property should format the expected collection as "one of <formatted collection>" so that failure messages are informative.

4) Public constraint class
   The constraint class should be public in the NUnit.Framework.Constraints namespace, allowing direct instantiation (e.g. new OneOfConstraint(collection).IgnoreCase) with the same modifiers as above.

5) Equality behavior
   Equality between the actual value and each collection element must use the standard NUnitEqualityComparer pipeline, honoring any tolerance, external comparers, and IgnoreCase settings applied through the modifiers.

6) Backward compatibility
   No existing constraint or helper (including Is, Constraint, NUnitEqualityComparer) may change its contract or public behavior.
```


```
During test fixture construction via [TestFixtureSource], a constructor signature that combines an
optional parameter and a params array is not handled correctly.

Steps to reproduce:
- Define a test fixture class with a constructor such as:
    public MyFixture(int required, int optional = 42, params int[] extra)
- Supply a [TestFixtureSource] that yields argument arrays of different lengths, for example:
    new object[] { 1 }
    new object[] { 1, 2 }
    new object[] { 1, 2, 3, 4, 5 }

Expected behaviour:
- The test framework should successfully construct the fixture for each argument set,
  using the default value for the optional parameter when it is not provided, and
  collecting any remaining arguments beyond the declared fixed parameters into the params
  array. All generated fixtures should be runnable and execute their tests.

Actual behaviour:
- The fixture either fails to be recognised as runnable, or construction throws an
  exception. The current argument matching/construction logic does not account for the
  interaction between optional parameters and a trailing params parameter.

Acceptance criteria:
- Fixtures whose constructor has an optional parameter immediately before a `params`
  parameter can be sourced from [TestFixtureSource].
- Argument sets of length equal to the number of required parameters, required plus
  optional, and required-plus-optional-plus-extra arguments all produce valid runnable
  fixtures.
- Existing behaviour for constructors with only optional parameters, only a params
  parameter, or neither must remain unchanged.
```

Ensure optional and params can be used in same signature of a testfixture


```
When a test method parameter is marked with [Values] and the parameter uses
the `in` or `ref` keyword (for example `in EnumValues value` or
`ref bool? testInput`), the attribute currently fails to automatically
generate values for that parameter. Instead it should generate the same
set of values that it would for the corresponding value-type parameter
(such as all enum members, true/false for bool, and plus null for
nullable values). The automatic generation should work for enum types,
bool, bool?, and Nullable<enum> when passed by-reference.
```

Add support for the ref and in keywords in ValuesAttribute


```
**Bug Report: Parallel execution shift signaling and resource cleanup**

During a review of the parallel execution infrastructure (WorkShift, ParallelWorkItemDispatcher, WorkItem), several issues were noted:

**1. Shift end notification is fragile**
The `EndOfShift` event on `WorkShift` currently uses `EventHandler`, which forces the dispatcher's `OnEndOfShift` method to cast `sender` back to `WorkShift`. It would be more robust and explicit to use a dedicated delegate that passes the `WorkShift` directly, avoiding the runtime cast and making the intent clearer. The shift that is ending should be provided as the argument without relying on `sender`.

**2. ManualResetEvent leak**
`WorkItem` creates a `ManualResetEvent` instance (used for test completion signalling) but never disposes it. In long-running test sessions with many work items this can cause a leak of native wait handles. `WorkItem` should implement `IDisposable` to properly clean up this resource.

**3. Insufficient test coverage of mixed parallel/non‑parallel execution**
The parallel execution tests (`ParallelExecutionTests`) currently construct a flat fixture suite without setting `ParallelScope` on any fixture. Consequently they do not exercise the real scenario where some fixtures inside a namespace hierarchy are parallelizable and others are not. The test data should be updated so that the suite structure resembles a typical nested namespace and applies appropriate parallel/non‑parallel attributes (analogous to what `[Parallelizable]` does at the attribute level). This may require adding simple test utility methods that set the parallel scope property on a `TestSuite` so that test data can be built declaratively.

**Expected behavior and invariants**
- The shift‑end notification must pass the ended `WorkShift` explicitly, not through a cast of `sender`.
- Every `WorkItem` must dispose its `ManualResetEvent` when the work item is no longer needed, without affecting the existing wait behaviour (the event must remain usable until disposal occurs).
- The parallel execution tests must cover at least one scenario with a namespace containing a mixture of parallel and non‑parallel test fixtures, nested inside a higher‑level suite (e.g., assembly → namespace → fixtures), to ensure shift transitions work correctly in mixed configurations.
```


When converting NUnit 3 test results into the legacy NUnit 2 XML format (using the NUnit2 result writer addin), two things are broken:

1. **Categories are missing from the output.** The `<categories>` element is always empty, even when tests have associated categories. I expected that for each category a `<category name="CategoryName"/>` child would appear inside `<categories>`, and that those categories would not also appear inside the `<properties>` element.

2. **The `name` attribute on `<test-case>` elements is wrong.** It currently uses the short method name instead of the fully‑qualified name (e.g. `Namespace.Class.Method`). The full name is what NUnit 2 consumers expect and is consistent with the NUnit 2 legacy format.

**Expected behavior:**

- The `<categories>` section (when present) contains a `<category>` element for each category assigned to the test, using the category’s name.
- The `<properties>` section contains only non-category properties. Category entries must not appear there, because they are already represented in `<categories>`.
- The `name` attribute on `<test-case>` elements is the full test name, matching what NUnit 2 would have produced.

This affects anyone who relies on the NUnit 2 result format for integration with older reporting tools and CI systems that still expect the classic output structure.

Fixes #1042 - Categories, properties and test-case names are now correctly written for NUnit 2 format


**Title:** `Does.ContainKey` does not respect custom key comparers on dictionaries

**Description**
When a `Dictionary<TKey, TValue>` is constructed with a custom `IEqualityComparer<TKey>` (e.g. `StringComparer.OrdinalIgnoreCase`), the `Does.ContainKey(...)` assertion and the equivalent `DictionaryContainsKeyConstraint` incorrectly fail for keys that *should* match according to the dictionary’s own comparer. The current implementation manually enumerates the dictionary’s keys and performs an item equality check, which ignores the lookup semantics of the dictionary.

**Acceptance Criteria**
1. `Does.ContainKey(expected)` succeeds when `expected` is a key that the dictionary considers present under its own comparer (case-insensitive, culture-specific, etc.).
2. The constraint should use the actual collection’s native key-lookup mechanism wherever possible: for a generic `IDictionary<TKey, TValue>` it should call `ContainsKey(TKey)`, and for other keyed containers that expose a `ContainsKey` or analogous method it should honour that method’s definition of key equality.
3. The behaviour must not break existing cases where the collection is a plain `IDictionary` without a custom comparer.
4. No breaking changes to the public API of the constraint. All existing overloads and usage patterns continue to work.

**Example**
The following assertion should pass:
```csharp
var d = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
    { "Hello", "World" },
    { "Hola", "Mundo" }
};

Assert.That(d, Does.ContainKey("hello"));
```

Updated DictionaryContainsKeyConstraint to look for ContainsKey and use that to look for a Key.


**Title:** `UsingPropertiesComparer` should name the failing property and show its expected/actual values

**Background**
When a test uses `Assert.That(actual, Is.EqualTo(expected).UsingPropertiesComparer())`, the framework compares objects property-by-property via `PropertiesComparer.Equal`. If a property fails equality, the `AssertionException` currently gives a generic difference message that does not identify *which* property is different – for objects with many properties this makes debugging unnecessarily hard.

**Desired behaviour**

1. **Single-level property failure**
   The error message must include the name of the first property that failed equality, together with the expected and actual values for that property. A natural phrasing would be:
   `Values differ at property ClassName.PropertyName`
   followed by the usual difference display (Expected: … / But was: …).

2. **Nested property chains**
   When a property value is itself an object that is compared via `UsingPropertiesComparer` (or through automatic structural comparison), the message should show the full path from the root object down to the leaf property that differs. For example:
   ```
   Values differ at property Parent.Child
     Values differ at property Child.Value
     Expected: 42
     But was:  99
   ```
   The recursion must correctly accumulate failure information so that each level adds its property name to the chain.

3. **Interaction with tolerance**
   Tolerance handling already has a code path that falls back to a no-tolerance comparison when tolerance is not supported for a particular property. The property-aware failure reporting must also work for those fallback comparisons – when the re-compared property fails, the same property‑name and value detail should appear.

4. **No impact on other comparisons**
   The new formatting must only activate when property‑based comparison is chosen (`ComparingProperties` is true). Stream, collection, and plain `Equals` diffs must continue to work as they do today, unchanged.

5. **Guard against missing failure points**
   `EqualConstraintResult` currently reads the `FailurePoints` list directly; make sure the formatting code is safe even when no failure points were recorded (the list may be null or empty).

**Non‑goal**
No new public API is required; this is purely an improvement to the error message text.

**Relevant internals (for reference)**
- `PropertiesComparer.Equal` performs the per‑property comparisons using `NUnitEqualityComparer.AreEqual`.
- `NUnitEqualityComparer` already has a `FailurePoint` type that collects positional information for streams and collections; it should be extended (or its usage adjusted) to carry property names and values.
- `EqualConstraintResult.DisplayDifferences` contains the branching logic for different difference types; it will need a new branch for property failures when `_comparingProperties` is true and a relevant failure point exists.

PropertiesComparer: Show actual property and value that fails equality (#4712)


As an NUnit maintainer ticket, it might look something like this:

---
**Title**: Restore implicit argument conversion for `params` arrays in `[TestCase]` and detect incompatible values early

**Background**
When a test method has a `params` parameter, the `[TestCase]` attribute can supply individual values that end up in that array. In NUnit 3.x the framework would implicitly convert those individual arguments to the array's element type, following standard C# implicit conversion rules (e.g. `int` → `long`, `int` → `double`). This conversion stopped working correctly, causing valid test cases to fail or behave unexpectedly.

**Desired behavior**
1. **Implicit conversions restored**: If an argument passed via `[TestCase]` for a `params T[]` parameter can be implicitly converted to `T`, NUnit should perform that conversion. For example, `[TestCase(1, 2)]` on a method with `params long[] a` should successfully create a `long[]` with values `1L, 2L`.
2. **Early validation for incompatible types**: If **any** argument *cannot* be implicitly converted to the element type—such as a string when `T` is a numeric type, or `null` when `T` is a non-nullable value type—the test must be marked **NotRunnable** at discovery/load time. The reason should include a clear message that mentions the incompatible type, so the user can immediately see which argument is invalid.
3. The conversion rules should follow standard C# implicit numeric widening, reference conversions, and nullable lifting where applicable. The only exception is that `null` is only acceptable if the element type is a reference type or a nullable value type; otherwise it is treated as incompatible and makes the test non-runnable.

**Scope**
This applies to any `[TestCase]` (including derived custom attributes built on top of it) that supplies values for a `params` parameter. The fix should make these test cases work correctly and provide readable diagnostics for invalid combinations, without affecting test cases where arguments match the element type exactly.

Readd code which implicitly converts individual args when constructing params arrays, but add a check for incompatible cases.


```
Bug: AppDomain setup is incorrect when using .nunit project files.

When a test run specifies a .nunit project, the application base, config file,
and private bin path are not resolved correctly.  This breaks tests that rely
on config files next to the assembly, or on custom base paths, or on private
bin paths for side-by-side assemblies.

Expected behavior:

1) Application base:
   - If a BasePath setting is provided, use it (with trailing separator).
   - Otherwise, for a single assembly package, use the directory containing
     the assembly.  For a multi-assembly project, use the common parent
     directory of all included assemblies.
   - For an anonymous package (no full name) containing sub-packages, use
     the common base of those sub-packages.

2) Configuration file:
   - If the ConfigurationFile setting is provided, combine it with the
     application base.
   - Otherwise, only use an automatic .config file when the package
     represents an executable assembly (.dll/.exe); in that case use
     <assemblyFullPath>.config.  .nunit project packages must not get an
     automatic per-assembly config.
   - For .nunit projects, if no config is set and a .config file exists
     next to the project file with the same base name, it should be used.
   - If none of the above, no config file should be specified.

3) Private bin path:
   - Honor an explicit PrivateBinPath setting.
   - When AutoBinPath is enabled (or no explicit bin path is set), compute
     the relative paths of the assembly locations from the application base.
     For a single assembly, this is the immediate parent directory name
     of the assembly relative to the base (or null if in the base).
     For multiple assemblies, include all distinct relative directory paths.

All of this must work for both direct assembly packages and project packages
that contain multiple sub-packages.
```

Fix Domain Setup to work with nunit projects. Fixes #1015


**Title:** Enhance test selection filter language: add `and`/`or`, drop comma‑lists, improve errors with position, support Unicode

**Description:**

The test selection parser (`TestSelectionParser.Parse`) currently only supports `&&` and `||` as logical operators. We’d like the filter language to also accept the English words `and` and `or` (case‑sensitive, same precedence as `&&`/`||`).

The comma‑separated list syntax (`cat = Urgent,High`) is confusing because it behaves like an OR but looks like a single value. Remove that syntax entirely; users must now write explicit `or`/`||` expressions (`cat = Urgent or cat = High`) to combine multiple values.

Additionally, the `category` keyword was an alias for `cat`. Remove that alias; only `cat` should be accepted.

Error messages from the parser currently give no indication of *where* a problem occurred. When an unexpected token is encountered, include the character position (e.g., `Unexpected token '...' at position N`) in the exception message to help users locate the mistake.

The tokenizer currently restricts word characters to a fixed ASCII set (letters, digits, `_`, `-`, `.`). This prevents users from writing test names or filter values that contain non‑ASCII characters. Relax the character‑acceptance logic so that any non‑whitespace character that is not a reserved operator symbol is treated as part of a word/identifier.

Empty or whitespace‑only input currently returns an empty XML string. Change the parser to treat this as an error: throw an appropriate exception (`NUnitEngineException` is fine) with a message indicating that no input was provided. The tokenizer should not itself throw on empty input; the parser will validate it.

**Acceptance criteria / invariants**

- `and` and `or` behave identically to `&&` and `||` respectively, including precedence and left‑associativity.
- Existing expressions using only `&&`, `||`, `=`, `==`, `!=`, `=~`, `!~`, parentheses, and negation (`!`) must continue to work unchanged (backward compatibility).
- The `!` negation operator still requires parentheses around the negated expression.
- Commas are no longer recognised as list separators; a filter like `cat = Urgent,High` should now be a syntax error (or treat the comma as part of the value, but better to reject as unexpected token).
- The `category` keyword is no longer accepted; use `cat`.
- Unicode characters in words (e.g., Cyrillic names) are correctly tokenised and can be used in identifiers and values.
- `null` input still throws `ArgumentNullException` (existing contract).
- Empty string, all‑whitespace strings, or input that only produces an EOF token cause the parser to throw an `NUnitEngineException` with a clear message.
- Error messages for unexpected tokens contain the character position (0‑based or 1‑based – pick consistent approach).

Refactor parser; simplify syntax; add or and and operators; add position to error messages


```
Title: Upstream ITestAction not executed and action ordering is broken when multiple actions are present

When an ITestAction is applied to a fixture (suite-level) and other ITestAction attributes are applied directly to test methods, the lifecycle methods (BeforeTest/AfterTest) are not invoked in the expected nested order. In some cases the suite-level actions are skipped entirely.

Expected behavior:
- Each ITestAction should decorate the test command individually, forming a chain of wrappers.
- Suite-level (upstream) actions should be the outermost wrappers, and method-level actions should be innermost (closest to the test body).
- Consequently, BeforeTest should execute from outermost wrapper to innermost, then the test (including its own SetUp/TearDown), and AfterTest should execute in reverse (innermost to outermost).

Current behavior:
- All actions are collected into a single list inside the decoration mechanism and are executed sequentially, with no nesting of individual action wrappers.
- The command is built before the execution context’s upstream actions are available, so suite-level actions are either not applied or their timing relative to other wrappers is unpredictable.
- This violates the expected lifecycle nesting and can cause test failures when actions depend on state managed by outer actions or by SetUp/TearDown.

The fix should ensure that:
1. The decoration command handles exactly one ITestAction at a time, wrapping the test command that executes the method (including any SetUp/TearDown and other user-supplied wrappers).
2. Actions from attributes on the test method are wrapped first (inner), then upstream actions from the suite context are wrapped in the correct order (outer).
3. The command chain is built at execution time, when the execution context is available to provide the upstream action list. This replaces the current design that builds the command eagerly before such context is populated.
4. The skipping logic for non-runnable tests should continue to work, without relying on an unnecessary factory method for skip commands.
```

Redefine TestActionCommand to handle a single action


**Title:** Add `Is.MultipleOf`, `Is.Even`, and `Is.Odd` constraints

**Background**
NUnit currently has no built-in fluent constraint for checking whether a number is a multiple of a given integer, or for the common cases of even/odd. Users often resort to custom helpers or manual modulus checks in assertions.

**Request**
Add the following user-facing constraints, available both via the `Is` helper and the constraint expression builder:

- `Is.MultipleOf(int n)`: passes when the actual value is an integer type (`byte`, `sbyte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`) and is evenly divisible by `n`. For any non-integer type (including floating-point types like `float`, `double`, `decimal`, as well as `null`, `string`, `bool`) the constraint must produce a failure — it should not throw an exception.
- `Is.Even`: shorthand for `Is.MultipleOf(2)`.
- `Is.Odd`: shorthand for `Is.Not.Even`. `Is.Not.Odd` must therefore resolve to `Is.Even`.
- All three must be usable in chained expressions, e.g. `Is.Not.MultipleOf(3).And.Even`.

**Behavioral requirements**
- `n` must be **positive** (`> 0`). Passing zero or a negative number should throw an appropriate guard exception (use `Guard.ArgumentInRange` or similar).
- The divisibility check must be an exact integer modulus operation. Correct handling of both signed and unsigned integral types is required, without overflow or lossy conversions.
- `0` is considered **even** and **not odd**.
- For non-integer types the constraint should return a constraint failure (not throw). This avoids precision surprises with floating-point numbers.
- The constraint’s description should clearly indicate the expected multiple, e.g. `"MultipleOf(3)"`.

**Integration points**
- Follow the existing pattern of constraints like `Empty`, `True`, `False`: introduce a new constraint class (likely named after `MultipleOf`) that inherits from `Constraint`, and expose factory members on the static `Is` class as well as extension-style members on `ConstraintExpression`.

**Goal**
After this change, tests like the following should work correctly (pseudocode):
```csharp
Assert.That(12, Is.MultipleOf(3));           // pass
Assert.That(12, Is.Even);                     // pass
Assert.That(9, Is.Odd);                       // pass
Assert.That(0, Is.Even);                      // pass
Assert.That(0, Is.Not.Odd);                   // pass
Assert.That(4.0f, Is.Even);                   // fail (not an integral type)
Assert.That("hello", Is.Even);                // fail
Assert.That(7, Is.MultipleOf(3));             // fail
Assert.That(6, Is.Not.MultipleOf(3).And.Odd); // fail (6 is not odd)
```

Add Support for MultipleOf, Even and Odd. (#4946)


**Title:** Avoid retrieving all custom attributes when applying tests to prevent unexpected exceptions

**Area:** Test attribute application (internal `Test.ApplyAttributesToTest` / `OSPlatformTranslator`)

**Problem**
When NUnit applies custom attributes to a test, the current logic calls `ICustomAttributeProvider.GetCustomAttributes` to retrieve every attribute on the type or method before filtering to the ones NUnit understands (attributes implementing `IApplyToTest` and .NET’s `OSPlatformAttribute`). This full retrieval can throw if any attribute’s constructor or property accessor has side effects or throws, even if NUnit would never use that attribute. The result is that NUnit completely fails to discover or run tests in those assemblies—an unnecessary and fragile dependency on arbitrary code in attributes.

**Desired behavior**
Only attributes that NUnit actually needs should be fetched from the provider:
- All attributes that implement `NUnit.Framework.Interfaces.IApplyToTest` (NUnit’s own test-behavior attributes).
- .NET runtime’s `System.Runtime.Versioning.OSPlatformAttribute` (if present on the target framework), which is translated into NUnit platform constraints.

No other attributes should be retrieved. This eliminates exceptions caused by third-party or framework-defined attributes that are irrelevant to test execution.

**Constraints / edge cases**
- The existing translation merging of OS platform attributes with NUnit `PlatformAttribute`s must be preserved: multiple platform and OS-platform attributes should still be combined into a single `PlatformAttribute` with unified include/exclude lists. Other `IApplyToTest` attributes must be passed through unchanged.
- The retrieval must be safe on older .NET frameworks where `OSPlatformAttribute` does not exist—the absence of that type should not cause any exception.
- The behaviour otherwise should remain identical: the set of applied attributes that reach test execution must match the current logic, just obtained without fetching all attributes.

Convert OSPlatformTranslator so it doesn't retrieve all attributes.


**Bug: ResultSummary.GetTestResults() produces incorrect summary XML**

`ResultSummary.GetTestResults()` builds a `<test-run>` element that aggregates results from an NUnit test run. Several attributes disagree with the true run totals observed elsewhere, and one attribute violates the engine’s casing convention.

Observed issues:

1. **Capitalisation of `result`** – The element has an attribute `Result` (uppercase ‘R’). All other engine‑emitted XML uses lowercase attribute names; this should be `result`.

2. **Incomplete skip count** – The `skipped` attribute reflects only the `SkipCount` counter. It should reflect the *total* skip count (`TotalSkipCount`) so that explicitly skipped tests are included, matching what other engine outputs report as skipped.

3. **Assert count comes from the wrong level** – The `asserts` attribute currently accumulates assertions from every `<test-case>` element. The engine’s convention is that the assert count is a property of the assembly‑level suite, not a sum of test‑case asserts. The attribute should therefore be set from the assembly suite’s `asserts` attribute only, avoiding potential double‑counting or inconsistencies.

4. **Counters are reset when traversing nested suites** – When the summary walks the XML tree, it re‑initialises its internal counters each time it enters a `test-suite` element. This causes it to “forget” counts accumulated from sibling or parent suites, so the final totals are too low. The counters must persist across the entire traversal without being reset at each suite boundary.

The fix should make the `<test-run>` element emitted by `ResultSummary.GetTestResults()` agree with the real run totals (as seen, for example, in the top‑level `<test-run>` of an NUnit result file) by correcting the attribute casing, skip source, assert source, and counter lifetime.


When `http.send` is called with `"force_cache": true` but without `"force_cache_duration_seconds"`, the validation error is only raised when OPA is running in server mode; in the REPL the call silently succeeds, falling back to intra-query caching without error. Both modes must reject the invalid call with a clear error before any caching path is chosen, making behavior consistent.

topdown/http: validate force_cache parameters


Feature request: `opa eval --partial` needs a `source` output format

When I run partial evaluation with `opa eval --partial --format pretty`, I get human-readable output that shows the partial queries and support modules, but it isn't valid Rego source. The `--format json` option gives JSON that's easy for programs to consume, but converting it into a `.rego` file I can reuse later is tedious. I want to copy-paste the partial evaluation results directly into a file and have it be valid, nicely formatted Rego.

Proposed solution: add a new `source` value for the `--format` flag. When `--partial` is set and `--format source` is used, the output should be valid Rego source code, with a comment prefix like `# Query 1` before each partial query body, and `# Module 1` before each support module body. The bodies and modules should be printed using the same Rego formatter already used for `opa fmt`. This would let me redirect the output straight to a `partial.rego` file and use it without further transformation.

Edge cases and constraints:
- `--format source` should **only** be allowed with `--partial`. If a user tries to run `opa eval --format source` without `--partial`, the command must exit with an error message clearly stating the format is invalid for that command.
- When there are errors (e.g., evaluation errors from the query), the `source` output should print only the error message (similar to the existing `prettyError` behavior) and not attempt to print any Rego source.
- The output must be deterministic across runs, using the same AST formatting routines that `opa fmt` uses.

This would make partial evaluation much easier to use in CI pipelines and local experimentation, and would fill a gap that pretty/json formats don’t cover.

cmd: Add source output format for partial eval


**Bug: Debug response body logging shows "[Payload Compressed]" for non‑compressed endpoints**

OPA’s debug‑level logging includes a `resp_body` field that shows the full HTTP response body sent to the client. However, as soon as a client sends the `Accept-Encoding` header with a value that includes `gzip` (which almost every client does – browsers, curl, Prometheus, etc.), the logger replaces the response body with the placeholder `[Payload Compressed]` for **all** endpoints, even endpoints that never compress their responses at all.

The vast majority of OPA’s REST endpoints (`/v1/data`, `/v1/policies`, `/v1/health`, …) serve uncompressed JSON or plain text; they do not perform gzip compression. Only two categories of endpoints actually produce compressed (or binary) responses:

1. `/metrics` – the Prometheus client library compresses the response when the client indicates it accepts `gzip`.
2. `/debug/pprof/` – these endpoints always return binary protocol‑buffer data regardless of headers.

Because the current logic suppresses the body based solely on whether the client *indicates* gzip support, debug logs become practically useless for inspecting API responses in normal operation – almost every logged response shows `[Payload Compressed]` instead of the real payload.

**Expected behaviour**
- For endpoints that do **not** compress their responses (the normal API endpoints), the real response body should always be logged in `resp_body`, even if the client sent `Accept-Encoding: gzip`.
- For `/metrics`, when the client accepts `gzip` the logger may use a distinct placeholder (e.g., `[compressed payload]`) because the response is genuinely compressed.
- For `/debug/pprof/` endpoints, the logger should indicate that the response is binary (`[binary payload]`) rather than implying compression, since those endpoints do not compress but always return protobuf.

**Impact**
This makes debugging with debug logging extremely difficult anywhere outside of a hand‑crafted test environment. Any client that sends a modern `Accept-Encoding` header will cause response body contents to be hidden from the log.

*Note*: The codebase also contains an unused `Hijack` method on the response recorder and a `dropInputParam` helper that are remnants of a removed watch feature; those can be cleaned up separately but are not part of the logging behaviour change.

runtime/logging: only suppress payloads for handlers that compress responses (#4502)


**Title**: Dynamic refs and comprehensions inside compound terms are not eagerly evaluated, causing variable capture in queries

**Description**
When OPA evaluates a query containing a ref or comprehension that appears inside a function call, a composite value, or a standalone term (i.e., not the entire side of an equality expression), the evaluation can produce incorrect results if that ref or comprehension refers to variables that are bound later in the same query. The expected behaviour is that such dynamic terms are always evaluated *before* any later-bound variable could affect them. Currently there is no compiler pass that ensures this eager evaluation.

**Observed problem**
Consider a rule body such as `count(arr.nums, n); n = 3`, or a term like `data.items[input.idx]`. If the variable used in the ref index is not yet ground when the ref is resolved, the ref may behave as if the variable is unbound, or the later binding may incorrectly influence the ref lookup. Even outside of explicit variable binding, refs that are arguments to built‑in calls or that appear inside arrays/objects/sets should be captured as values before the surrounding expression is evaluated, to match the semantics of the language.

**Expected behaviour**
The compiler should rewrite any query body (both rule bodies during module compilation and ad‑hoc query bodies) so that:

1. **For non‑equality expressions** (e.g., function calls, term‑only expressions): any dynamic term (a ref or a comprehension) that appears in the expression is extracted and bound to a freshly generated local variable (`__local0__`, etc.). A new equality expression is inserted before the original expression, and the original term is replaced with the new local variable.

2. **For equality expressions** (`A = B`): the two sides are not extracted as a whole, but each side is traversed; any dynamic sub‑term nested inside `A` or `B` (e.g. inside an array, object, set, or comprehension) is still extracted and bound, while the equality itself remains.

3. **Composite values** (arrays, objects, sets) are traversed recursively; each dynamic term found in an element is bound to a local variable and replaced.

4. **Comprehensions** (array, set, object): the compiler should first rewrite dynamic terms in the comprehension body (applying this same pass recursively), then bind the entire comprehension result to a local variable, and replace the comprehension with that variable.

5. The generated local variables must not collide with any existing variable in scope; the compiler should track generated variables per module and per query, reusing the same generation mechanism that already exists for comprehension‑term rewriting.

6. This rewrite must be performed **after** reference resolution and after comprehension‑term rewriting, but **before** safety checks (so that the safety checker sees the correct variable dependencies).

**Impact**
Without this rewrite any policy that relies on refs or comprehensions being evaluated before variables are bound later in the query can produce wrong results. This affects all rule‑body evaluation and ad‑hoc query compilation.


I’ll provide the implementation request as a bug report from a developer who wants to improve OPA’s topdown evaluation performance by optimizing the bindings store. The request describes the symptom (unnecessary hash-map overhead for the typical case of a few active bindings), the desired behavior (a hybrid data-structure that uses a small array for low counts and falls back to a native map), and the critical correctness constraints.

---

**Subject: `topdown` bindings: reduce overhead of generic hash map for small binding sets**

### Problem
The `topdown` evaluation engine’s `bindings` struct currently holds variable bindings in a `*util.HashMap`.  In many evaluation scenarios the number of active bindings is very small (often fewer than 10–20).  The generic `util.HashMap` adds overhead from:

* interface-value boxing/unboxing in `Get`/`Put`
* custom equality and hash functions that ultimately perform full `ast.Term.Equal`, even though all keys are variable terms (`*ast.Term` wrapping an `ast.Var`).

This overhead is measurable in benchmarks such as `BenchmarkAuthzAllow100Paths`, `BenchmarkPartialEvalCompile/1000`, and others where a large portion of time is spent on simple binding operations.

### Desired behaviour
Replace the `*util.HashMap` inside **`topdown/bindings.go`** with a purpose‑built data structure that is optimised for the common case of few bindings:

* When the number of bindings is ≤ 16, use a **fixed‑size array** with a linear scan for `Get`/`Put`/`Delete`/`Iter`.
* When more than 16 bindings are inserted, **transition to a native Go `map`** keyed by `ast.Var` (the variable name extracted from the `*ast.Term` key).  The map value is the same `(value)` struct already used internally.
* The transition must be **seamless**: inserting the 17th binding should construct the map from the existing array contents and then continue operating in map mode.  Subsequent lookups, updates, and deletions should all work correctly regardless of which mode is active.
* Delete must handle removal from the array correctly (e.g., swap‑with‑last to keep the array packed) and from the map in the usual way.
* Iteration order is not required to be stable, but every key/value pairing that has been inserted (and not deleted) must be visited exactly once.
* The exported API of the `bindings` type must not change; the change is purely an internal optimization.
* All existing tests must pass without modification.

### Additional constraints
* The new implementation may **remove the dependency on `util`** from `bindings.go` if that package is no longer needed.
* Provide focused **unit tests** for the new store that exercise both the array‑only path and the array‑to‑map transition, covering insertion, update, deletion, iteration, and the edge of exactly 16 entries.

### Impact
This improvement is expected to yield substantial speedups in `topdown` evaluation, as shown by the benchmark comparison included in the commit message, without affecting correctness, because the semantics of variable binding operations remain identical.

topdown: Improved bindings implementation.


**Title:** `oracle`: `FindDefinition` does not resolve object refs (e.g., `obj.key`)

**Background**
The `FindDefinition` method in `v1/ast/oracle` provides go-to-definition for symbols in Rego source. It currently handles simple rule names like `q`, imported package references, and bare variables. It does **not** handle composite references that start with a variable and refer to an object or a partial object rule — for example `obj.key` in a rule body — and returns `ErrNoMatchFound`.

**Desired behavior**
When the cursor is on a ref such as `obj.key`, `FindDefinition` should:
1. Look for a rule whose head matches the full ref; if found, navigate to that rule (e.g., `obj.key := "value"` → head of that partial rule).
2. If no exact rule matches the full ref, navigate to the rule that defines the base object (`obj := {"key": "value"}`) — i.e., the longest matching rule prefix.
3. If no rule matches any part of the ref, fall back to the existing variable-definition behavior: locate the first occurrence of the variable in function arguments or the rule body (as it already does for bare variables like `bar`).

**Edge cases / safety constraints**
- If the ref’s key does not exist in the object but the object is defined (e.g., `obj.foobar` where only `obj := {"key": "value"}` exists), still navigate to the object’s definition.
- If the ref’s base variable refers to an undefined name (no rule, no import), the method must still fall back to the variable-find logic — it should not return a “no match” error.
- The import resolution path (e.g., `data.foo.bar`) must continue to work exactly as before; this change is additive.

oracle: Support object refs in FindDefinition (#7711)


**Title:** Debug tracing mode: disable indexing, correct JSON trace output, and include variable metadata and location

**Background**
When running `opa eval --explain full`, the trace output helps users understand rule evaluation, including which variable bindings are created. However, two problems exist:

1. **Indexing hides variable bindings from some rules.**
   Because of indexing optimizations, certain rules that don’t contribute to the final result are never evaluated, and their variable bindings are completely absent from the trace. This makes the debug trace misleading or incomplete when a user wants to see all variable assignments—even for rules that don’t apply.

2. **The JSON trace event format is incomplete and broken.**
   - The `Locals` field is currently a `ValueMap`, which marshals to JSON in an unhelpful way (typically as an empty object or missing type information). It should instead produce an array of objects containing `name`, `type`, and `value`.
   - There is no `Location` information attached to trace events, so the user can’t easily map an event back to its source file and line.
   - There is no metadata that maps internal (rewritten) variable names back to the user‑readable variable names declared in the policy, making local bindings hard to interpret.

3. **Eval options do not propagate to partial evaluation.**
   When `--partial` is used, the eval options (such as tracers or the new indexing flag) are not passed to the partial evaluation query, so tracing and the disable‑indexing feature don’t work in combination with `--partial`.

**Desired behavior**

- Add a new `--disable-indexing` flag to `opa eval`. When set, rule indexing is turned off so that **all** rules are evaluated even if they don’t affect the final result, and every intermediate variable assignment appears in the trace. This must work with both normal and partial evaluation (`--partial`).

- When `--explain full` is used with JSON output (`-f json`), each trace event must include:
  - `Location`: the source location (file, row, col) of the AST node that the event relates to.
  - `LocalMetadata`: a map from internal variable identifier to a `VarMetadata` object containing:
    - `name`: the user‑written variable name (after de‑rewriting).
    - `location`: the source location of the variable’s declaration or occurrence.
  - `Locals`: a JSON array of objects, each with `name`, `type`, and `value`, correctly serialized from the `ValueMap`.

- When `--partial` is specified, the `Partial` method on the prepared query must be called with all relevant eval options (tracers, disable‑indexing, etc.), not just the context. This ensures the new options and trace output work for partial evaluation as well.

**Acceptance Criteria**
- `opa eval --explain full -f json --disable-indexing` produces trace events with `Location`, `Locals` (as array of objects with type info), and `LocalMetadata`.
- The same output is produced when `--partial` is also used.
- Without `--disable-indexing`, the `Locals` serialization is still correct (array of typed objects) and `Location`/`LocalMetadata` are present.
- Existing tests that consume JSON trace output (e.g., in the tester) are updated to expect the new fields.


**Title:** Improve performance of bundle root overlap detection

**Summary:**
The `hasRootsOverlap` check in the bundle store activation path currently compares every root against every other root, leading to O(N²) behavior that becomes a bottleneck when many bundles (hundreds or thousands) are active. We need to improve the algorithm so that activation time scales significantly better with the number of active bundles, while preserving the exact same set of detected conflicts.

**Conflict–detection invariants (must be preserved):**
- Two different bundles declaring the exact same root must still be flagged.
- Ancestor/descendant root overlaps (e.g., `a` and `a/b`) across different bundles must still be detected.
- A single bundle with overlapping roots internally is allowed and must not produce an error.
- When a bundle is replaced (re-activated), the new roots must be used; the stale roots from the store must be ignored to avoid false conflicts against the bundle’s own new roots.
- Bundles with an empty root (`""`) must still be handled correctly, as they trigger a special error case.
- Error messages for conflicts should remain human-readable and consistent with the current format (e.g., `"root … is in multiple bundles"`, `"… overlaps …"`).

No external APIs change; only the internal overlap–checking logic in `hasRootsOverlap` needs to be optimised.

bundle: Update bundle roots conflict detection algorithm. (#8664)


We need to improve the performance of several core operations in the `ast` package without changing their externally visible equality semantics. The specific areas to address are:

1. **Number comparison in `Compare`**
   When both operands are integers (as represented by `json.Number`), the comparison should avoid allocations by using `json.Number.Int64()` directly. For non-integer numbers the existing `big.Float` path must remain, and the overall behavior must stay identical: two `Number` values are equal when they represent the same numeric value (e.g., `"1"` and `"1.0"` must still be considered equal).

2. **Set and object key lookups**
   The internal hash-table lookups inside `set` and `object` (used by `Set.Contains`, `Set.Add`, `Object.Get`, `Object.Insert`, etc.) currently rely on the generic `Compare` function to determine key equality. Replace those calls with type-specific equality checks for simple scalar types (`Null`, `Boolean`, `String`, `Var`, `Number`) while falling back to `Compare` for everything else. This should noticeably speed up membership tests, intersections, and lookups, especially for integer-keyed data.
   – For `Number` keys, use the same integer short-circuit as in `Compare` when possible, and otherwise compare via `big.Float`.
   – The semantics of set membership, insertion, and object key lookup must be unchanged – every operation must see the same results as with the current code.

3. **`Term.Equal` completeness**
   `Term.Equal` currently does not handle `Number` terms explicitly. Add a case that delegates to `Number.Equal` so that two `Term`s wrapping equal `Number` values compare equal. This is both a correctness fix and a prerequisite for the other optimizations to be tested properly.

**Acceptance criteria**
- All existing tests in `ast` pass.
- `Term.Equal` returns `true` for two `Number` terms that represent the same numeric value.
- Benchmark results for set membership, set intersection, and object lookups improve by at least 25%, with substantially larger gains for integer-heavy workloads (ideally >50% reduction in ns/op).
- No behavior change in `Set.Contains`, `Set.Add`, `Object.Get`, `Object.Insert`, or `Compare` for any combination of values.

ast: Optimize set/object lookups and Number comparison.


Currently, the string representation of sets and objects in the `ast` package is non-deterministic: the output order depends on insertion order rather than on any canonical ordering of the elements. This makes string output unsuitable for stable comparisons (e.g., in tests or logging).

Make the `String()` output for AST sets and objects deterministic by sorting elements according to the existing term comparison order (the same order used by `termSlice` sorting). The sorting must be applied consistently so that equal values always produce identical string representations, regardless of how they were constructed.

No change to the underlying data structures is required—only the way strings are built from sets and objects.

ast: Make Set and Object string representation deterministic


When using `opa fmt` with the `--fail` flag together with `--list` or `--diff`, the tool currently discards the listing or diff output and only prints the error message. That makes it impossible to see *which* files need reformatting while still exiting with a non-zero code for automation.

**Observed behavior:**
`opa fmt ... --list --fail` or `opa fmt ... --diff --fail` emits only `"unexpected diff"` and exits with 1 when files are misformatted. The file names (with `--list`) or the actual diff (with `--diff`) are absent.

**Expected behavior:**
When `--fail` is combined with `--list`, the command should first print the paths of files that need reformatting, then return the `"unexpected diff"` error. When combined with `--diff`, it should print the diff output, then the error. The standalone `--fail` flag should continue to behave as it does today (only the error, no additional output). In all cases, unchanged files should not produce any output.

**Impact:**
This prevents scripting patterns such as:

```sh
opa fmt ./... --list --fail
```

which should list every file that needs changes *and* fail when at least one file is misformatted, in a single invocation.

format: Output list and diff changes with --fail flag (#4508) (#4710)


```
Title: Support per-query custom built-ins with access to the caller's context

Currently, built-in functions can only be registered globally (via RegisterFunctionalBuiltin1/…/4 or RegisterBuiltinFunc). This is problematic when the built-in's behaviour depends on per-request state – for example, a Go context for cancellation or tracing, per-query authentication tokens, or any other data that is naturally scoped to a single evaluation.

We need a way for callers to supply custom built-in implementations that are scoped to a single query evaluation. The framework already defines a `BuiltinFunc` signature that supports iterator-based emission of multiple values, but there is no API on the `Query` object to inject such custom built-ins.

The request:

1. Add a mechanism on the `Query` type that lets callers provide a mapping of built-in names to `BuiltinFunc` implementations. These per-query built-ins should take effect for the lifetime of that query only and should not require global registration.

2. Update the evaluator to resolve custom built-ins during evaluation. The resolution order relative to globally registered built-ins should be well-defined (e.g., global built-ins take precedence, and per-query built-ins are consulted as a fallback, or the other way around – pick the safest option for compatibility).

3. Expose the caller's `context.Context` (the one passed to `Query.Run`) to built-in functions via the `BuiltinContext` struct. This allows custom built-ins to respect cancellation, deadlines, and carry per-request values, which is currently impossible because `BuiltinContext` only carries runtime info, cache, location, tracers, and query IDs.

4. Preserve backward compatibility: global built-in registration and the existing `BuiltinFunc`/functional APIs must continue to work as before.

This feature will make OPA much easier to embed in systems that need dynamic, request-scoped builtin decisions without resorting to global mutable state.
```

topdown: Update evaluator to accept custom built-ins


```
Add Base and Virtual functions to the dependencies package, and use them in the watch package.

The watch package currently has an internal method `extractBaseRefs` that computes the base document dependencies of a compiled query for change notification. This logic belongs in the dependencies package for reuse.

Add two public functions:

- `Base(compiler *ast.Compiler, x interface{}) ([]ast.Ref, error)` – returns the base data documents that the given AST element depends on. Refs are truncated to their constant prefix. When a ref targets a virtual document (a rule), the function must recurse into the ruleʼs body and return the base documents that rule depends on, not the rule itself.

- `Virtual(compiler *ast.Compiler, x interface{}) ([]ast.Ref, error)` – returns the virtual documents that the given AST element depends on. It should include the rule refs themselves and, for each rule, recurse into its body to include transitively depended virtual documents.

Both functions must return deduplicated, sorted results. Factor out the deduplication logic already present in `All` into a shared helper to avoid duplication.

Update the watch package to call `dependencies.Base` instead of its internal method, and remove the now-unused `extractBaseRefs`.
```

Refactor dependencies package to support Base and Virtual deps


When OPA undergoes graceful shutdown (e.g., `SIGTERM`), the decision log plugin currently does not flush buffered decision events. They are simply discarded, even though a remote service is configured. This means auditable decision logs are lost on shutdown.

The desired behavior: during graceful shutdown, the decision log plugin must attempt to flush all buffered events to the configured remote service. It should retry uploads until the buffer is empty or the graceful shutdown deadline expires, whichever comes first. After the deadline, shutdown must proceed even if events remain, with a logged warning. If no remote service is configured, no flushing is needed.

The runtime parameter `GracefulShutdownPeriod` (already accepted by the runtime) determines the shutdown deadline. This period must be propagated to the plugin manager, and the manager must create a context with that timeout before calling `Stop` on plugins. The decision log plugin should then respect that deadline during its flush loop.

Edge cases to address:
- If the graceful shutdown period is zero or negative, no flush should be attempted; the plugin should stop immediately.
- If the upload succeeds quickly (all events sent before the deadline), shutdown should proceed immediately without waiting for the full deadline.
- If uploads continue to fail (e.g., the remote service is unreachable), the flush loop should retry with a short, fixed delay (no exponential backoff) and terminate at the deadline.
- The flush must be safe concurrent with ongoing decision logging; Log calls should continue to be accepted and buffered until the flush completes or deadline expires.

Flush buffered decision logs on graceful shutdown