Skip to content

[DSPX-3302] (4/5) xtest conftest: --scenario and --instance flags#453

Draft
dmihalcik-virtru wants to merge 26 commits into
DSPX-3302-03-multi-instancefrom
DSPX-3302-04-xtest-conftest
Draft

[DSPX-3302] (4/5) xtest conftest: --scenario and --instance flags#453
dmihalcik-virtru wants to merge 26 commits into
DSPX-3302-03-multi-instancefrom
DSPX-3302-04-xtest-conftest

Conversation

@dmihalcik-virtru

@dmihalcik-virtru dmihalcik-virtru commented May 15, 2026

Copy link
Copy Markdown
Member

Summary

Fourth PR in the five-part stack. Adds two new pytest CLI options to xtest/conftest.py:

  • --scenario PATH — when set, defaults --sdks-encrypt, --sdks-decrypt, and --containers from the scenario file's sdks and suite blocks. CLI-passed options always win.
  • --instance NAME — propagated to OTDF_LOCAL_INSTANCE_NAME so any nested otdf-local invocation agrees on the instance.

The authoritative entry point remains otdf-local scenario run <path> (PR 3), which sets these flags for you. This PR lets pytest accept them directly when running scenario-aware sessions outside the wrapper (e.g., a developer iterating with pytest --scenario foo.yaml -k some_test).

The --scenario import of otdf_sdk_mgr.schema is guarded with a try/except so minimal pytest environments that don't have otdf-sdk-mgr installed silently no-op.

Stack

  1. (base) Shared schema — chore(xtest): Shared Scenario/Instance Pydantic schema in otdf-sdk-mgr #450
  2. (base) Platform installer + install scenario — feat(xtest): Lets otdf-sdk-mgr manage platform too #451
  3. (base) otdf-local multi-instance refactor — feat(xtest): otdf-local multi-instance refactor #452
  4. This PR — xtest conftest integration
  5. Claude plugin

Test plan

  • cd xtest && uv run pytest --collect-only -q → 103 tests still collect cleanly
  • uv run pytest --help shows --scenario and --instance under custom options
  • No regressions to existing --sdks-encrypt / --sdks-decrypt / --containers parametrization

Jira: https://virtru.atlassian.net/browse/DSPX-3302

🤖 Generated with Claude Code

Stack (a60d3302):

Generated by wgo stack. Edit text above or below this block, not inside it.

@coderabbitai

coderabbitai Bot commented May 15, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9f1ec9d3-4410-45e7-b6ed-0e36cd08086d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch DSPX-3302-04-xtest-conftest

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces new pytest CLI options, --scenario and --instance, and implements logic in pytest_configure to handle scenario-based defaults and environment variable propagation. The review feedback suggests using explicit is None checks for CLI options to ensure that user-provided empty strings are respected as overrides, rather than being ignored in favor of defaults. Additionally, a redundant import os statement was identified for removal.

Comment thread xtest/conftest.py
Comment on lines +131 to +135
import os

instance = config.getoption("--instance")
if instance:
os.environ["OTDF_LOCAL_INSTANCE_NAME"] = instance

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The import os statement is redundant as it is already imported at the top of the file (line 18). Additionally, the check if instance: should be replaced with if instance is not None: to correctly handle cases where an empty string is explicitly passed via the CLI, ensuring it is respected and not ignored in favor of scenario defaults.

Suggested change
import os
instance = config.getoption("--instance")
if instance:
os.environ["OTDF_LOCAL_INSTANCE_NAME"] = instance
instance = config.getoption("--instance")
if instance is not None:
os.environ["OTDF_LOCAL_INSTANCE_NAME"] = instance
References
  1. Redundant imports should be removed to maintain code cleanliness and avoid confusion.
  2. When implementing configuration overrides, ensure that explicit user input (even if empty) is prioritized over default values.

Comment thread xtest/conftest.py Outdated
Comment on lines +146 to +153
if not config.getoption("--sdks-encrypt") and scenario.sdks.encrypt:
config.option.sdks_encrypt = " ".join(scenario.sdks.encrypt.keys())
if not config.getoption("--sdks-decrypt") and scenario.sdks.decrypt:
config.option.sdks_decrypt = " ".join(scenario.sdks.decrypt.keys())
if not config.getoption("--containers") and scenario.suite.containers:
config.option.containers = scenario.suite.containers
if not instance and scenario.instance.metadata.name:
os.environ["OTDF_LOCAL_INSTANCE_NAME"] = scenario.instance.metadata.name

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Use is None checks for CLI options to ensure that explicit overrides (including empty strings) are respected and not overwritten by scenario defaults. This maintains the 'CLI wins' principle described in the function's docstring.

Suggested change
if not config.getoption("--sdks-encrypt") and scenario.sdks.encrypt:
config.option.sdks_encrypt = " ".join(scenario.sdks.encrypt.keys())
if not config.getoption("--sdks-decrypt") and scenario.sdks.decrypt:
config.option.sdks_decrypt = " ".join(scenario.sdks.decrypt.keys())
if not config.getoption("--containers") and scenario.suite.containers:
config.option.containers = scenario.suite.containers
if not instance and scenario.instance.metadata.name:
os.environ["OTDF_LOCAL_INSTANCE_NAME"] = scenario.instance.metadata.name
if config.getoption("--sdks-encrypt") is None and scenario.sdks.encrypt:
config.option.sdks_encrypt = " ".join(scenario.sdks.encrypt.keys())
if config.getoption("--sdks-decrypt") is None and scenario.sdks.decrypt:
config.option.sdks_decrypt = " ".join(scenario.sdks.decrypt.keys())
if config.getoption("--containers") is None and scenario.suite.containers:
config.option.containers = scenario.suite.containers
if instance is None and scenario.instance.metadata.name:
os.environ["OTDF_LOCAL_INSTANCE_NAME"] = scenario.instance.metadata.name
References
  1. When implementing configuration overrides, ensure that explicit user input (even if empty) is prioritized over default values.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces new CLI options, --scenario and --instance, to the pytest configuration and implements a pytest_configure hook to manage scenario-based defaults and environment variable propagation. The review feedback highlights that using falsiness checks for CLI options prevents users from overriding scenario defaults with empty values; it is recommended to use is None checks instead to ensure CLI-passed options always take precedence. Additionally, a redundant local import of the os module was identified.

Comment thread xtest/conftest.py
Comment on lines +131 to +153
import os

instance = config.getoption("--instance")
if instance:
os.environ["OTDF_LOCAL_INSTANCE_NAME"] = instance

scenario_path = config.getoption("--scenario")
if not scenario_path:
return
try:
from otdf_sdk_mgr.schema import load_scenario
except ImportError:
# otdf-sdk-mgr may not be installed in a minimal pytest env.
return
scenario = load_scenario(scenario_path)
if not config.getoption("--sdks-encrypt") and scenario.sdks.encrypt:
config.option.sdks_encrypt = " ".join(scenario.sdks.encrypt.keys())
if not config.getoption("--sdks-decrypt") and scenario.sdks.decrypt:
config.option.sdks_decrypt = " ".join(scenario.sdks.decrypt.keys())
if not config.getoption("--containers") and scenario.suite.containers:
config.option.containers = scenario.suite.containers
if not instance and scenario.instance.metadata.name:
os.environ["OTDF_LOCAL_INSTANCE_NAME"] = scenario.instance.metadata.name

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current implementation uses falsiness checks (if not ...) to determine if CLI options were provided. This prevents users from explicitly overriding scenario defaults with empty values (e.g., --sdks-encrypt "" to run no encryption SDKs), as an empty string is falsy and will be replaced by the scenario's default. Using is None checks ensures that only unprovided options are populated from the scenario file, adhering to the principle that CLI-passed options always win.

Additionally, the local import os is redundant as it is already imported at the top of the file (line 18).

    instance = config.getoption("--instance")
    if instance is not None:
        os.environ["OTDF_LOCAL_INSTANCE_NAME"] = instance

    scenario_path = config.getoption("--scenario")
    if not scenario_path:
        return
    try:
        from otdf_sdk_mgr.schema import load_scenario
    except ImportError:
        # otdf-sdk-mgr may not be installed in a minimal pytest env.
        return
    scenario = load_scenario(scenario_path)
    if config.getoption("--sdks-encrypt") is None and scenario.sdks.encrypt:
        config.option.sdks_encrypt = " ".join(scenario.sdks.encrypt.keys())
    if config.getoption("--sdks-decrypt") is None and scenario.sdks.decrypt:
        config.option.sdks_decrypt = " ".join(scenario.sdks.decrypt.keys())
    if config.getoption("--containers") is None and scenario.suite.containers:
        config.option.containers = scenario.suite.containers
    if instance is None and scenario.instance.metadata.name:
        os.environ["OTDF_LOCAL_INSTANCE_NAME"] = scenario.instance.metadata.name

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces new --scenario and --instance CLI options to the pytest configuration, along with a new pytest_configure function to handle scenario-based defaults and environment variable propagation. The review identifies a potential issue where the current logic for applying scenario defaults to SDK flags ignores the general --sdks CLI flag, and provides a code suggestion to ensure that CLI-provided options correctly take precedence.

Comment thread xtest/conftest.py Outdated
Comment on lines +146 to +149
if not config.getoption("--sdks-encrypt") and scenario.sdks.encrypt:
config.option.sdks_encrypt = " ".join(scenario.sdks.encrypt.keys())
if not config.getoption("--sdks-decrypt") and scenario.sdks.decrypt:
config.option.sdks_decrypt = " ".join(scenario.sdks.decrypt.keys())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current logic for applying scenario defaults to --sdks-encrypt and --sdks-decrypt does not account for the general --sdks CLI flag. Since pytest_generate_tests (lines 199-203 and 214-218) uses --sdks as a fallback when the specific flags are missing, programmatically setting config.option.sdks_encrypt or config.option.sdks_decrypt from the scenario file will cause any CLI-provided --sdks value to be ignored. To ensure that "CLI-passed options always win" as stated in the PR description, these defaults should only be applied if neither the specific flag nor the general --sdks flag was provided on the command line.

Suggested change
if not config.getoption("--sdks-encrypt") and scenario.sdks.encrypt:
config.option.sdks_encrypt = " ".join(scenario.sdks.encrypt.keys())
if not config.getoption("--sdks-decrypt") and scenario.sdks.decrypt:
config.option.sdks_decrypt = " ".join(scenario.sdks.decrypt.keys())
if not config.getoption("--sdks-encrypt") and not config.getoption("--sdks") and scenario.sdks.encrypt:
config.option.sdks_encrypt = " ".join(scenario.sdks.encrypt.keys())
if not config.getoption("--sdks-decrypt") and not config.getoption("--sdks") and scenario.sdks.decrypt:
config.option.sdks_decrypt = " ".join(scenario.sdks.decrypt.keys())

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request enhances the pytest configuration by adding --scenario and --instance CLI options. A new pytest_configure hook is implemented to propagate the instance name via environment variables and to load default values for SDK and container settings from a scenario file when provided. The review feedback points out a redundant local import of the os module that should be removed to maintain code cleanliness and follow PEP 8 standards.

Comment thread xtest/conftest.py
via `OTDF_LOCAL_INSTANCE_NAME` so any child `otdf-local` invocation sees
the same instance.
"""
import os

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The os module is already imported at the top of the file (line 18). This local import is redundant and can be removed to improve code clarity and adhere to Python's best practices (PEP 8), which recommend placing all imports at the top of the module.

@github-actions

Copy link
Copy Markdown

@dmihalcik-virtru dmihalcik-virtru force-pushed the DSPX-3302-03-multi-instance branch from c69afd6 to a8ef24a Compare May 15, 2026 16:36
@dmihalcik-virtru dmihalcik-virtru force-pushed the DSPX-3302-04-xtest-conftest branch from 6d2e83c to f23ccce Compare May 15, 2026 16:37
@dmihalcik-virtru dmihalcik-virtru force-pushed the DSPX-3302-03-multi-instance branch from a8ef24a to 78b2ca6 Compare May 15, 2026 16:58
@dmihalcik-virtru dmihalcik-virtru force-pushed the DSPX-3302-04-xtest-conftest branch from f23ccce to 6498765 Compare May 15, 2026 17:02
dmihalcik-virtru added a commit that referenced this pull request May 21, 2026
#450)

## Summary

First PR in a five-part stack that introduces a multi-instance test
harness and a Claude plugin for OpenTDF bug reproduction. This PR adds
*only* the shared Pydantic schema in `otdf-sdk-mgr` — no consumers yet.

- Adds `otdf_sdk_mgr.schema` with v2 models: `Scenario`, `Instance`,
`PlatformPin`, `KasPin`, `SdkPin`, `ScenarioSdks`, `Suite`, etc.
- `ScenarioSdks.encrypt` / `.decrypt` mirror xtest's existing
`--sdks-encrypt` / `--sdks-decrypt` convention so a→b-only scenarios are
first-class.
- `python -m otdf_sdk_mgr.schema validate <path>` validates either a
Scenario or an Instance file based on its `kind:`.
- Adds `pydantic` + `ruamel.yaml` to `otdf-sdk-mgr/pyproject.toml`.
- 6 unit tests covering round-trips, pin invariants, and unknown-field
rejection.

## Stack

1. [**This PR**](#450) — Shared
schema
2. [Platform installer + `install
scenario`](#451) in `otdf-sdk-mgr`
(builds on this)
3. `otdf-local` [multi-instance
refactor](#452) + new CLI
subcommands
4. `xtest/conftest.py`
[integration](#453) (`--scenario`,
`--instance`)
5. [Claude plugin](#454)
(`.claude/skills/`, settings, plugin manifest)
6. #455

## Test plan

- [x] `cd otdf-sdk-mgr && uv run pytest tests/test_schema.py` — all 6
pass
- [x] `uv run python -m otdf_sdk_mgr.schema validate <path>` accepts a
valid scenarios.yaml and rejects unknown fields

Jira: https://virtru.atlassian.net/browse/DSPX-3302

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added schema validation for OpenTDF Scenario and Instance YAML
configurations with a new CLI command.
* Introduced strict validation with cross-field constraints for SDK and
platform configurations.

* **Documentation**
  * Updated supported container formats from `nano` to `ztdf-ecwrap`.

* **Dependencies**
* Updated core package dependencies to support enhanced validation
capabilities.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/opentdf/tests/pull/450?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dmihalcik-virtru dmihalcik-virtru force-pushed the DSPX-3302-03-multi-instance branch from 78b2ca6 to e196e43 Compare May 21, 2026 15:38
@dmihalcik-virtru dmihalcik-virtru force-pushed the DSPX-3302-04-xtest-conftest branch from 6498765 to 5877740 Compare May 21, 2026 15:38
@github-actions

Copy link
Copy Markdown

X-Test Failure Report

@github-actions

Copy link
Copy Markdown

X-Test Failure Report

✅ java@main-main
✅ js@main-main
✅ js@v0.4.0-main

@dmihalcik-virtru dmihalcik-virtru force-pushed the DSPX-3302-03-multi-instance branch 2 times, most recently from 5b1c928 to a1bcecc Compare May 22, 2026 13:50
@dmihalcik-virtru dmihalcik-virtru force-pushed the DSPX-3302-04-xtest-conftest branch from 5877740 to fe5c8ae Compare May 22, 2026 13:51
@github-actions

Copy link
Copy Markdown

X-Test Failure Report

@github-actions

Copy link
Copy Markdown

X-Test Failure Report

1 similar comment
@github-actions

Copy link
Copy Markdown

X-Test Failure Report

@dmihalcik-virtru dmihalcik-virtru force-pushed the DSPX-3302-03-multi-instance branch from 6d83353 to b441b38 Compare June 10, 2026 18:37
dependabot Bot and others added 26 commits June 12, 2026 13:56
)

Bumps [typer](https://github.com/fastapi/typer) from 0.26.5 to 0.26.6.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/fastapi/typer/releases">typer's
releases</a>.</em></p>
<blockquote>
<h2>0.26.6</h2>
<h3>Fixes</h3>
<ul>
<li>🐛 Ensure that the default of a list argument is used correctly. PR
<a href="https://redirect.github.com/fastapi/typer/pull/1821">#1821</a>
by <a
href="https://github.com/svlandeg"><code>@​svlandeg</code></a>.</li>
</ul>
<h3>Internal</h3>
<ul>
<li>👷 Speed up the CI by shuffling the test matrix. PR <a
href="https://redirect.github.com/fastapi/typer/pull/1815">#1815</a> by
<a href="https://github.com/svlandeg"><code>@​svlandeg</code></a>.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/fastapi/typer/blob/master/docs/release-notes.md">typer's
changelog</a>.</em></p>
<blockquote>
<h2>0.26.6 (2026-06-02)</h2>
<h3>Fixes</h3>
<ul>
<li>🐛 Ensure that the default of a list argument is used correctly. PR
<a href="https://redirect.github.com/fastapi/typer/pull/1821">#1821</a>
by <a
href="https://github.com/svlandeg"><code>@​svlandeg</code></a>.</li>
</ul>
<h3>Internal</h3>
<ul>
<li>👷 Speed up the CI by shuffling the test matrix. PR <a
href="https://redirect.github.com/fastapi/typer/pull/1815">#1815</a> by
<a href="https://github.com/svlandeg"><code>@​svlandeg</code></a>.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/fastapi/typer/commit/d2e002ab0d0afc9527cfacc00dd4c359a44898e9"><code>d2e002a</code></a>
🔖 Release version 0.26.6 (<a
href="https://redirect.github.com/fastapi/typer/issues/1822">#1822</a>)</li>
<li><a
href="https://github.com/fastapi/typer/commit/2fe5d0c2b63db2243b3f3ad37d9f164791a390b1"><code>2fe5d0c</code></a>
📝 Update release notes</li>
<li><a
href="https://github.com/fastapi/typer/commit/06fce7dc851de662792c840320abe990ddd3566e"><code>06fce7d</code></a>
🐛 Ensure that the default of a list argument is used correctly (<a
href="https://redirect.github.com/fastapi/typer/issues/1821">#1821</a>)</li>
<li><a
href="https://github.com/fastapi/typer/commit/7f888b24c867458ab9547702720b71dc920c8925"><code>7f888b2</code></a>
📝 Update release notes</li>
<li><a
href="https://github.com/fastapi/typer/commit/2e9bf9e49401e58c59947a28f062566343f94c8f"><code>2e9bf9e</code></a>
👷 Speed up the CI by shuffling the test matrix (<a
href="https://redirect.github.com/fastapi/typer/issues/1815">#1815</a>)</li>
<li>See full diff in <a
href="https://github.com/fastapi/typer/compare/0.26.5...0.26.6">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=typer&package-manager=uv&previous-version=0.26.5&new-version=0.26.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [typer](https://github.com/fastapi/typer) from 0.26.5 to 0.26.6.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/fastapi/typer/releases">typer's
releases</a>.</em></p>
<blockquote>
<h2>0.26.6</h2>
<h3>Fixes</h3>
<ul>
<li>🐛 Ensure that the default of a list argument is used correctly. PR
<a href="https://redirect.github.com/fastapi/typer/pull/1821">#1821</a>
by <a
href="https://github.com/svlandeg"><code>@​svlandeg</code></a>.</li>
</ul>
<h3>Internal</h3>
<ul>
<li>👷 Speed up the CI by shuffling the test matrix. PR <a
href="https://redirect.github.com/fastapi/typer/pull/1815">#1815</a> by
<a href="https://github.com/svlandeg"><code>@​svlandeg</code></a>.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/fastapi/typer/blob/master/docs/release-notes.md">typer's
changelog</a>.</em></p>
<blockquote>
<h2>0.26.6 (2026-06-02)</h2>
<h3>Fixes</h3>
<ul>
<li>🐛 Ensure that the default of a list argument is used correctly. PR
<a href="https://redirect.github.com/fastapi/typer/pull/1821">#1821</a>
by <a
href="https://github.com/svlandeg"><code>@​svlandeg</code></a>.</li>
</ul>
<h3>Internal</h3>
<ul>
<li>👷 Speed up the CI by shuffling the test matrix. PR <a
href="https://redirect.github.com/fastapi/typer/pull/1815">#1815</a> by
<a href="https://github.com/svlandeg"><code>@​svlandeg</code></a>.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/fastapi/typer/commit/d2e002ab0d0afc9527cfacc00dd4c359a44898e9"><code>d2e002a</code></a>
🔖 Release version 0.26.6 (<a
href="https://redirect.github.com/fastapi/typer/issues/1822">#1822</a>)</li>
<li><a
href="https://github.com/fastapi/typer/commit/2fe5d0c2b63db2243b3f3ad37d9f164791a390b1"><code>2fe5d0c</code></a>
📝 Update release notes</li>
<li><a
href="https://github.com/fastapi/typer/commit/06fce7dc851de662792c840320abe990ddd3566e"><code>06fce7d</code></a>
🐛 Ensure that the default of a list argument is used correctly (<a
href="https://redirect.github.com/fastapi/typer/issues/1821">#1821</a>)</li>
<li><a
href="https://github.com/fastapi/typer/commit/7f888b24c867458ab9547702720b71dc920c8925"><code>7f888b2</code></a>
📝 Update release notes</li>
<li><a
href="https://github.com/fastapi/typer/commit/2e9bf9e49401e58c59947a28f062566343f94c8f"><code>2e9bf9e</code></a>
👷 Speed up the CI by shuffling the test matrix (<a
href="https://redirect.github.com/fastapi/typer/issues/1815">#1815</a>)</li>
<li>See full diff in <a
href="https://github.com/fastapi/typer/compare/0.26.5...0.26.6">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=typer&package-manager=uv&previous-version=0.26.5&new-version=0.26.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Dave Mihalcik <dmihalcik@virtru.com>
## Summary

- Adds `test_dpop.py`: positive DPoP flow, negative flow, Bearer-vs-DPoP
authorization scheme drift, and nonce-challenge gate tests
- Wires `dpop` and `dpop_nonce_challenge` feature gates in `conftest.py`
with `--require-features` flag for red-green-red TDD
- Adds CI step: `test_dpop.py` included in the attribute-based xtest
workflow job
- Adds `xtest/scenarios/DSPX-3397.yaml` pinning platform + SDK topology
for this feature
- Adds `xtest/features/DSPX-3397.yaml` feature spec and
`mechanism-mlkem.yaml`
- Adds `xtest/schema/` with generated JSON Schemas for scenario/instance
models
- Updates SDK shims, `pyproject.toml`, and `uv.lock`

This PR contains only `xtest/` and `.github/` changes from the DSPX-3397
DPoP work, rebased on main so it can merge independently of parent PRs.

Closes DSPX-3397 (tests side)

## Test plan

- [x] CI passes the xtest workflow with `test_dpop.py` included
- [x] `test_dpop.py` tests are skipped (not failed) when `dpop` feature
gate is absent from the platform
- [ ] Once platform ships DPoP support, tests go green end-to-end

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Tests**
* Added comprehensive DPoP (RFC 9449) integration test suite covering
secure token proof scenarios, including happy-path flows, replay
protection, and tampered proof rejection.
* Extended test infrastructure to detect DPoP and nonce-challenge
capabilities across SDKs and platform configurations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…519)

Bumps [ruff](https://github.com/astral-sh/ruff) from 0.15.15 to 0.15.16.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/astral-sh/ruff/releases">ruff's
releases</a>.</em></p>
<blockquote>
<h2>0.15.16</h2>
<h2>Release Notes</h2>
<p>Released on 2026-06-04.</p>
<h3>Preview features</h3>
<ul>
<li>[<code>flake8-async</code>] Implement
<code>yield-in-context-manager-in-async-generator</code>
(<code>ASYNC119</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24644">#24644</a>)</li>
<li>[<code>pylint</code>] Narrow diagnostic range and exclude cases
without exception handlers (<code>PLW0717</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25440">#25440</a>)</li>
<li>[<code>ruff</code>] Treat <code>yield</code> before
<code>break</code> from a terminal loop as terminal
(<code>RUF075</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25447">#25447</a>)</li>
</ul>
<h3>Bug fixes</h3>
<ul>
<li>[<code>eradicate</code>] Avoid flagging <code>ruff:ignore</code>
comments as code (<code>ERA001</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25537">#25537</a>)</li>
<li>[<code>eradicate</code>] Fix <code>ERA001</code>/<code>RUF100</code>
conflict when <code>noqa</code> is on commented-out code (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25414">#25414</a>)</li>
<li>[<code>pyflakes</code>] Avoid removing the <code>format</code> call
when it would change behavior (<code>F523</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25320">#25320</a>)</li>
<li>[<code>pylint</code>] Avoid syntax errors in invalid character
replacements in f-strings before Python 3.12 (<code>PLE2510</code>,
<code>PLE2512</code>, <code>PLE2513</code>, <code>PLE2514</code>,
<code>PLE2515</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25544">#25544</a>)</li>
<li>[<code>pyupgrade</code>] Avoid converting <code>format</code> calls
with more kinds of side effects (<code>UP032</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25484">#25484</a>)</li>
</ul>
<h3>Rule changes</h3>
<ul>
<li>[<code>flake8-pytest-style</code>] Avoid fixes for ambiguous
<code>argnames</code> and <code>argvalues</code> combinations
(<code>PT006</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24776">#24776</a>)</li>
</ul>
<h3>Performance</h3>
<ul>
<li>Drop excess capacity from statement suites during parsing (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25368">#25368</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>[<code>pydocstyle</code>] Improve discoverability of rules enabled
for each convention (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24973">#24973</a>)</li>
<li>[<code>ruff</code>] Restore example code for Python versions before
3.15 (<code>RUF017</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25439">#25439</a>)</li>
<li>Fix typo <code>bin/active</code> → <code>bin/activate</code> in
tutorial (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25473">#25473</a>)</li>
</ul>
<h3>Other changes</h3>
<ul>
<li>Shrink additional parser AST collections (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25465">#25465</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a
href="https://github.com/Redslayer112"><code>@​Redslayer112</code></a></li>
<li><a
href="https://github.com/koriyoshi2041"><code>@​koriyoshi2041</code></a></li>
<li><a
href="https://github.com/George-Ogden"><code>@​George-Ogden</code></a></li>
<li><a
href="https://github.com/TejasAmle"><code>@​TejasAmle</code></a></li>
<li><a
href="https://github.com/anishgirianish"><code>@​anishgirianish</code></a></li>
<li><a href="https://github.com/ntBre"><code>@​ntBre</code></a></li>
<li><a
href="https://github.com/MichaReiser"><code>@​MichaReiser</code></a></li>
<li><a
href="https://github.com/loganrosen"><code>@​loganrosen</code></a></li>
<li><a
href="https://github.com/RafaelJohn9"><code>@​RafaelJohn9</code></a></li>
<li><a
href="https://github.com/adityasingh2400"><code>@​adityasingh2400</code></a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md">ruff's
changelog</a>.</em></p>
<blockquote>
<h2>0.15.16</h2>
<p>Released on 2026-06-04.</p>
<h3>Preview features</h3>
<ul>
<li>[<code>flake8-async</code>] Implement
<code>yield-in-context-manager-in-async-generator</code>
(<code>ASYNC119</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24644">#24644</a>)</li>
<li>[<code>pylint</code>] Narrow diagnostic range and exclude cases
without exception handlers (<code>PLW0717</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25440">#25440</a>)</li>
<li>[<code>ruff</code>] Treat <code>yield</code> before
<code>break</code> from a terminal loop as terminal
(<code>RUF075</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25447">#25447</a>)</li>
</ul>
<h3>Bug fixes</h3>
<ul>
<li>[<code>eradicate</code>] Avoid flagging <code>ruff:ignore</code>
comments as code (<code>ERA001</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25537">#25537</a>)</li>
<li>[<code>eradicate</code>] Fix <code>ERA001</code>/<code>RUF100</code>
conflict when <code>noqa</code> is on commented-out code (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25414">#25414</a>)</li>
<li>[<code>pyflakes</code>] Avoid removing the <code>format</code> call
when it would change behavior (<code>F523</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25320">#25320</a>)</li>
<li>[<code>pylint</code>] Avoid syntax errors in invalid character
replacements in f-strings before Python 3.12 (<code>PLE2510</code>,
<code>PLE2512</code>, <code>PLE2513</code>, <code>PLE2514</code>,
<code>PLE2515</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25544">#25544</a>)</li>
<li>[<code>pyupgrade</code>] Avoid converting <code>format</code> calls
with more kinds of side effects (<code>UP032</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25484">#25484</a>)</li>
</ul>
<h3>Rule changes</h3>
<ul>
<li>[<code>flake8-pytest-style</code>] Avoid fixes for ambiguous
<code>argnames</code> and <code>argvalues</code> combinations
(<code>PT006</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24776">#24776</a>)</li>
</ul>
<h3>Performance</h3>
<ul>
<li>Drop excess capacity from statement suites during parsing (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25368">#25368</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>[<code>pydocstyle</code>] Improve discoverability of rules enabled
for each convention (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24973">#24973</a>)</li>
<li>[<code>ruff</code>] Restore example code for Python versions before
3.15 (<code>RUF017</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25439">#25439</a>)</li>
<li>Fix typo <code>bin/active</code> → <code>bin/activate</code> in
tutorial (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25473">#25473</a>)</li>
</ul>
<h3>Other changes</h3>
<ul>
<li>Shrink additional parser AST collections (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25465">#25465</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a
href="https://github.com/Redslayer112"><code>@​Redslayer112</code></a></li>
<li><a
href="https://github.com/koriyoshi2041"><code>@​koriyoshi2041</code></a></li>
<li><a
href="https://github.com/George-Ogden"><code>@​George-Ogden</code></a></li>
<li><a
href="https://github.com/TejasAmle"><code>@​TejasAmle</code></a></li>
<li><a
href="https://github.com/anishgirianish"><code>@​anishgirianish</code></a></li>
<li><a href="https://github.com/ntBre"><code>@​ntBre</code></a></li>
<li><a
href="https://github.com/MichaReiser"><code>@​MichaReiser</code></a></li>
<li><a
href="https://github.com/loganrosen"><code>@​loganrosen</code></a></li>
<li><a
href="https://github.com/RafaelJohn9"><code>@​RafaelJohn9</code></a></li>
<li><a
href="https://github.com/adityasingh2400"><code>@​adityasingh2400</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/astral-sh/ruff/commit/6c498ab5394edc5622d7f348e12956bf86203716"><code>6c498ab</code></a>
Bump 0.15.16 (<a
href="https://redirect.github.com/astral-sh/ruff/issues/25635">#25635</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/e51e132831c4e1c4a5ac00fca4c9256354ab99bf"><code>e51e132</code></a>
[<code>flake8-async</code>] Implement
<code>yield-in-context-manager-in-async-generator</code> (`AS...</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/7c6dcd9f2611999c449143d241c582dedf287964"><code>7c6dcd9</code></a>
[ty] Add caching for pattern match narrowing (<a
href="https://redirect.github.com/astral-sh/ruff/issues/25613">#25613</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/27058fc071b542bf06395ba89cabed061d313ca6"><code>27058fc</code></a>
[ty] Compact retained definition and expression identities (<a
href="https://redirect.github.com/astral-sh/ruff/issues/25606">#25606</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/bf80d05f007c939799f530c9e775ed9449f5b2eb"><code>bf80d05</code></a>
Fix CODEOWNERS syntax (<a
href="https://redirect.github.com/astral-sh/ruff/issues/25622">#25622</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/10ccd511e94a81d1e836b174f1c553a73ff3f1b3"><code>10ccd51</code></a>
Shrink additional parser AST collections (<a
href="https://redirect.github.com/astral-sh/ruff/issues/25465">#25465</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/0d7135f4d23e7f4d8404daed16b9ef11d14f3fb9"><code>0d7135f</code></a>
[ty] Upgrade Salsa (<a
href="https://redirect.github.com/astral-sh/ruff/issues/25545">#25545</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/49493a3cea83a08fa9aa143695017c816a540f1d"><code>49493a3</code></a>
[ty] Show type alias value on hover (<a
href="https://redirect.github.com/astral-sh/ruff/issues/25381">#25381</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/85207d3b7657a84252f266766cb0d56034dc21cc"><code>85207d3</code></a>
[ty] sys.implementation.version is not sys.version_info (<a
href="https://redirect.github.com/astral-sh/ruff/issues/25608">#25608</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/a8a0614348c1fcf47fc9b666eff61a103914d520"><code>a8a0614</code></a>
[ty] Avoid retaining duplicate function signatures (<a
href="https://redirect.github.com/astral-sh/ruff/issues/25609">#25609</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/astral-sh/ruff/compare/0.15.15...0.15.16">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=ruff&package-manager=uv&previous-version=0.15.15&new-version=0.15.16)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…11.19 in /otdf-sdk-mgr (#518)

Updates the requirements on [uv-build](https://github.com/astral-sh/uv)
to permit the latest version.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/astral-sh/uv/releases">uv-build's
releases</a>.</em></p>
<blockquote>
<h2>0.11.19</h2>
<h2>Release Notes</h2>
<p>Released on 2026-06-03.</p>
<h3>Python</h3>
<ul>
<li>Add CPython 3.15.0b2 (<a
href="https://redirect.github.com/astral-sh/uv/pull/19531">#19531</a>)</li>
</ul>
<h3>Enhancements</h3>
<ul>
<li>Always compute SHA256 for remote distributions (<a
href="https://redirect.github.com/astral-sh/uv/pull/19662">#19662</a>)</li>
<li>Add PyEmscripten platform (PEP 783) (<a
href="https://redirect.github.com/astral-sh/uv/pull/19629">#19629</a>)</li>
<li>Add Pyodide 2025 target triple (<a
href="https://redirect.github.com/astral-sh/uv/pull/19653">#19653</a>)</li>
</ul>
<h3>Preview features</h3>
<ul>
<li>Make preview features for commands have names that aren't ambiguous
with the command (<a
href="https://redirect.github.com/astral-sh/uv/pull/19645">#19645</a>)</li>
<li>Respect <code>--isolated</code> in <code>uv check</code> (<a
href="https://redirect.github.com/astral-sh/uv/pull/19666">#19666</a>)</li>
</ul>
<h3>Bug fixes</h3>
<ul>
<li>Continue tool uninstall after dangling receipts (<a
href="https://redirect.github.com/astral-sh/uv/pull/19623">#19623</a>)</li>
<li>Skip Unix-specific installation steps when cross-installing Windows
Python distributions (<a
href="https://redirect.github.com/astral-sh/uv/pull/19424">#19424</a>)</li>
</ul>
<h2>Install uv 0.11.19</h2>
<h3>Install prebuilt binaries via shell script</h3>
<pre lang="sh"><code>curl --proto '=https' --tlsv1.2 -LsSf
https://releases.astral.sh/github/uv/releases/download/0.11.19/uv-installer.sh
| sh
</code></pre>
<h3>Install prebuilt binaries via powershell script</h3>
<pre lang="sh"><code>powershell -ExecutionPolicy Bypass -c &quot;irm
https://releases.astral.sh/github/uv/releases/download/0.11.19/uv-installer.ps1
| iex&quot;
</code></pre>
<h2>Download uv 0.11.19</h2>
<table>
<thead>
<tr>
<th>File</th>
<th>Platform</th>
<th>Checksum</th>
</tr>
</thead>
<tbody>
<tr>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.19/uv-aarch64-apple-darwin.tar.gz">uv-aarch64-apple-darwin.tar.gz</a></td>
<td>Apple Silicon macOS</td>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.19/uv-aarch64-apple-darwin.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.19/uv-x86_64-apple-darwin.tar.gz">uv-x86_64-apple-darwin.tar.gz</a></td>
<td>Intel macOS</td>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.19/uv-x86_64-apple-darwin.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.19/uv-aarch64-pc-windows-msvc.zip">uv-aarch64-pc-windows-msvc.zip</a></td>
<td>ARM64 Windows</td>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.19/uv-aarch64-pc-windows-msvc.zip.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.19/uv-i686-pc-windows-msvc.zip">uv-i686-pc-windows-msvc.zip</a></td>
<td>x86 Windows</td>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.19/uv-i686-pc-windows-msvc.zip.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.19/uv-x86_64-pc-windows-msvc.zip">uv-x86_64-pc-windows-msvc.zip</a></td>
<td>x64 Windows</td>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.19/uv-x86_64-pc-windows-msvc.zip.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.19/uv-aarch64-unknown-linux-gnu.tar.gz">uv-aarch64-unknown-linux-gnu.tar.gz</a></td>
<td>ARM64 Linux</td>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.19/uv-aarch64-unknown-linux-gnu.tar.gz.sha256">checksum</a></td>
</tr>
<tr>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.19/uv-i686-unknown-linux-gnu.tar.gz">uv-i686-unknown-linux-gnu.tar.gz</a></td>
<td>x86 Linux</td>
<td><a
href="https://releases.astral.sh/github/uv/releases/download/0.11.19/uv-i686-unknown-linux-gnu.tar.gz.sha256">checksum</a></td>
</tr>
</tbody>
</table>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/astral-sh/uv/blob/main/CHANGELOG.md">uv-build's
changelog</a>.</em></p>
<blockquote>
<h2>0.11.19</h2>
<p>Released on 2026-06-03.</p>
<h3>Python</h3>
<ul>
<li>Add CPython 3.15.0b2 (<a
href="https://redirect.github.com/astral-sh/uv/pull/19531">#19531</a>)</li>
</ul>
<h3>Enhancements</h3>
<ul>
<li>Always compute SHA256 for remote distributions (<a
href="https://redirect.github.com/astral-sh/uv/pull/19662">#19662</a>)</li>
<li>Add PyEmscripten platform (PEP 783) (<a
href="https://redirect.github.com/astral-sh/uv/pull/19629">#19629</a>)</li>
<li>Add Pyodide 2025 target triple (<a
href="https://redirect.github.com/astral-sh/uv/pull/19653">#19653</a>)</li>
</ul>
<h3>Preview features</h3>
<ul>
<li>Make preview features for commands have names that aren't ambiguous
with the command (<a
href="https://redirect.github.com/astral-sh/uv/pull/19645">#19645</a>)</li>
<li>Respect <code>--isolated</code> in <code>uv check</code> (<a
href="https://redirect.github.com/astral-sh/uv/pull/19666">#19666</a>)</li>
</ul>
<h3>Bug fixes</h3>
<ul>
<li>Continue tool uninstall after dangling receipts (<a
href="https://redirect.github.com/astral-sh/uv/pull/19623">#19623</a>)</li>
<li>Skip Unix-specific installation steps when cross-installing Windows
Python distributions (<a
href="https://redirect.github.com/astral-sh/uv/pull/19424">#19424</a>)</li>
</ul>
<h2>0.11.18</h2>
<p>Released on 2026-06-01.</p>
<h3>Performance</h3>
<ul>
<li>Fix performance regression in unzip of local wheels (<a
href="https://redirect.github.com/astral-sh/uv/pull/19637">#19637</a>)</li>
</ul>
<h3>Preview</h3>
<ul>
<li>Add <code>uv check</code> to run <code>ty</code> from uv (<a
href="https://redirect.github.com/astral-sh/uv/pull/19605">#19605</a>)</li>
</ul>
<h3>Bug fixes</h3>
<ul>
<li>Update activation scripts with upstream fixes (<a
href="https://redirect.github.com/astral-sh/uv/pull/19628">#19628</a>)</li>
</ul>
<h3>Other changes</h3>
<ul>
<li>Bump MSRV to 1.94 (<a
href="https://redirect.github.com/astral-sh/uv/pull/19600">#19600</a>)</li>
</ul>
<h2>0.11.17</h2>
<p>Released on 2026-05-28.</p>
<h3>Enhancements</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/astral-sh/uv/commit/7b2cff1c316eb3b7f52b1cc121d7e25eeea1b17c"><code>7b2cff1</code></a>
Bump version to 0.11.19 (<a
href="https://redirect.github.com/astral-sh/uv/issues/19668">#19668</a>)</li>
<li><a
href="https://github.com/astral-sh/uv/commit/ebd5033d77bca6a58c329cbbc351406a9415c920"><code>ebd5033</code></a>
Fix setup-crates-io-publish call (<a
href="https://redirect.github.com/astral-sh/uv/issues/19667">#19667</a>)</li>
<li><a
href="https://github.com/astral-sh/uv/commit/9abd9b122b963dc6d71a8b29729efa86b3b41d4c"><code>9abd9b1</code></a>
Respect <code>--isolated</code> in <code>uv check</code> (<a
href="https://redirect.github.com/astral-sh/uv/issues/19666">#19666</a>)</li>
<li><a
href="https://github.com/astral-sh/uv/commit/95507374d81bee4c6ffe55d9ccef60c5120e20b2"><code>9550737</code></a>
Use global preview in
<code>CondaEnvironmentKind::from_prefix_path</code> (<a
href="https://redirect.github.com/astral-sh/uv/issues/19649">#19649</a>)</li>
<li><a
href="https://github.com/astral-sh/uv/commit/b82cadf1555c23a0971bd660ed07fba223290aee"><code>b82cadf</code></a>
Remove more duplication in <code>show_settings</code> tests (<a
href="https://redirect.github.com/astral-sh/uv/issues/19664">#19664</a>)</li>
<li><a
href="https://github.com/astral-sh/uv/commit/d3cd632c83dbce8452cd2d4c3ccd8a3cd8ff5069"><code>d3cd632</code></a>
Always compute SHA256 for remote distributions (<a
href="https://redirect.github.com/astral-sh/uv/issues/19662">#19662</a>)</li>
<li><a
href="https://github.com/astral-sh/uv/commit/48548c496e835a84c9a69d7d7e7d3b4b809fad8c"><code>48548c4</code></a>
Sync latest Python releases (<a
href="https://redirect.github.com/astral-sh/uv/issues/19531">#19531</a>)</li>
<li><a
href="https://github.com/astral-sh/uv/commit/8df759892cd54488c66a77852d7fc3f87a066f64"><code>8df7598</code></a>
fix feature-gates on <code>format</code> and <code>check</code> tests to
specify they access r2 (<a
href="https://redirect.github.com/astral-sh/uv/issues/1">#1</a>...</li>
<li><a
href="https://github.com/astral-sh/uv/commit/68ae09e00cb4a1b7eb512b0ac77b79e4683cfaf4"><code>68ae09e</code></a>
Remove unused <code>pub</code> code from workspace (<a
href="https://redirect.github.com/astral-sh/uv/issues/19565">#19565</a>)</li>
<li><a
href="https://github.com/astral-sh/uv/commit/d4450f385074cd2a639e2525f44608c3c27f6414"><code>d4450f3</code></a>
Remove a bunch of duplication in <code>show_settings</code> tests by
using diffs (<a
href="https://redirect.github.com/astral-sh/uv/issues/19654">#19654</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/astral-sh/uv/compare/0.11.18...0.11.19">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Dave Mihalcik <dmihalcik@virtru.com>
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.15.14 to 0.15.16.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/astral-sh/ruff/releases">ruff's
releases</a>.</em></p>
<blockquote>
<h2>0.15.16</h2>
<h2>Release Notes</h2>
<p>Released on 2026-06-04.</p>
<h3>Preview features</h3>
<ul>
<li>[<code>flake8-async</code>] Implement
<code>yield-in-context-manager-in-async-generator</code>
(<code>ASYNC119</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24644">#24644</a>)</li>
<li>[<code>pylint</code>] Narrow diagnostic range and exclude cases
without exception handlers (<code>PLW0717</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25440">#25440</a>)</li>
<li>[<code>ruff</code>] Treat <code>yield</code> before
<code>break</code> from a terminal loop as terminal
(<code>RUF075</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25447">#25447</a>)</li>
</ul>
<h3>Bug fixes</h3>
<ul>
<li>[<code>eradicate</code>] Avoid flagging <code>ruff:ignore</code>
comments as code (<code>ERA001</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25537">#25537</a>)</li>
<li>[<code>eradicate</code>] Fix <code>ERA001</code>/<code>RUF100</code>
conflict when <code>noqa</code> is on commented-out code (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25414">#25414</a>)</li>
<li>[<code>pyflakes</code>] Avoid removing the <code>format</code> call
when it would change behavior (<code>F523</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25320">#25320</a>)</li>
<li>[<code>pylint</code>] Avoid syntax errors in invalid character
replacements in f-strings before Python 3.12 (<code>PLE2510</code>,
<code>PLE2512</code>, <code>PLE2513</code>, <code>PLE2514</code>,
<code>PLE2515</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25544">#25544</a>)</li>
<li>[<code>pyupgrade</code>] Avoid converting <code>format</code> calls
with more kinds of side effects (<code>UP032</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25484">#25484</a>)</li>
</ul>
<h3>Rule changes</h3>
<ul>
<li>[<code>flake8-pytest-style</code>] Avoid fixes for ambiguous
<code>argnames</code> and <code>argvalues</code> combinations
(<code>PT006</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24776">#24776</a>)</li>
</ul>
<h3>Performance</h3>
<ul>
<li>Drop excess capacity from statement suites during parsing (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25368">#25368</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>[<code>pydocstyle</code>] Improve discoverability of rules enabled
for each convention (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24973">#24973</a>)</li>
<li>[<code>ruff</code>] Restore example code for Python versions before
3.15 (<code>RUF017</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25439">#25439</a>)</li>
<li>Fix typo <code>bin/active</code> → <code>bin/activate</code> in
tutorial (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25473">#25473</a>)</li>
</ul>
<h3>Other changes</h3>
<ul>
<li>Shrink additional parser AST collections (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25465">#25465</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a
href="https://github.com/Redslayer112"><code>@​Redslayer112</code></a></li>
<li><a
href="https://github.com/koriyoshi2041"><code>@​koriyoshi2041</code></a></li>
<li><a
href="https://github.com/George-Ogden"><code>@​George-Ogden</code></a></li>
<li><a
href="https://github.com/TejasAmle"><code>@​TejasAmle</code></a></li>
<li><a
href="https://github.com/anishgirianish"><code>@​anishgirianish</code></a></li>
<li><a href="https://github.com/ntBre"><code>@​ntBre</code></a></li>
<li><a
href="https://github.com/MichaReiser"><code>@​MichaReiser</code></a></li>
<li><a
href="https://github.com/loganrosen"><code>@​loganrosen</code></a></li>
<li><a
href="https://github.com/RafaelJohn9"><code>@​RafaelJohn9</code></a></li>
<li><a
href="https://github.com/adityasingh2400"><code>@​adityasingh2400</code></a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md">ruff's
changelog</a>.</em></p>
<blockquote>
<h2>0.15.16</h2>
<p>Released on 2026-06-04.</p>
<h3>Preview features</h3>
<ul>
<li>[<code>flake8-async</code>] Implement
<code>yield-in-context-manager-in-async-generator</code>
(<code>ASYNC119</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24644">#24644</a>)</li>
<li>[<code>pylint</code>] Narrow diagnostic range and exclude cases
without exception handlers (<code>PLW0717</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25440">#25440</a>)</li>
<li>[<code>ruff</code>] Treat <code>yield</code> before
<code>break</code> from a terminal loop as terminal
(<code>RUF075</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25447">#25447</a>)</li>
</ul>
<h3>Bug fixes</h3>
<ul>
<li>[<code>eradicate</code>] Avoid flagging <code>ruff:ignore</code>
comments as code (<code>ERA001</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25537">#25537</a>)</li>
<li>[<code>eradicate</code>] Fix <code>ERA001</code>/<code>RUF100</code>
conflict when <code>noqa</code> is on commented-out code (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25414">#25414</a>)</li>
<li>[<code>pyflakes</code>] Avoid removing the <code>format</code> call
when it would change behavior (<code>F523</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25320">#25320</a>)</li>
<li>[<code>pylint</code>] Avoid syntax errors in invalid character
replacements in f-strings before Python 3.12 (<code>PLE2510</code>,
<code>PLE2512</code>, <code>PLE2513</code>, <code>PLE2514</code>,
<code>PLE2515</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25544">#25544</a>)</li>
<li>[<code>pyupgrade</code>] Avoid converting <code>format</code> calls
with more kinds of side effects (<code>UP032</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25484">#25484</a>)</li>
</ul>
<h3>Rule changes</h3>
<ul>
<li>[<code>flake8-pytest-style</code>] Avoid fixes for ambiguous
<code>argnames</code> and <code>argvalues</code> combinations
(<code>PT006</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24776">#24776</a>)</li>
</ul>
<h3>Performance</h3>
<ul>
<li>Drop excess capacity from statement suites during parsing (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25368">#25368</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>[<code>pydocstyle</code>] Improve discoverability of rules enabled
for each convention (<a
href="https://redirect.github.com/astral-sh/ruff/pull/24973">#24973</a>)</li>
<li>[<code>ruff</code>] Restore example code for Python versions before
3.15 (<code>RUF017</code>) (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25439">#25439</a>)</li>
<li>Fix typo <code>bin/active</code> → <code>bin/activate</code> in
tutorial (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25473">#25473</a>)</li>
</ul>
<h3>Other changes</h3>
<ul>
<li>Shrink additional parser AST collections (<a
href="https://redirect.github.com/astral-sh/ruff/pull/25465">#25465</a>)</li>
</ul>
<h3>Contributors</h3>
<ul>
<li><a
href="https://github.com/Redslayer112"><code>@​Redslayer112</code></a></li>
<li><a
href="https://github.com/koriyoshi2041"><code>@​koriyoshi2041</code></a></li>
<li><a
href="https://github.com/George-Ogden"><code>@​George-Ogden</code></a></li>
<li><a
href="https://github.com/TejasAmle"><code>@​TejasAmle</code></a></li>
<li><a
href="https://github.com/anishgirianish"><code>@​anishgirianish</code></a></li>
<li><a href="https://github.com/ntBre"><code>@​ntBre</code></a></li>
<li><a
href="https://github.com/MichaReiser"><code>@​MichaReiser</code></a></li>
<li><a
href="https://github.com/loganrosen"><code>@​loganrosen</code></a></li>
<li><a
href="https://github.com/RafaelJohn9"><code>@​RafaelJohn9</code></a></li>
<li><a
href="https://github.com/adityasingh2400"><code>@​adityasingh2400</code></a></li>
</ul>
<h2>0.15.15</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/astral-sh/ruff/commit/6c498ab5394edc5622d7f348e12956bf86203716"><code>6c498ab</code></a>
Bump 0.15.16 (<a
href="https://redirect.github.com/astral-sh/ruff/issues/25635">#25635</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/e51e132831c4e1c4a5ac00fca4c9256354ab99bf"><code>e51e132</code></a>
[<code>flake8-async</code>] Implement
<code>yield-in-context-manager-in-async-generator</code> (`AS...</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/7c6dcd9f2611999c449143d241c582dedf287964"><code>7c6dcd9</code></a>
[ty] Add caching for pattern match narrowing (<a
href="https://redirect.github.com/astral-sh/ruff/issues/25613">#25613</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/27058fc071b542bf06395ba89cabed061d313ca6"><code>27058fc</code></a>
[ty] Compact retained definition and expression identities (<a
href="https://redirect.github.com/astral-sh/ruff/issues/25606">#25606</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/bf80d05f007c939799f530c9e775ed9449f5b2eb"><code>bf80d05</code></a>
Fix CODEOWNERS syntax (<a
href="https://redirect.github.com/astral-sh/ruff/issues/25622">#25622</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/10ccd511e94a81d1e836b174f1c553a73ff3f1b3"><code>10ccd51</code></a>
Shrink additional parser AST collections (<a
href="https://redirect.github.com/astral-sh/ruff/issues/25465">#25465</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/0d7135f4d23e7f4d8404daed16b9ef11d14f3fb9"><code>0d7135f</code></a>
[ty] Upgrade Salsa (<a
href="https://redirect.github.com/astral-sh/ruff/issues/25545">#25545</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/49493a3cea83a08fa9aa143695017c816a540f1d"><code>49493a3</code></a>
[ty] Show type alias value on hover (<a
href="https://redirect.github.com/astral-sh/ruff/issues/25381">#25381</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/85207d3b7657a84252f266766cb0d56034dc21cc"><code>85207d3</code></a>
[ty] sys.implementation.version is not sys.version_info (<a
href="https://redirect.github.com/astral-sh/ruff/issues/25608">#25608</a>)</li>
<li><a
href="https://github.com/astral-sh/ruff/commit/a8a0614348c1fcf47fc9b666eff61a103914d520"><code>a8a0614</code></a>
[ty] Avoid retaining duplicate function signatures (<a
href="https://redirect.github.com/astral-sh/ruff/issues/25609">#25609</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/astral-sh/ruff/compare/0.15.14...0.15.16">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=ruff&package-manager=uv&previous-version=0.15.14&new-version=0.15.16)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Dave Mihalcik <dmihalcik@virtru.com>
Refactors otdf-local from a single-instance CLI (one platform checkout,
fixed ports, hardcoded six KAS instances) into a multi-instance harness
where each named instance under tests/instances/<name>/ owns its own
opentdf.yaml, keys, KAS configs, and port range.

Why
---

A single bug report often describes a *combination* — platform v0.9.0
with Java SDK 0.7.8 and a KAS at a pre-release. Today a developer has
to hand-edit configs and re-checkout the platform to reproduce. After
this change:

  otdf-local instance init java-078 --from-scenario .../scenario.yaml
  otdf-local --instance java-078 up

brings up exactly the topology the scenario describes, using platform
binaries that otdf-sdk-mgr already provisioned (each instance, and each
KAS within an instance, can reference a different pinned version). Two
instances on disjoint ports.base can coexist on a developer laptop.

What changes
------------

otdf-local now depends on otdf-sdk-mgr via a uv path source so both
tools share the canonical Scenario/Instance schema.

Settings (otdf_local.config.settings):
  - New instance_name (env-overridable via OTDF_LOCAL_INSTANCE_NAME),
    instance_dir, instances_root, instance_yaml properties.
  - platform_dir becomes optional; legacy sibling-discovery only kicks
    in when no per-instance configuration is present.
  - platform_binary_for(dist) resolves to the otdf-sdk-mgr-managed
    xtest/platform/dist/<dist>/service binary.
  - keys_dir, logs_dir, config_dir, platform_config, and
    get_kas_config_path switch to per-instance paths whenever
    instance.yaml exists; legacy behavior is preserved otherwise.
  - load_instance() reads the per-instance manifest via the shared
    Pydantic model.

Ports (otdf_local.config.ports):
  - KAS_OFFSETS exposes the offset table (alpha=+101, beta=+202, ...,
    km2=+606) so multiple instances on different bases get disjoint
    port ranges. The legacy 8080-based constants are preserved as
    defaults.
  - get_kas_port(name, base=...) computes the port relative to base.

Services (otdf_local.services.platform / .kas):
  - PlatformService.start() and KASService.start() use the pinned dist
    binary at xtest/platform/dist/<dist>/service when an instance is
    loaded, with cwd set to the recorded worktree so the binary finds
    its embedded resources. Legacy `go run ./service` path runs
    unchanged when no instance is active.
  - KASService.is_key_management defers to the manifest's `mode` field
    instead of the legacy name-based heuristic; per-KAS features (e.g.
    ec_tdf_enabled) pass through to opentdf.yaml.
  - KASManager constructs only the KAS instances listed in
    instance.yaml's kas: map. start_standard / start_km filter on
    is_key_management so subset topologies still work.

utils.keys.setup_golden_keys:
  - Writes key files into the target directory (per-instance keys_dir
    or legacy platform_dir) and uses absolute paths in the generated
    keys_config so the binary finds them regardless of cwd.

CLI:
  - New top-level --instance option threads through every command via
    OTDF_LOCAL_INSTANCE_NAME.
  - New `instance` subcommand group: init [--from-scenario PATH],
    ls --json, rm.
  - New `scenario` subcommand: `run <path>` translates the scenario's
    suite block into `pytest --sdks-encrypt ... --sdks-decrypt ...
    --containers ...` under xtest/ with OTDF_LOCAL_INSTANCE_NAME set.

Tests (otdf-local/tests/test_multi_instance.py):
  - Port arithmetic at default and alternate bases.
  - Settings round-trip with and without an instance.yaml.
  - platform_binary_for resolves under the otdf-sdk-mgr-managed
    xtest/platform/ tree.

.gitignore additions:
  - tests/instances/ (per-instance config and logs)
  - xtest/scenarios/*.installed.json (provisioning records)
  - .claude/tmp/

Backward compatibility:
  - `otdf-local up` with no --instance flag keeps working against a
    sibling platform/ checkout.

Refs: https://virtru.atlassian.net/browse/DSPX-3302

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Before this change, `otdf-local instance init` only wrote `instance.yaml`
and empty subdirs. Anyone running a fresh instance had to manually copy
keys from another worktree, run `init-temp-keys.sh` by hand, and copy
`opentdf-dev.yaml` into the instance dir before `up` would succeed —
otherwise Keycloak crash-looped on a missing `truststore.jks`, and
pytest failed with `OT_ROOT_KEY environment variable is not set`.

Changes:
- utils/keys.py: add `generate_localhost_cert()` and `generate_ca_jks()`
  to produce the Keycloak TLS pair + JKS truststore (matches the
  platform's `init-temp-keys.sh`). `generate_ca_jks()` runs `keytool`
  inside the `keycloak/keycloak:25.0` image so a local JDK isn't
  required. `ensure_keys_exist()` now generates the full bootstrap
  bundle, idempotently.
- cli_instance.py: `_init_from_scenario` and `_init_minimal` call a new
  `_provision_instance_dir()` helper that runs `ensure_keys_exist()` and
  copies the platform's `opentdf-dev.yaml` (or `opentdf-example.yaml`)
  into the instance dir, overriding `services.kas.root_key` with a
  freshly generated value so every instance owns its own root key.
- services/platform.py: `_generate_config()` preserves an existing
  per-instance `opentdf.yaml`, only patching logger + golden-key fields
  in place, so the init-time `root_key` survives restarts.
- services/docker.py: docker-compose subprocesses are now run with
  `KEYS_DIR=<instance>/keys` so the compose file's `${KEYS_DIR:-./keys}`
  mounts resolve to the per-instance bundle.

Users can now run:

  otdf-local instance init <name> --from-scenario path/to/scenario.yaml
  otdf-local --instance <name> up
  eval $(otdf-local --instance <name> env)
  cd xtest && uv run pytest ...

with no manual key-copying, no editing of `opentdf.yaml`, and no
shell-script fallback. Verified end-to-end against `pure-mlkem.yaml`
(PR opentdf/platform#3537): all 9 services come up healthy on the first
try and `env` exports `OT_ROOT_KEY`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…chema

`_build_pytest_args` read `suite.select` and treated `suite.containers`
as a string, but the Pydantic Suite model exposes `targets: list[str]`
and `containers: list[ContainerKind]`. Any user invoking
`otdf-local scenario run` hit AttributeError. Also wires `suite.kexpr`
through as `-k`; it was silently dropped.

Adds unit tests covering empty/multi targets, container join, kexpr,
markers + extra args, and SDK token forwarding.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…leanup

- `up` command now uses `settings.get_platform_port()` and iterates
  `kas_manager._instances` with `settings.get_kas_port()` for health checks
  so non-default instances with a different `ports.base` work correctly
- Add `Settings.get_platform_port()` alongside the existing `get_kas_port()`
- Simplify metadata name update: `instance.metadata.name = name` (frozen=False)
- Use `shlex.join(cmd)` for display in cli_scenario.py
- Add `"Instance | None"` return type to `load_instance` via TYPE_CHECKING
- Drop unused `Path` import in cli.py, stale `os` import in test file

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Guard platform_dir None-access in env command; replace non-existent
PlatformPin.image attribute with "unknown" fallback in ls command.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- cli_scenario: set OTDF_LOCAL_INSTANCE_NAME + clear settings cache before
  get_settings() so scenario-driven instance name is picked up
- cli_instance: add _validate_instance_name() to guard against path traversal
  in init/rm; add --force flag to init to prevent silent overwrite
- kas: add get_instance_names() public method; replace _instances access in cli
- keys: generate_ca_jks() now imports cert only (keytool -importcert) so ca.jks
  is a proper truststore; ensure_keys_exist() guards include cert files alongside
  private keys to catch partial-init broken state

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reverts the keytool -importcert change from the previous commit.
The PKCS12 + importkeystore approach mirrors init-temp-keys.sh in the
platform repo exactly (lines 65-90); Keycloak requires this form of
ca.jks and the cert-only truststore broke it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Refactors otdf-local from a single-instance CLI (one platform checkout,
fixed ports, hardcoded six KAS instances) into a multi-instance harness
where each named instance under tests/instances/<name>/ owns its own
opentdf.yaml, keys, KAS configs, and port range.

Why
---

A single bug report often describes a *combination* — platform v0.9.0
with Java SDK 0.7.8 and a KAS at a pre-release. Today a developer has
to hand-edit configs and re-checkout the platform to reproduce. After
this change:

  otdf-local instance init java-078 --from-scenario .../scenario.yaml
  otdf-local --instance java-078 up

brings up exactly the topology the scenario describes, using platform
binaries that otdf-sdk-mgr already provisioned (each instance, and each
KAS within an instance, can reference a different pinned version). Two
instances on disjoint ports.base can coexist on a developer laptop.

What changes
------------

otdf-local now depends on otdf-sdk-mgr via a uv path source so both
tools share the canonical Scenario/Instance schema.

Settings (otdf_local.config.settings):
  - New instance_name (env-overridable via OTDF_LOCAL_INSTANCE_NAME),
    instance_dir, instances_root, instance_yaml properties.
  - platform_dir becomes optional; legacy sibling-discovery only kicks
    in when no per-instance configuration is present.
  - platform_binary_for(dist) resolves to the otdf-sdk-mgr-managed
    xtest/platform/dist/<dist>/service binary.
  - keys_dir, logs_dir, config_dir, platform_config, and
    get_kas_config_path switch to per-instance paths whenever
    instance.yaml exists; legacy behavior is preserved otherwise.
  - load_instance() reads the per-instance manifest via the shared
    Pydantic model.

Ports (otdf_local.config.ports):
  - KAS_OFFSETS exposes the offset table (alpha=+101, beta=+202, ...,
    km2=+606) so multiple instances on different bases get disjoint
    port ranges. The legacy 8080-based constants are preserved as
    defaults.
  - get_kas_port(name, base=...) computes the port relative to base.

Services (otdf_local.services.platform / .kas):
  - PlatformService.start() and KASService.start() use the pinned dist
    binary at xtest/platform/dist/<dist>/service when an instance is
    loaded, with cwd set to the recorded worktree so the binary finds
    its embedded resources. Legacy `go run ./service` path runs
    unchanged when no instance is active.
  - KASService.is_key_management defers to the manifest's `mode` field
    instead of the legacy name-based heuristic; per-KAS features (e.g.
    ec_tdf_enabled) pass through to opentdf.yaml.
  - KASManager constructs only the KAS instances listed in
    instance.yaml's kas: map. start_standard / start_km filter on
    is_key_management so subset topologies still work.

utils.keys.setup_golden_keys:
  - Writes key files into the target directory (per-instance keys_dir
    or legacy platform_dir) and uses absolute paths in the generated
    keys_config so the binary finds them regardless of cwd.

CLI:
  - New top-level --instance option threads through every command via
    OTDF_LOCAL_INSTANCE_NAME.
  - New `instance` subcommand group: init [--from-scenario PATH],
    ls --json, rm.
  - New `scenario` subcommand: `run <path>` translates the scenario's
    suite block into `pytest --sdks-encrypt ... --sdks-decrypt ...
    --containers ...` under xtest/ with OTDF_LOCAL_INSTANCE_NAME set.

Tests (otdf-local/tests/test_multi_instance.py):
  - Port arithmetic at default and alternate bases.
  - Settings round-trip with and without an instance.yaml.
  - platform_binary_for resolves under the otdf-sdk-mgr-managed
    xtest/platform/ tree.

.gitignore additions:
  - tests/instances/ (per-instance config and logs)
  - xtest/scenarios/*.installed.json (provisioning records)
  - .claude/tmp/

Backward compatibility:
  - `otdf-local up` with no --instance flag keeps working against a
    sibling platform/ checkout.

Refs: https://virtru.atlassian.net/browse/DSPX-3302

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds two new pytest CLI options so xtest can be driven by a scenarios.yaml
and run against a specific otdf-local instance.

  --scenario PATH    When set, defaults --sdks-encrypt, --sdks-decrypt,
                     and --containers from the scenario's `sdks` and
                     `suite` blocks. Options explicitly passed on the
                     CLI always override.
  --instance NAME    Propagated to OTDF_LOCAL_INSTANCE_NAME so child
                     `otdf-local` invocations within the test see the
                     same instance the scenario expects.

If otdf-sdk-mgr is not installed (minimal pytest environments), the
--scenario flag silently no-ops via an ImportError guard. The flag
shape is invariant either way so CI configs don't fork.

This is the consumer side of the PR 3 / scenario-driven flow: the
authoritative entry point remains `otdf-local scenario run <path>`,
which sets these flags for you; this PR lets pytest accept them
directly when running scenario-aware sessions outside the wrapper.

Refs: https://virtru.atlassian.net/browse/DSPX-3302

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dmihalcik-virtru dmihalcik-virtru force-pushed the DSPX-3302-04-xtest-conftest branch from 837416f to 8a22603 Compare June 12, 2026 21:26
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant