diff --git a/.github/workflows/python-quality.yml b/.github/workflows/python-quality.yml index 61e9cce7..df177a7b 100644 --- a/.github/workflows/python-quality.yml +++ b/.github/workflows/python-quality.yml @@ -19,6 +19,7 @@ jobs: skill_authoring: ${{ steps.scopes.outputs.skill_authoring }} observability_langfuse: ${{ steps.scopes.outputs.observability_langfuse }} discovery: ${{ steps.scopes.outputs.discovery }} + verification: ${{ steps.scopes.outputs.verification }} steps: - uses: actions/checkout@v4 with: @@ -120,6 +121,30 @@ jobs: enable-cache: true - run: scripts/check-python --package plugins/capability/darrow-discovery/skills/plan-implementation/backend + verification-windows: + name: Verification Python ${{ matrix.python-version }} on windows-latest + needs: changes + if: needs.changes.outputs.verification == 'true' + strategy: + fail-fast: false + matrix: + python-version: ["3.10", "3.11", "3.12", "3.13"] + runs-on: windows-latest + defaults: + run: + shell: bash + env: + UV_PYTHON: ${{ matrix.python-version }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + - run: scripts/check-python --package plugins/capability/darrow-verification/skills/verify-change/backend + inventory: name: Python inventory guard runs-on: ubuntu-latest @@ -201,6 +226,36 @@ jobs: shell: pwsh run: "& '${{ matrix.test }}'" + verification-fresh-install: + name: Fresh install verification on ${{ matrix.os }} + needs: changes + if: needs.changes.outputs.verification == 'true' + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + shell: bash + test: plugins/capability/darrow-verification/tests/fresh-install.test.sh + - os: macos-latest + shell: bash + test: plugins/capability/darrow-verification/tests/fresh-install.test.sh + - os: windows-latest + shell: pwsh + test: plugins/capability/darrow-verification/tests/fresh-install.test.ps1 + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.13" + - uses: astral-sh/setup-uv@v6 + - if: matrix.shell == 'bash' + run: bash "${{ matrix.test }}" + - if: matrix.shell == 'pwsh' + shell: pwsh + run: "& '${{ matrix.test }}'" + performance: name: Python performance invariants needs: changes @@ -222,10 +277,12 @@ jobs: - package - skill-authoring-windows - discovery-windows + - verification-windows - inventory - skill-authoring-fresh-install - observability-fresh-install - discovery-fresh-install + - verification-fresh-install - performance runs-on: ubuntu-latest steps: @@ -238,12 +295,15 @@ jobs: SKILL_AUTHORING_CHANGED: ${{ needs.changes.outputs.skill_authoring }} OBSERVABILITY_CHANGED: ${{ needs.changes.outputs.observability_langfuse }} DISCOVERY_CHANGED: ${{ needs.changes.outputs.discovery }} + VERIFICATION_CHANGED: ${{ needs.changes.outputs.verification }} WINDOWS_RESULT: ${{ needs.skill-authoring-windows.result }} DISCOVERY_WINDOWS_RESULT: ${{ needs.discovery-windows.result }} + VERIFICATION_WINDOWS_RESULT: ${{ needs.verification-windows.result }} INVENTORY_RESULT: ${{ needs.inventory.result }} SKILL_INSTALL_RESULT: ${{ needs.skill-authoring-fresh-install.result }} OBSERVABILITY_INSTALL_RESULT: ${{ needs.observability-fresh-install.result }} DISCOVERY_INSTALL_RESULT: ${{ needs.discovery-fresh-install.result }} + VERIFICATION_INSTALL_RESULT: ${{ needs.verification-fresh-install.result }} PERFORMANCE_RESULT: ${{ needs.performance.result }} run: | scripts/verify-python-quality-results \ @@ -253,4 +313,6 @@ jobs: "$OBSERVABILITY_CHANGED" "$OBSERVABILITY_INSTALL_RESULT" \ "$PERFORMANCE_RESULT" \ "$DISCOVERY_CHANGED" "$DISCOVERY_WINDOWS_RESULT" \ - "$DISCOVERY_INSTALL_RESULT" + "$DISCOVERY_INSTALL_RESULT" \ + "$VERIFICATION_CHANGED" "$VERIFICATION_WINDOWS_RESULT" \ + "$VERIFICATION_INSTALL_RESULT" diff --git a/docs/specs/verification.md b/docs/specs/verification.md index 2fa6057a..80bd4e6f 100644 --- a/docs/specs/verification.md +++ b/docs/specs/verification.md @@ -85,6 +85,15 @@ delivery under #155. It does not execute QA or create reviewer-facing evidence p any next-action field in a no-progress result carries the stop reason or needed investigation, never an implementation instruction or another repair followed by reassessment. +- **VF-C8 — Portable assessment renderer.** The retained-report renderer MUST + be a skill-contained, UV-locked Python package with no runtime dependencies. + Its public command MUST preserve the accepted argument order, assessment + bytes, report-link escaping and placement, validation diagnostics, + stdout/stderr separation, and exit statuses on Linux, macOS, and native + Windows. Claude Code and Codex MUST resolve the backend from the installed + skill directory and invoke the same frozen package entrypoint directly. + Fresh copied-artifact checks MUST exercise that installed layout without + repository-relative or sibling-plugin references. ## Consumer handoff diff --git a/plugins/capability/darrow-verification/.claude-plugin/plugin.json b/plugins/capability/darrow-verification/.claude-plugin/plugin.json index ce89e97b..36694558 100644 --- a/plugins/capability/darrow-verification/.claude-plugin/plugin.json +++ b/plugins/capability/darrow-verification/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "darrow-verification", - "version": "0.1.1", + "version": "0.2.0", "description": "Bounded acceptance verification through replaceable independent code review", "license": "BUSL-1.1", "author": { "name": "Björn Rochel", "email": "bjoern@bjro.de" } diff --git a/plugins/capability/darrow-verification/.codex-plugin/plugin.json b/plugins/capability/darrow-verification/.codex-plugin/plugin.json index 3f284c90..273e2d9a 100644 --- a/plugins/capability/darrow-verification/.codex-plugin/plugin.json +++ b/plugins/capability/darrow-verification/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "darrow-verification", - "version": "0.1.1", + "version": "0.2.0", "description": "Bounded acceptance verification through replaceable independent code review", "author": { "name": "Björn Rochel", "email": "bjoern@bjro.de" }, "skills": "./skills/", diff --git a/plugins/capability/darrow-verification/README.md b/plugins/capability/darrow-verification/README.md index a8ed1291..16ccb1b8 100644 --- a/plugins/capability/darrow-verification/README.md +++ b/plugins/capability/darrow-verification/README.md @@ -36,7 +36,8 @@ The plugin is self-contained and supports Claude Code and Codex. A compatible review provider needs native fresh-agent support and its own check tools. The host must allow a bounded provider assessment context and that provider's independent readers; unavailable depth or authority blocks the operation. -The small report renderer uses Bash 3.2 or Bash 5 and baseline Unix utilities. +The report renderer requires UV and a UV-managed Python 3.10–3.13 runtime. It is +installed from this plugin's locked, dependency-free Python package. Discover the review provider by host-advertised intent, then check prerequisites, authorized effects, result evidence and stop conditions. The provider must independently @@ -83,9 +84,11 @@ A compatible review capability must be separately available when invoked. ## Troubleshooting Missing review, incompatible effects, stale target identity, unreadable original -findings or incomplete required evidence blocks assessment. Supply the exact -missing input or compatible provider, preserving prior evidence for follow-up. -Do not replace the blocked result with self-review or a new repair loop. +findings, incomplete required evidence, a missing backend or lock, UV, or a +supported Python runtime blocks assessment. Supply the exact missing input or +compatible provider, preserving prior evidence for follow-up. Do not replace +the blocked result with self-review, another checkout's renderer, or a new +repair loop. ## License diff --git a/plugins/capability/darrow-verification/skills/verify-change/SKILL.md b/plugins/capability/darrow-verification/skills/verify-change/SKILL.md index 065e7b1e..535095d7 100644 --- a/plugins/capability/darrow-verification/skills/verify-change/SKILL.md +++ b/plugins/capability/darrow-verification/skills/verify-change/SKILL.md @@ -243,25 +243,49 @@ or a lifecycle ledger. ## 5. Render a retained-report handoff -When the selected provider returned a retained local report, use the bundled -[assessment renderer](scripts/render-assessment) for the final handoff. It owns +When the selected provider returned a retained local report, use the packaged +[assessment renderer](backend/src/darrow_verification/assessment.py) for the final handoff. It owns absolute-reference validation and rendering; do not reproduce its output by hand. It does not interpret the provider's format or decide findings. First finish the semantic assessment above. Obtain a unique temporary draft -file with `mktemp` outside the product scope and write that assessment using -the host's file-writing tool. This temporary draft is assessment output, not -an implementation edit or a required evidence package. Then run: +file outside the product scope with the host's native temporary-file facility +(`mktemp` in a POSIX shell or `[System.IO.Path]::GetTempFileName()` in +PowerShell), then write that assessment using the host's file-writing tool. +This temporary draft is assessment output, not an implementation edit or a +required evidence package. Resolve the renderer backend from the loaded skill: -```sh -bash /scripts/render-assessment --assessment --provider-report +- Claude Code: resolve `backend` from the absolute skill directory supplied in + `CLAUDE_SKILL_DIR`; use the host shell's environment-variable and path syntax. +- Codex: take the absolute `SKILL.md` path supplied in the selected skill's + catalog entry and resolve `backend` relative to that file's directory. + +Then run: + +```text +uv run --quiet --isolated --frozen --no-dev --project "" darrow-render-assessment --assessment "" --provider-report "" ``` -Substitute the actual paths; resolve the script from this installed skill, -never another plugin. On success, copy the command's complete stdout unchanged -as the final response. Do not shorten, rewrite or append to it: the renderer's -absolute provider reference is part of the result. Complete semantic checks -before this final command so no later tool or commentary displaces its output. +Substitute the actual paths and invoke this frozen entrypoint directly; do not +look for or create a host-specific launcher. Resolve the backend from this +installed skill, never the user's repository or another plugin. On success, +copy the command's complete stdout unchanged as the final response. Do not +shorten, rewrite or append to it: the renderer's absolute provider reference is +part of the result. Complete semantic checks before this final command so no +later tool or commentary displaces its output. + +The final nonempty line must remain the renderer's angle-delimited Markdown +link: the literal prefix `Complete provider result: [report]` immediately +followed by `()`. A rewritten destination without the surrounding +angle delimiters is not the renderer's exact stdout and does not complete this +handoff. + +That exact link is necessary but not sufficient. Before invocation, read the +temporary draft back and confirm it contains the complete step 4 assessment, +including the criterion-by-criterion evidence and conclusion. After invocation, +return the assessment prefix and final link together as the renderer emitted +them. A response containing only the link, only a conclusion, or a summary of +the draft is an incomplete handoff. A renderer refusal is a blocked handoff. Return the precise missing/unreadable evidence gap rather than a partial rendering or a passing summary. Do not diff --git a/plugins/capability/darrow-verification/skills/verify-change/backend/pyproject.toml b/plugins/capability/darrow-verification/skills/verify-change/backend/pyproject.toml new file mode 100644 index 00000000..1af073a6 --- /dev/null +++ b/plugins/capability/darrow-verification/skills/verify-change/backend/pyproject.toml @@ -0,0 +1,57 @@ +[project] +name = "darrow-verification" +version = "0.2.0" +description = "Deterministic helpers for Darrow verification" +requires-python = ">=3.10,<3.14" +dependencies = [] + +[project.scripts] +darrow-render-assessment = "darrow_verification.assessment:entrypoint" + +[dependency-groups] +dev = [ + "coverage[toml]>=7.10,<8", + "hypothesis>=6.138,<7", + "mypy>=1.18,<2", + "pytest>=8.4,<9", + "ruff>=0.12,<1", +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.uv] +default-groups = [] + +[tool.hatch.build.targets.wheel] +packages = ["src/darrow_verification"] + +[tool.ruff] +target-version = "py310" +line-length = 88 +src = ["src", "tests"] + +[tool.ruff.lint] +select = ["B", "C4", "C90", "E4", "E7", "E9", "F", "I", "N", "RUF", "SIM", "UP"] +mccabe.max-complexity = 5 + +[tool.ruff.format] +docstring-code-format = true + +[tool.mypy] +python_version = "3.10" +strict = true +files = ["src", "tests"] + +[tool.pytest.ini_options] +addopts = ["--strict-config", "--strict-markers"] +testpaths = ["tests"] + +[tool.coverage.run] +branch = true +source = ["darrow_verification"] + +[tool.coverage.report] +show_missing = true +skip_covered = false diff --git a/plugins/capability/darrow-verification/skills/verify-change/backend/src/darrow_verification/__init__.py b/plugins/capability/darrow-verification/skills/verify-change/backend/src/darrow_verification/__init__.py new file mode 100644 index 00000000..09e3c9c3 --- /dev/null +++ b/plugins/capability/darrow-verification/skills/verify-change/backend/src/darrow_verification/__init__.py @@ -0,0 +1 @@ +"""Deterministic helpers for Darrow verification.""" diff --git a/plugins/capability/darrow-verification/skills/verify-change/backend/src/darrow_verification/assessment.py b/plugins/capability/darrow-verification/skills/verify-change/backend/src/darrow_verification/assessment.py new file mode 100644 index 00000000..16b44735 --- /dev/null +++ b/plugins/capability/darrow-verification/skills/verify-change/backend/src/darrow_verification/assessment.py @@ -0,0 +1,146 @@ +"""Validate and render one retained verification assessment handoff.""" + +from __future__ import annotations + +import os +import sys +from collections.abc import Sequence +from dataclasses import dataclass +from io import TextIOWrapper +from pathlib import Path +from typing import Protocol, cast + +USAGE = ( + "usage: render-assessment --assessment ABSOLUTE_FILE " + "--provider-report ABSOLUTE_FILE" +) +ERROR_PREFIX = "render-assessment" + + +class BinaryWriter(Protocol): + """Minimal byte stream used by the renderer boundary.""" + + def write(self, data: bytes, /) -> object: + """Write bytes to the stream.""" + + +class UsageError(Exception): + """Signal malformed command syntax.""" + + +class InputError(Exception): + """Signal an invalid assessment or provider-report input.""" + + +@dataclass(frozen=True) +class AssessmentInputs: + """Canonical files used to render one assessment handoff.""" + + assessment: Path + provider_report: Path + + +def parse(arguments: Sequence[str]) -> tuple[str, str]: + """Parse the renderer's stable ordered flag contract.""" + if ( + len(arguments) != 4 + or arguments[0] != "--assessment" + or arguments[2] != "--provider-report" + ): + raise UsageError + return arguments[1], arguments[3] + + +def _contains_control_character(value: str) -> bool: + return any(ord(character) < 32 or ord(character) == 127 for character in value) + + +def _is_readable_nonempty_file(path: Path) -> bool: + try: + if not path.is_file(): + return False + with path.open("rb") as stream: + return bool(stream.read(1)) + except OSError: + return False + + +def canonical_input(value: str, label: str) -> Path: + """Validate one path and resolve its parent without resolving its filename.""" + path = Path(value) + if not path.is_absolute(): + raise InputError(f"{label} requires an absolute path") + if _contains_control_character(value): + raise InputError(f"{label} path contains a control character") + if not _is_readable_nonempty_file(path): + raise InputError(f"{label} is not a readable nonempty regular file: {value}") + try: + parent = path.parent.resolve(strict=True) + except OSError as error: + raise InputError(f"cannot resolve {label} parent: {value}") from error + return parent / path.name + + +def escape_destination(value: str) -> str: + """Escape the Markdown URI delimiters escaped by the original renderer.""" + destination = value.replace("%", "%25") + destination = destination.replace(" ", "%20") + destination = destination.replace("#", "%23") + destination = destination.replace("?", "%3F") + destination = destination.replace("<", "%3C").replace(">", "%3E") + return destination.replace("\\", "%5C") + + +def validate(arguments: Sequence[str]) -> AssessmentInputs: + """Parse and validate both inputs in their public error order.""" + assessment, provider_report = parse(arguments) + return AssessmentInputs( + assessment=canonical_input(assessment, "assessment"), + provider_report=canonical_input(provider_report, "provider-report"), + ) + + +def render(inputs: AssessmentInputs) -> bytes: + """Preserve the assessment bytes and append the canonical report link.""" + destination = escape_destination(os.fspath(inputs.provider_report)) + try: + assessment = inputs.assessment.read_bytes() + except OSError as error: + raise InputError( + f"assessment is not a readable nonempty regular file: {inputs.assessment}" + ) from error + return assessment + ( + b"\n\nComplete provider result: [report](<" + os.fsencode(destination) + b">)\n" + ) + + +def _write_error(stream: BinaryWriter, message: str) -> None: + stream.write(os.fsencode(f"{ERROR_PREFIX}: {message}\n")) + + +def main( + arguments: Sequence[str], *, stdout: BinaryWriter, stderr: BinaryWriter +) -> int: + """Run the renderer and return its public process status.""" + try: + inputs = validate(arguments) + except UsageError: + _write_error(stderr, USAGE) + return 2 + except InputError as error: + _write_error(stderr, str(error)) + return 2 + try: + output = render(inputs) + except InputError as error: + _write_error(stderr, str(error)) + return 2 + stdout.write(output) + return 0 + + +def entrypoint() -> None: + """Run the installed console entrypoint with byte-stable streams.""" + stdout = cast(TextIOWrapper, sys.stdout).buffer + stderr = cast(TextIOWrapper, sys.stderr).buffer + raise SystemExit(main(tuple(sys.argv[1:]), stdout=stdout, stderr=stderr)) diff --git a/plugins/capability/darrow-verification/skills/verify-change/backend/src/darrow_verification/py.typed b/plugins/capability/darrow-verification/skills/verify-change/backend/src/darrow_verification/py.typed new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/plugins/capability/darrow-verification/skills/verify-change/backend/src/darrow_verification/py.typed @@ -0,0 +1 @@ + diff --git a/plugins/capability/darrow-verification/skills/verify-change/backend/tests/test_assessment.py b/plugins/capability/darrow-verification/skills/verify-change/backend/tests/test_assessment.py new file mode 100644 index 00000000..2b7a7501 --- /dev/null +++ b/plugins/capability/darrow-verification/skills/verify-change/backend/tests/test_assessment.py @@ -0,0 +1,319 @@ +import os +import sys +from collections.abc import Sequence +from io import BytesIO, TextIOWrapper +from pathlib import Path + +import pytest + +from darrow_verification import assessment as renderer +from darrow_verification.assessment import main + + +def run(arguments: Sequence[str]) -> tuple[int, bytes, bytes]: + stdout = BytesIO() + stderr = BytesIO() + status = main(arguments, stdout=stdout, stderr=stderr) + return status, stdout.getvalue(), stderr.getvalue() + + +def valid_arguments(assessment: Path, report: Path) -> tuple[str, ...]: + return ( + "--assessment", + str(assessment), + "--provider-report", + str(report), + ) + + +def destination_bytes(path: Path) -> bytes: + return os.fsencode(renderer.escape_destination(str(path))) + + +def test_renders_multiline_assessment_and_exact_report_link(tmp_path: Path) -> None: + assessment = tmp_path / "assessment.md" + report = tmp_path / "report.md" + assessment.write_bytes( + b"Conclusion: progress.\n\nF1 remains blocking; A1 is advisory.\n" + ) + report.write_bytes(b"Complete provider report, unchanged.\n") + + status, stdout, stderr = run(valid_arguments(assessment, report)) + + assert status == 0 + assert stderr == b"" + assert stdout == ( + assessment.read_bytes() + + b"\n\nComplete provider result: [report](<" + + destination_bytes(report) + + b">)\n" + ) + + +def test_preserves_assessment_without_a_terminal_newline(tmp_path: Path) -> None: + assessment = tmp_path / "assessment.md" + report = tmp_path / "report.md" + assessment.write_bytes(b"Conclusion: clear.") + report.write_bytes(b"Provider result.\n") + + status, stdout, stderr = run(valid_arguments(assessment, report)) + + assert status == 0 + assert stderr == b"" + assert stdout == ( + b"Conclusion: clear.\n\nComplete provider result: [report](<" + + destination_bytes(report) + + b">)\n" + ) + + +def test_resolves_a_symlinked_parent_without_resolving_the_file_name( + tmp_path: Path, +) -> None: + actual = tmp_path / "actual" + actual.mkdir() + alias = tmp_path / "alias" + try: + alias.symlink_to(actual, target_is_directory=True) + except OSError: + pytest.skip("directory symlinks are unavailable") + assessment = actual / "assessment.md" + report = actual / "report.md" + assessment.write_bytes(b"Assessment.\n") + report.write_bytes(b"Report.\n") + + status, stdout, stderr = run(valid_arguments(assessment, alias / report.name)) + + assert status == 0 + assert stderr == b"" + assert stdout.endswith( + b"Complete provider result: [report](<" + destination_bytes(report) + b">)\n" + ) + + +def test_escapes_only_markdown_uri_delimiters_in_order(tmp_path: Path) -> None: + assessment = tmp_path / "assessment.md" + report = tmp_path / "report [one](two) #%.md" + assessment.write_bytes(b"Assessment.\n") + report.write_bytes(b"Report.\n") + + status, stdout, stderr = run(valid_arguments(assessment, report)) + + assert status == 0 + assert stderr == b"" + escaped = str(report).replace("%", "%25").replace(" ", "%20") + escaped = escaped.replace("#", "%23").replace("<", "%3C").replace(">", "%3E") + escaped = escaped.replace("\\", "%5C") + assert stdout.endswith( + f"Complete provider result: [report](<{escaped}>)\n".encode() + ) + + +@pytest.mark.parametrize( + "arguments", + [ + (), + ("--assessment",), + ("--assessment", "a", "--provider-report"), + ("--unknown", "a", "--provider-report", "b"), + ("--provider-report", "b", "--assessment", "a"), + ("--assessment=a", "--provider-report=b"), + ("--assessment", "a", "--provider-report", "b", "extra"), + ], +) +def test_refuses_malformed_arguments(arguments: Sequence[str]) -> None: + status, stdout, stderr = run(arguments) + + assert status == 2 + assert stdout == b"" + assert stderr == ( + b"render-assessment: usage: render-assessment --assessment ABSOLUTE_FILE " + b"--provider-report ABSOLUTE_FILE\n" + ) + + +def test_validates_assessment_before_provider_report(tmp_path: Path) -> None: + status, stdout, stderr = run( + ("--assessment", "relative.md", "--provider-report", "also-relative.md") + ) + + assert status == 2 + assert stdout == b"" + assert stderr == b"render-assessment: assessment requires an absolute path\n" + + assessment = tmp_path / "assessment.md" + assessment.write_bytes(b"Assessment.\n") + status, stdout, stderr = run( + valid_arguments(assessment, Path("relative-report.md")) + ) + + assert status == 2 + assert stdout == b"" + assert stderr == b"render-assessment: provider-report requires an absolute path\n" + + +@pytest.mark.parametrize("label", ["assessment", "provider-report"]) +@pytest.mark.parametrize("kind", ["missing", "empty", "directory"]) +def test_refuses_unavailable_inputs(tmp_path: Path, label: str, kind: str) -> None: + assessment = tmp_path / "assessment.md" + report = tmp_path / "report.md" + assessment.write_bytes(b"Assessment.\n") + report.write_bytes(b"Report.\n") + invalid = tmp_path / kind + if kind == "empty": + invalid.write_bytes(b"") + elif kind == "directory": + invalid.mkdir() + arguments = list(valid_arguments(assessment, report)) + arguments[1 if label == "assessment" else 3] = str(invalid) + + status, stdout, stderr = run(arguments) + + assert status == 2 + assert stdout == b"" + assert stderr == ( + f"render-assessment: {label} is not a readable nonempty regular file: " + f"{invalid}\n".encode() + ) + + +@pytest.mark.parametrize("label", ["assessment", "provider-report"]) +@pytest.mark.parametrize("control", ["\n", "\t", "\r", "\x7f"]) +def test_refuses_control_characters_in_paths( + tmp_path: Path, label: str, control: str +) -> None: + assessment = tmp_path / "assessment.md" + report = tmp_path / "report.md" + assessment.write_bytes(b"Assessment.\n") + report.write_bytes(b"Report.\n") + arguments = list(valid_arguments(assessment, report)) + arguments[1 if label == "assessment" else 3] = str(tmp_path / f"bad{control}path") + + status, stdout, stderr = run(arguments) + + assert status == 2 + assert stdout == b"" + assert ( + stderr + == f"render-assessment: {label} path contains a control character\n".encode() + ) + + +def test_treats_a_filesystem_probe_error_as_an_unavailable_input( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + assessment = tmp_path / "assessment.md" + report = tmp_path / "report.md" + assessment.write_bytes(b"Assessment.\n") + report.write_bytes(b"Report.\n") + + def refuse_probe(_path: Path) -> bool: + raise OSError("probe failed") + + monkeypatch.setattr(Path, "is_file", refuse_probe) + status, stdout, stderr = run(valid_arguments(assessment, report)) + + assert status == 2 + assert stdout == b"" + assert stderr == ( + b"render-assessment: assessment is not a readable nonempty regular file: " + + str(assessment).encode() + + b"\n" + ) + + +def test_treats_an_open_error_as_an_unavailable_input( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + assessment = tmp_path / "assessment.md" + report = tmp_path / "report.md" + assessment.write_bytes(b"Assessment.\n") + report.write_bytes(b"Report.\n") + + def refuse_open(_path: Path, _mode: str) -> object: + raise OSError("open failed") + + monkeypatch.setattr(Path, "open", refuse_open) + status, stdout, stderr = run(valid_arguments(assessment, report)) + + assert status == 2 + assert stdout == b"" + assert stderr == ( + b"render-assessment: assessment is not a readable nonempty regular file: " + + str(assessment).encode() + + b"\n" + ) + + +def test_translates_an_assessment_read_race_to_the_stable_refusal( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + assessment = tmp_path / "assessment.md" + report = tmp_path / "report.md" + assessment.write_bytes(b"Assessment.\n") + report.write_bytes(b"Report.\n") + + def refuse_read(_path: Path) -> bytes: + raise OSError("read failed") + + monkeypatch.setattr(Path, "read_bytes", refuse_read) + status, stdout, stderr = run(valid_arguments(assessment, report)) + + assert status == 2 + assert stdout == b"" + assert stderr == ( + b"render-assessment: assessment is not a readable nonempty regular file: " + + str(assessment).encode() + + b"\n" + ) + + +def test_reports_an_unresolvable_input_parent( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + assessment = tmp_path / "assessment.md" + report = tmp_path / "report.md" + + monkeypatch.setattr(renderer, "_is_readable_nonempty_file", lambda _path: True) + + def refuse_resolution(_path: Path, *, strict: bool = False) -> Path: + del strict + raise OSError("resolution failed") + + monkeypatch.setattr(Path, "resolve", refuse_resolution) + status, stdout, stderr = run(valid_arguments(assessment, report)) + + assert status == 2 + assert stdout == b"" + assert stderr == ( + b"render-assessment: cannot resolve assessment parent: " + + str(assessment).encode() + + b"\n" + ) + + +def test_console_entrypoint_uses_binary_standard_streams( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + assessment = tmp_path / "assessment.md" + report = tmp_path / "report.md" + assessment.write_bytes(b"Assessment.\n") + report.write_bytes(b"Report.\n") + stdout_bytes = BytesIO() + stderr_bytes = BytesIO() + stdout = TextIOWrapper(stdout_bytes, encoding="utf-8") + stderr = TextIOWrapper(stderr_bytes, encoding="utf-8") + monkeypatch.setattr( + sys, + "argv", + ["darrow-render-assessment", *valid_arguments(assessment, report)], + ) + monkeypatch.setattr(sys, "stdout", stdout) + monkeypatch.setattr(sys, "stderr", stderr) + + with pytest.raises(SystemExit) as raised: + renderer.entrypoint() + + assert raised.value.code == 0 + assert stderr_bytes.getvalue() == b"" + assert stdout_bytes.getvalue().startswith(b"Assessment.\n") diff --git a/plugins/capability/darrow-verification/skills/verify-change/backend/tests/test_properties.py b/plugins/capability/darrow-verification/skills/verify-change/backend/tests/test_properties.py new file mode 100644 index 00000000..479592cc --- /dev/null +++ b/plugins/capability/darrow-verification/skills/verify-change/backend/tests/test_properties.py @@ -0,0 +1,65 @@ +import os +import string +import tempfile +from io import BytesIO +from pathlib import Path + +from hypothesis import given +from hypothesis import strategies as st + +from darrow_verification.assessment import escape_destination, main + + +@given( + value=st.text( + alphabet=string.ascii_letters + string.digits + " /#%?<>\\[]()_-.", + min_size=1, + max_size=80, + ) +) +def test_destination_escaping_preserves_order_and_safe_characters(value: str) -> None: + escapes = { + "%": "%25", + " ": "%20", + "#": "%23", + "?": "%3F", + "<": "%3C", + ">": "%3E", + "\\": "%5C", + } + expected = "".join(escapes.get(character, character) for character in value) + + assert escape_destination(value) == expected + + +@given(body=st.binary(min_size=1, max_size=512)) +def test_rendering_preserves_every_assessment_byte(body: bytes) -> None: + with tempfile.TemporaryDirectory() as directory: + assessment = Path(directory) / "assessment.md" + report = Path(directory) / "report.md" + assessment.write_bytes(body) + report.write_bytes(b"Provider result.\n") + stdout = BytesIO() + stderr = BytesIO() + + status = main( + ( + "--assessment", + str(assessment), + "--provider-report", + str(report), + ), + stdout=stdout, + stderr=stderr, + ) + + assert status == 0 + assert stderr.getvalue() == b"" + assert stdout.getvalue() == ( + body + + b"\n\nComplete provider result: [report](<" + + os.fsencode( + escape_destination(os.fspath(report.parent.resolve() / report.name)) + ) + + b">)\n" + ) diff --git a/plugins/capability/darrow-verification/skills/verify-change/backend/uv.lock b/plugins/capability/darrow-verification/skills/verify-change/backend/uv.lock new file mode 100644 index 00000000..4f4fd133 --- /dev/null +++ b/plugins/capability/darrow-verification/skills/verify-change/backend/uv.lock @@ -0,0 +1,462 @@ +version = 1 +revision = 3 +requires-python = ">=3.10, <3.14" + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "coverage" +version = "7.16.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/65/2d/c738872f477f5687152acae68635790387425d407ae37dd3d3a8a6692307/coverage-7.16.1.tar.gz", hash = "sha256:f83981779bcf9dfa06fa0a8d4cb43e0faec1706328ce07aa3e7b665b4ac0f210", size = 969651, upload-time = "2026-09-13T19:12:21.422Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/62/a8b4dd53308a4b1571e6bdd77ee562c9b1c1742db9886c0aef2f69463067/coverage-7.16.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4f12a9e27ca7b65e40a8475d27899b2d45064d9020e6a89148939e01987b5853", size = 223178, upload-time = "2026-09-13T19:08:21.805Z" }, + { url = "https://files.pythonhosted.org/packages/5e/7a/b9325b8d486a5c05fd4a9eb1cd9c9f697c361ddeda0e73567d5c2d8959f6/coverage-7.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f590d46c30d9e4c1fda3efefe5443f4c2f6a4192c5ca2ba403653e9ebadf097", size = 223701, upload-time = "2026-09-13T19:08:23.391Z" }, + { url = "https://files.pythonhosted.org/packages/ef/ee/3ab3ac6e9e7165a623695b73a83d1bfbdf5069681a68a012cb5abb096060/coverage-7.16.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3df82f0a3cef4e1bcfc799436056f0b978dda319d0bfd4460c6e479b2802d98a", size = 250434, upload-time = "2026-09-13T19:08:24.948Z" }, + { url = "https://files.pythonhosted.org/packages/b6/a9/31ffc231e9879876f611cf1ac7622cf092d76762c07ca64dce47b29a200b/coverage-7.16.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb462d59146656e278d1e8ed913ce374d0ba68a4e081acde1867f6d3377fc881", size = 252264, upload-time = "2026-09-13T19:08:26.734Z" }, + { url = "https://files.pythonhosted.org/packages/c9/f4/4fb4f6b16de07dbae3d3a111ae28d32ad657ce6ff4d364c49c10962daad5/coverage-7.16.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d7db888dd0a1df1a653cae7f99d4047430d2187a3626eda51bc847b0fd6b9b43", size = 254127, upload-time = "2026-09-13T19:08:28.254Z" }, + { url = "https://files.pythonhosted.org/packages/9b/3d/048e88eb610f44b7542dac10fa45bb2ffad5fbfcdab0f03289a843da934b/coverage-7.16.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:38a7e16f061504ac2b45370bf5bf97e8250d8d3f25e37385bae884978166554b", size = 256043, upload-time = "2026-09-13T19:08:30.019Z" }, + { url = "https://files.pythonhosted.org/packages/97/59/d27b113c80b04c7fc64c69457132648e1d287b83a1de41189a504b50b52b/coverage-7.16.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2b26b55b18e1a53e1a159dd743728c4ddbbef28ba19000a91aaff5ce023197ec", size = 251092, upload-time = "2026-09-13T19:08:31.57Z" }, + { url = "https://files.pythonhosted.org/packages/45/42/b7eafaceff08dc1dcbfc924fa9233031fd00c8b8287cc4a1a0dcdedcb0f9/coverage-7.16.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fdb2f528b50953e29d22033b3256c396a700193c6e45b2490222ef9c333cbbf9", size = 252170, upload-time = "2026-09-13T19:08:33.191Z" }, + { url = "https://files.pythonhosted.org/packages/7c/8f/57dcc5360aa6d8d24370c12fe855f6ebf5fbf2718c154fb9fdec7161de07/coverage-7.16.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3284754371dc78592aa3ae4d661d30ee20e02b2b6b0de3590a181a936d0d3b38", size = 250173, upload-time = "2026-09-13T19:08:34.75Z" }, + { url = "https://files.pythonhosted.org/packages/10/bf/379a8c7ac0f3063769dc43fd2475e9fc9b2722cb88eabe71f4d862660c93/coverage-7.16.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:802d1246c540e07486d4ee1adfa19a797e4b33529ffc371dc140644e8f27da0a", size = 253986, upload-time = "2026-09-13T19:08:36.737Z" }, + { url = "https://files.pythonhosted.org/packages/45/25/70218d2a6077730843b75ee2525fa64a750283d71cc4c063dc0b75ba6979/coverage-7.16.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:6fc735d6fe6d57f803e7ba021be4dde48e43e6aff94e6954350555d5332b0594", size = 250444, upload-time = "2026-09-13T19:08:38.434Z" }, + { url = "https://files.pythonhosted.org/packages/58/ac/6c41c441baa6c8a1fed5615e4e1e7d0ca649b33b472ee28591a92d70a1ff/coverage-7.16.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ed5ade1bb18f62edace1bd198c66f9d4c75a8385d5fd24e87d917ea1a5958773", size = 251046, upload-time = "2026-09-13T19:08:40.133Z" }, + { url = "https://files.pythonhosted.org/packages/fa/f9/01166d6f8aa42acd148b4c2c88173d60eefdd0270ea2bcbba79be35bc193/coverage-7.16.1-cp310-cp310-win32.whl", hash = "sha256:0c309096926b119543dc16438a11ef4c80783d2f4e59ff94f7f462651a944cdc", size = 225242, upload-time = "2026-09-13T19:08:41.672Z" }, + { url = "https://files.pythonhosted.org/packages/d2/28/a76060478919becf0fbea2df2053a6e1a7195f11edf8dfd51c58c1636b43/coverage-7.16.1-cp310-cp310-win_amd64.whl", hash = "sha256:7562f8067ed9360e8b9739e5703403a7686dd1b36bf0f89fc538047c54cdea90", size = 225868, upload-time = "2026-09-13T19:08:43.22Z" }, + { url = "https://files.pythonhosted.org/packages/af/3a/d09495dfd5191b5593852bbe2f037afae768157443378d066d9ecea69a49/coverage-7.16.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:72e013665e25cf9d44779f01f340af26319756f9a76822b7c94ce6b1d93813da", size = 223307, upload-time = "2026-09-13T19:08:44.914Z" }, + { url = "https://files.pythonhosted.org/packages/a3/34/310196a80258e28ebf6e5aa814a5a6ffbb18c03cf3c0c0c04191ed893e88/coverage-7.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cb05c0ff98b56ba6969adf35556bc43bcb8d094df8bc9cb403acff53460c4e07", size = 223817, upload-time = "2026-09-13T19:08:46.738Z" }, + { url = "https://files.pythonhosted.org/packages/73/29/ed0fda1fcd440cdfc07e72a07f3c9c3b43f65a6b3d53bff2cba9b9de50e8/coverage-7.16.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0d0ececb32090e3fbb03e0d352b973a0485879b4de6c58daf47227b9988b99e5", size = 254222, upload-time = "2026-09-13T19:08:48.358Z" }, + { url = "https://files.pythonhosted.org/packages/ac/4e/5e2507fbd71ec1047620978f39568b0d2ed6c4468d08495a0c24f5677f65/coverage-7.16.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f0ba3892d81aacf36996c52f16bca04e39af31a6c5de930b7688ab617f4a6475", size = 256134, upload-time = "2026-09-13T19:08:49.926Z" }, + { url = "https://files.pythonhosted.org/packages/7e/b1/a9f97846554bc240473656f9c3874591105f29c08eccf4282daadc6bc281/coverage-7.16.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6410b75fe07d5271eaa95fc24bd0a9177ed588d9d1c10c0cf67829adb8f0567", size = 258240, upload-time = "2026-09-13T19:08:51.584Z" }, + { url = "https://files.pythonhosted.org/packages/a0/c3/2b1ab208d06719394851cdd7dc322157870a93b178410b1bda6cac7755f9/coverage-7.16.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d1039cb2de093225d597109342ce1675626bd127565e80b3044f4eca07c15b2e", size = 260202, upload-time = "2026-09-13T19:08:53.204Z" }, + { url = "https://files.pythonhosted.org/packages/af/14/ff4db31d6e3126d5b72aab1e868d35ec94e1b71dee46f5c3b54cb1b2438e/coverage-7.16.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cf047bc39fde5425be2628666d0f435ed8817859111c3aacc84b32d858069f5d", size = 254305, upload-time = "2026-09-13T19:08:54.833Z" }, + { url = "https://files.pythonhosted.org/packages/ce/27/c28faa4818f61c49ca3bfb4a77e1c191e79a40447504b89af0bd859cd6fc/coverage-7.16.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2c05913d0d5badf7ac83200f35dcf9514cce5df16a7cc89e7d1d7fff0461813b", size = 255935, upload-time = "2026-09-13T19:08:56.453Z" }, + { url = "https://files.pythonhosted.org/packages/46/5a/42e64e5b716048cae33e2e15f8c6fe04a927467056c533b8e88900da895e/coverage-7.16.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4027bf6d7bc0a16df058ce913b69f10c5687f8e1ca668f08caa659ce101744bf", size = 253995, upload-time = "2026-09-13T19:08:58.159Z" }, + { url = "https://files.pythonhosted.org/packages/84/e5/860287acd5a1e29acd337f302b476b54148203590091b1a1555b2e914b50/coverage-7.16.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4f48b345f831eaf4402ab6333c2dc3e2e2b5bc7b9c1b8fe12680dee3f0538f01", size = 257767, upload-time = "2026-09-13T19:08:59.958Z" }, + { url = "https://files.pythonhosted.org/packages/a5/c5/4b68006da567b4b413352f891776c35776b3557a0479a28187ab57c21659/coverage-7.16.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:8643baeb590726c558b2faed6cd59b0917480f9367fcc692026f1a86d824fd08", size = 253715, upload-time = "2026-09-13T19:09:01.778Z" }, + { url = "https://files.pythonhosted.org/packages/a1/0e/b2d0c47cfbf11770e197f1d0f11a92864eb29fd7eaca45bc9915573d9725/coverage-7.16.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d06dcc420b570bf683cdb647cc8fe62b672d9e429ef711c3cbbb7a6880ca1572", size = 254624, upload-time = "2026-09-13T19:09:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/2e/14/3bab8821b2b942155578fc34e88e21b0670d922f039ab23651f6035ba7f7/coverage-7.16.1-cp311-cp311-win32.whl", hash = "sha256:946f58aa59b08bcd6afcc6a7bd0ff54ed5eee844f32ad69fe6814836d15856a2", size = 225404, upload-time = "2026-09-13T19:09:05.174Z" }, + { url = "https://files.pythonhosted.org/packages/9b/ce/4f4ce667a97d1c538b46a79b89161395ad2beffc248b6c782afc9f521927/coverage-7.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:684c7ee9b4c04358fe6ac8b517ab51ec35fcd79d08ff0f105dd8bcd96885bbb7", size = 225879, upload-time = "2026-09-13T19:09:06.81Z" }, + { url = "https://files.pythonhosted.org/packages/26/17/f3dce5e47351ab34522dd037fd64b111742958e24861bdd2820971bc1a26/coverage-7.16.1-cp311-cp311-win_arm64.whl", hash = "sha256:1b24f79e25bcf6c73931aeca7a3dfc7595c0cb5e9364aba3fdf387a3de4b1c22", size = 225428, upload-time = "2026-09-13T19:09:08.502Z" }, + { url = "https://files.pythonhosted.org/packages/1a/f7/7cd4c9f2a3b7574414222ec425d5eee21cc690d867a013315e3be8ba185c/coverage-7.16.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b7f2c26ce6ce0b1e0ca0d5fae96ea510e3a2e78b7207f06e76b7f2c87fa3d0af", size = 223475, upload-time = "2026-09-13T19:09:10.145Z" }, + { url = "https://files.pythonhosted.org/packages/3e/f2/9ac65f9cedd43f91e2d3657c11f13aef82aebae89b2243dc9e44fe58a740/coverage-7.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070acb9da788dff743a4d36fc015feee12d68f0349959017542017c79f59c21c", size = 223845, upload-time = "2026-09-13T19:09:11.988Z" }, + { url = "https://files.pythonhosted.org/packages/62/4d/f13db452d4367fe1122abfd98ae330596f65a3b40498f8e1a5811f68f123/coverage-7.16.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e366587b370bc9b8b51b7b7272c610c56db5d5b4795b9e4a29d28ff2f440f809", size = 255341, upload-time = "2026-09-13T19:09:13.708Z" }, + { url = "https://files.pythonhosted.org/packages/5d/6b/85ed86e82835a96ddfbe7705c387c28ce1cbbecd1b6d606f5ddfeeb712df/coverage-7.16.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e82e10b9d290f60b63459cfb245a841aec347603997206296b93881463a93dcf", size = 258078, upload-time = "2026-09-13T19:09:15.368Z" }, + { url = "https://files.pythonhosted.org/packages/cd/d2/f66945853d850b9c05f4e012e37396f1f31d0cd40d062394b229c22e7b56/coverage-7.16.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d73bb1f85c4150ac208fb0755beb04b2e44897bad81414de9380f98dd74729f", size = 259191, upload-time = "2026-09-13T19:09:17.046Z" }, + { url = "https://files.pythonhosted.org/packages/77/a1/7b907abe62461f289035c7ac60ab3e6334efb8c425151aac4abfa0c97820/coverage-7.16.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3397b9032553d281ad6a9253b12675b65e0cc8cd7a3b0633cf48872c9eb13360", size = 261452, upload-time = "2026-09-13T19:09:18.756Z" }, + { url = "https://files.pythonhosted.org/packages/51/e6/e26f4d6b1069a2a2fee3238a132f85c5b4c1dc25aebd88e015451ff0bd68/coverage-7.16.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:77890395cf37026a5907d3ad32376aa51f41c0f163b7477fdbd4f94966cc1d08", size = 255698, upload-time = "2026-09-13T19:09:20.786Z" }, + { url = "https://files.pythonhosted.org/packages/7f/ec/99db7450e813050ebce300e31bbd4f997c76a8c6e3a6d168c5dffc104109/coverage-7.16.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b0944dc3bee3091039bf970d73caaf930c906013128a421bdc132e797494d941", size = 257111, upload-time = "2026-09-13T19:09:22.693Z" }, + { url = "https://files.pythonhosted.org/packages/2e/9d/500df9d3cd8c541ac84b5e0bcab00646be75654e05aa34e8410ec851282d/coverage-7.16.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b89d22a89d5bc05dd95b64e08295b8394aa96dc88e08f8ba210c9ebfebbe0489", size = 255258, upload-time = "2026-09-13T19:09:24.429Z" }, + { url = "https://files.pythonhosted.org/packages/c0/24/53318d96da332bb4946f8ac646fa19fd37fecbd0c49144735c716ac69bf0/coverage-7.16.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:550a2a1faf7559f13d5344f12d1eb886ad87955155d7dfab2a3fe5c8ec8fe776", size = 259326, upload-time = "2026-09-13T19:09:26.32Z" }, + { url = "https://files.pythonhosted.org/packages/c5/ce/abc0462b2e6ae96ae22197d2bc30fe33b6b4e52937e782745436ceb2a761/coverage-7.16.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:55eb268e5b81aefac759766c9162625b06c1bedb7b77d936225bafc4f038a6f6", size = 254827, upload-time = "2026-09-13T19:09:28.191Z" }, + { url = "https://files.pythonhosted.org/packages/8b/e1/0a04eedaaf19196b51f0180968134228c7646e469ed29914e48690a7cf3e/coverage-7.16.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:65a8fc80898c9ce59f04349fe8b4849b1f9787f14e52ead990e5f849ff4727a0", size = 256698, upload-time = "2026-09-13T19:09:29.922Z" }, + { url = "https://files.pythonhosted.org/packages/de/1d/d441a55cf22ce9d8e9e34814806c47441ab844bd534e0f4b64e1c6ae8bd1/coverage-7.16.1-cp312-cp312-win32.whl", hash = "sha256:528a61be40977c340cf201d23b69bd6a6bab507da60e9dbda85f8b30e935d70d", size = 225541, upload-time = "2026-09-13T19:09:31.752Z" }, + { url = "https://files.pythonhosted.org/packages/73/27/ec3d032375735dd331477caa051419678079ff90fcb53d0284a6c2bfb757/coverage-7.16.1-cp312-cp312-win_amd64.whl", hash = "sha256:d0f02c633630e2b74522108ee95a84ad6e1204a8016a6cca5297f335ea27147e", size = 226075, upload-time = "2026-09-13T19:09:33.556Z" }, + { url = "https://files.pythonhosted.org/packages/94/00/90e9f5c4434878494306b9c0ee8068ebbd829483737d8cefccc58884d728/coverage-7.16.1-cp312-cp312-win_arm64.whl", hash = "sha256:2959978f9d1d20a2c0c15d0a68baaeccf615ac1aa214cf4a05a10d6f568926c8", size = 225461, upload-time = "2026-09-13T19:09:35.48Z" }, + { url = "https://files.pythonhosted.org/packages/aa/74/c08c0c4dc9fa6bcd1d90728a63660aa1b17b488a806948598456c48f75d1/coverage-7.16.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ee5465db6e9152a7d09f3215309326878c6aa3ac509195a369f9d264ff4bfbd9", size = 223501, upload-time = "2026-09-13T19:09:37.276Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c8/784986d326663285258a8e39835c46fb730cc85284f0dbcd82078586dd22/coverage-7.16.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2b8256f8b525ba233d2e4cdcdce0d6673c66fc9bf70df1fd5e67c54a74e2d245", size = 223876, upload-time = "2026-09-13T19:09:39.385Z" }, + { url = "https://files.pythonhosted.org/packages/15/76/73fb792928872bbb07e553f920ff55c65ee962c469265feb5d1d4ae5f97a/coverage-7.16.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d57cc400275b9a2892e905fc893f732b21ddb95271bf96406c88e2f6367848b5", size = 254863, upload-time = "2026-09-13T19:09:41.326Z" }, + { url = "https://files.pythonhosted.org/packages/ca/8d/1fd78899513244b065ccecdd1cfc6aa8b8f1bdee796d8c4f2aa8e8e5397d/coverage-7.16.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6b3fd0f3435ebb7a7183b32a6062a8b755f08242ced1f3f22761d30b56b3c2a5", size = 257460, upload-time = "2026-09-13T19:09:43.086Z" }, + { url = "https://files.pythonhosted.org/packages/47/1c/b23ddfcb7ef9bd7b3fdb7be9a5dccf9925ae88e556df7aa5b655283c78f2/coverage-7.16.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e5eb1762e7eb5fad34ef913e8107c7788a66f19d328e598ce95bf7217f9e5c8f", size = 258697, upload-time = "2026-09-13T19:09:45.161Z" }, + { url = "https://files.pythonhosted.org/packages/1c/c9/19d0c6ca35778a7e9415c30c46a0c64c9d4374219d004a35563132296896/coverage-7.16.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:46cd3a73e9140410de62cceb66214bce0e08fb3922b9176fbfc1522fec151b41", size = 260827, upload-time = "2026-09-13T19:09:47.061Z" }, + { url = "https://files.pythonhosted.org/packages/97/fc/5862f7344382c62b85df43d483e579168cc062e007f54d895dfa51fe3e7d/coverage-7.16.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d1ba5142d68dd2cb775cbd0ac8601819298152047803c8efe4eec6d7d7aa7878", size = 255038, upload-time = "2026-09-13T19:09:48.945Z" }, + { url = "https://files.pythonhosted.org/packages/2e/9f/5c26583199a68df8d15124b4590520903f8eb7cef17db293fea712bb0783/coverage-7.16.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c389c6f9d1d518e1249ddcb8a7f158135644ce2c508fa6cc17b680777dad5bf2", size = 256827, upload-time = "2026-09-13T19:09:50.738Z" }, + { url = "https://files.pythonhosted.org/packages/95/13/605198bff079b107336710f28a797312ab132587168600d70a290e7ecd2e/coverage-7.16.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cdc57746c7ac0ea063351b4d651c3bb4dd4fd35e64dbb8e90c10e14eb03c4080", size = 254794, upload-time = "2026-09-13T19:09:52.577Z" }, + { url = "https://files.pythonhosted.org/packages/43/b7/0bbb32dc5ccdac766a13763fdfbffd7d73fc80349a69230c46c6b71e7508/coverage-7.16.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5597180ed7670cc94c04c65347418a467d3a43d5f0cf52fcac647f5425f42037", size = 258947, upload-time = "2026-09-13T19:09:54.372Z" }, + { url = "https://files.pythonhosted.org/packages/13/bd/65c31ddd43ff4e61b63721b3dad17cca412dba6e2ce89f92abdf75734339/coverage-7.16.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:a9647a0ac46255b8fef59a433a2161f03e5483f3a35e1cbd9dfe4600baff0c6b", size = 254613, upload-time = "2026-09-13T19:09:56.198Z" }, + { url = "https://files.pythonhosted.org/packages/25/20/d278115f2ba522b3e3128c6852be265f4e0df7202b2effca4db96cf0217d/coverage-7.16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e306e98186b9cd109121f3583aeb7978797ad21d948f22944c5c08845cd554d0", size = 256388, upload-time = "2026-09-13T19:09:58.095Z" }, + { url = "https://files.pythonhosted.org/packages/e1/ca/d43aa396fb3a2f71c9411b99d926475f5acaf5c124f605c592865c80c8d5/coverage-7.16.1-cp313-cp313-win32.whl", hash = "sha256:48a78a66fcce49d7f6156524bf979c0ac633d584199717c68c6ffa949fc14e6a", size = 225550, upload-time = "2026-09-13T19:09:59.998Z" }, + { url = "https://files.pythonhosted.org/packages/0e/30/df2f6114f6a17cffe94721bd1d298f77f86aad493717fa5bc03cb291a1a6/coverage-7.16.1-cp313-cp313-win_amd64.whl", hash = "sha256:7af03247d598a353bbbbe1b925deb735276e4d845e7197c4073dc89352b236fa", size = 226091, upload-time = "2026-09-13T19:10:02.331Z" }, + { url = "https://files.pythonhosted.org/packages/ee/31/6f90d8aab72a112492bd50e3b5485b53b772b1f5fa70da0a620e723caae6/coverage-7.16.1-cp313-cp313-win_arm64.whl", hash = "sha256:166adae25b05b04c9a84135912066d9c97482115af38df1a419a38aacc6b6f5d", size = 225481, upload-time = "2026-09-13T19:10:04.151Z" }, + { url = "https://files.pythonhosted.org/packages/96/1a/d6d16babd0a5fe4c3fae40702158c570351694e74516d8d81b86c5637448/coverage-7.16.1-py3-none-any.whl", hash = "sha256:3d8bd4e58b6a5c2018d808f297905393c6c61da466a48c3f0596a76a4900ebe4", size = 215264, upload-time = "2026-09-13T19:12:18.895Z" }, +] + +[package.optional-dependencies] +toml = [ + { name = "tomli", marker = "python_full_version <= '3.11'" }, +] + +[[package]] +name = "darrow-verification" +version = "0.2.0" +source = { editable = "." } + +[package.dev-dependencies] +dev = [ + { name = "coverage", extra = ["toml"] }, + { name = "hypothesis" }, + { name = "mypy" }, + { name = "pytest" }, + { name = "ruff" }, +] + +[package.metadata] + +[package.metadata.requires-dev] +dev = [ + { name = "coverage", extras = ["toml"], specifier = ">=7.10,<8" }, + { name = "hypothesis", specifier = ">=6.138,<7" }, + { name = "mypy", specifier = ">=1.18,<2" }, + { name = "pytest", specifier = ">=8.4,<9" }, + { name = "ruff", specifier = ">=0.12,<1" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, +] + +[[package]] +name = "hypothesis" +version = "6.168.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "sortedcontainers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/ce/c0946bebffb99b62426a6a7643d4272cc6c5cf777a488b3b4d0ee724e960/hypothesis-6.168.0.tar.gz", hash = "sha256:72af51087b7b5ab21c49f0d502f803c20897678652835596bd2a8b169a39135e", size = 510805, upload-time = "2026-09-08T18:48:36.072Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/f8/8b2cc9ae7b439538f6f2d32a92892340b6343a6b04d171c516666901dedc/hypothesis-6.168.0-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:47b89491ff02e3ae9b302c440457938e87b47a45b9a1d98ff5575b6910d779e2", size = 791358, upload-time = "2026-09-08T18:47:37.076Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f4/4d7d897310cde5085779fb96feadb8529d98cb8e51ed7b24f7da9b6c6bdc/hypothesis-6.168.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:1f4cd0ff11bd470a1a846296ed5fe55e84214194850370994fd1370fe73d3099", size = 787081, upload-time = "2026-09-08T18:47:16.227Z" }, + { url = "https://files.pythonhosted.org/packages/26/7b/9d52066d363faba7f3ac20ee60a3c696a475feb2c477d235d0d649d41cc1/hypothesis-6.168.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:732ae5d47482f99d8028cca096729625f05690a83f5e7ce31466e266155792f4", size = 1123850, upload-time = "2026-09-08T18:48:11.504Z" }, + { url = "https://files.pythonhosted.org/packages/50/cf/aa46d76fa7df43caf2c372e394fda84ce1dc08421814f8674a6b9295e2ea/hypothesis-6.168.0-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2085ee74ac3ab6b70e2f7ffae9b4cb74c246da2f574b2de81a0818a8a30f659f", size = 1147685, upload-time = "2026-09-08T18:46:58.219Z" }, + { url = "https://files.pythonhosted.org/packages/84/c2/78ed8c8d5aa37e4baae2a4b3e29687ee3d9b7f1a5e56ea5ea5ec7ec71ecb/hypothesis-6.168.0-cp310-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1894782fae5d9a7bb44e6dcf848ccb09ccb5babab48d8b5c31a0a7fc025b82a1", size = 1149294, upload-time = "2026-09-08T18:47:04.757Z" }, + { url = "https://files.pythonhosted.org/packages/78/7f/d57440f19e9de70e85359cf179ce786f309ee17609bd3c5a0113272875c0/hypothesis-6.168.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecf0ab13cef899efb816ffdd7963e0679f372520884ce06756c7642f3df94213", size = 1169729, upload-time = "2026-09-08T18:47:44.056Z" }, + { url = "https://files.pythonhosted.org/packages/a1/86/dc74410a186990bb22c2a3eea0e77804f2eb0f300860b46d7d8948073674/hypothesis-6.168.0-cp310-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:3f6dcf66270278d078bed01b401f47db4e26456cd909d8e23c6b9366a6c0b131", size = 1129182, upload-time = "2026-09-08T18:46:33.382Z" }, + { url = "https://files.pythonhosted.org/packages/b2/5e/be048fc4f6dac831625e155bdf11e4233caf46bb54030391d6fba8e19449/hypothesis-6.168.0-cp310-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bfef4d46dbf1704a7b8fa3a78778651a2cb18870ca0a70da19c381646822b149", size = 1160180, upload-time = "2026-09-08T18:47:26.459Z" }, + { url = "https://files.pythonhosted.org/packages/0f/4a/15a34498a5f08720fbbdbbe4668a8050fe4e17c16c9eeb6f56a017f2fa6b/hypothesis-6.168.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1d1aa5b3484e329295d88488a5ba06243909e65c2ab616513c2d36721de4ed1d", size = 1299711, upload-time = "2026-09-08T18:47:28.34Z" }, + { url = "https://files.pythonhosted.org/packages/77/09/5354e0dae302ab98c4f0046b7e8699c2186ae4349397bda5d5c852bda68b/hypothesis-6.168.0-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:3bc00fd8cda04b58e37a1163e8a65389b247b4f5ee547ae37d244a4960995517", size = 1425341, upload-time = "2026-09-08T18:46:56.785Z" }, + { url = "https://files.pythonhosted.org/packages/59/7c/3c0e1f59043ff128c70a51d298d3d6b5973525c357a1e1d0542dbc05ac90/hypothesis-6.168.0-cp310-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:990026952d5b2eca290c88f639ac639233f47e13dae338c6dfb6e4774bcab349", size = 1281063, upload-time = "2026-09-08T18:46:55.401Z" }, + { url = "https://files.pythonhosted.org/packages/40/dd/db884db9a7d42ae6b72a00c13c725940b638dfb263e618b22af59d5dfa2f/hypothesis-6.168.0-cp310-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:a74b0945acbbd552c7c2d0a99a3b5232962b8848c8eed1829451800a9bfcf00b", size = 1300247, upload-time = "2026-09-08T18:47:02.949Z" }, + { url = "https://files.pythonhosted.org/packages/99/8a/4ee9769e1d48676efb6a78a130f82e0d52d3f87b2055294102272a08615c/hypothesis-6.168.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2a380b521b5a76a9e8917d64adcf7f861a45a4360a34b1579af14c5df8eb0377", size = 1336084, upload-time = "2026-09-08T18:48:05.675Z" }, + { url = "https://files.pythonhosted.org/packages/61/17/d4ed11bc99d205d6d2651a0f1a4874f377150f836b5a0849bd511d68a2eb/hypothesis-6.168.0-cp310-abi3-win32.whl", hash = "sha256:2264f15a1c80329e3ad48e39c44bd5c9429b7b04c9ee62cdd72f4b10aaac9f29", size = 677989, upload-time = "2026-09-08T18:47:24.722Z" }, + { url = "https://files.pythonhosted.org/packages/77/51/abf1fde7b8afab87db30afb73b3847e62440551d146472111cabeba2fe00/hypothesis-6.168.0-cp310-abi3-win_amd64.whl", hash = "sha256:5b54769033b84477931d2072e7133a7555e0de5c53fd5ca3bbde960762d7d31b", size = 684692, upload-time = "2026-09-08T18:46:24.449Z" }, + { url = "https://files.pythonhosted.org/packages/12/e2/64d79aed47a95186ce9edcef60eb4684870c72542da3b7027c2483d8ee8c/hypothesis-6.168.0-cp310-abi3-win_arm64.whl", hash = "sha256:112b0900059bf9d7d6528ed729770629ab146e0d133c4143b9bd4a01dc002bcc", size = 682709, upload-time = "2026-09-08T18:48:03.756Z" }, + { url = "https://files.pythonhosted.org/packages/95/67/bfacadcc6cd4bd179d1cd2f6c85f580136019b38af7db71e3159ebb3ce33/hypothesis-6.168.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:cb10aa59b0af45badca76911f5323f40d24fdbe00d01b7b67fef8648c99411b5", size = 792193, upload-time = "2026-09-08T18:47:42.392Z" }, + { url = "https://files.pythonhosted.org/packages/fc/29/4abab4114035f54438e3b07bdab3f68fd8769fe0fb84f8ffd6656eaa58fd/hypothesis-6.168.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e21e30b76b6d3adb87c550576132a3204f4c257ec43353f6c09b9d59bb762abc", size = 788121, upload-time = "2026-09-08T18:47:09.398Z" }, + { url = "https://files.pythonhosted.org/packages/85/d3/c54242439dcb59c7e3274a2952188bebbc605be397c3a6ff027fc81e1726/hypothesis-6.168.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:527452b43e79e6dfbf9cb69145a940547a3cd177c556698a3fc939ed2354c4b3", size = 1124222, upload-time = "2026-09-08T18:46:28.345Z" }, + { url = "https://files.pythonhosted.org/packages/7e/8b/1c9f54be7040fe7d94e4cd5d9343da5a36240bee2fa856f44109f9622251/hypothesis-6.168.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:489d5c060f49f495b64215cae627c71730cffd5ef59dc4d7f431932e6e6d2e67", size = 1170346, upload-time = "2026-09-08T18:46:52.784Z" }, + { url = "https://files.pythonhosted.org/packages/af/d4/f6b1557bd3cf2976d56465fcbc483214bc6e7f1aa7e4db9f8c5f776d547a/hypothesis-6.168.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c3af200b322f710c76c2189866246cdcff2039165dd77edff1a7bf1157162fb0", size = 1300267, upload-time = "2026-09-08T18:46:45.644Z" }, + { url = "https://files.pythonhosted.org/packages/e5/ad/a345588acc038deb09a43610328004d31fe5a53289627e03b0199978dc45/hypothesis-6.168.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:73084b76e4a79cd0f7883ce80fc60c9f374ce7dcad8f520b39db40470ce1852f", size = 1336724, upload-time = "2026-09-08T18:46:44.179Z" }, + { url = "https://files.pythonhosted.org/packages/97/e9/c5e380fd29fd552ff442bcfb1090926287e2911d40212ae0602a0736347b/hypothesis-6.168.0-cp310-cp310-win_amd64.whl", hash = "sha256:8067e6b4b48e5cfdc849a1a20c9d4972b3f532b3e3edb5e2b5dfd106045a5236", size = 684644, upload-time = "2026-09-08T18:47:45.814Z" }, + { url = "https://files.pythonhosted.org/packages/a6/7a/7a28d9afd52c9a89c4d8e3170c92fe1ffbd4a4ce3f905ca15fa7ddaaa581/hypothesis-6.168.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4d7d29dd63ad9fdc4aa1d65fa272449e14aaf6c6bb8451091818c2945533a43a", size = 792045, upload-time = "2026-09-08T18:46:29.612Z" }, + { url = "https://files.pythonhosted.org/packages/0a/40/e2d6fe12b54abbc8b802b234ec4974d0a420dde4098f57a324e9c791ac27/hypothesis-6.168.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a72ed7afa1f7e30488b8a5754fca0ad9755518bdb77d6f0b003cadf7437a5f9", size = 787932, upload-time = "2026-09-08T18:47:11.145Z" }, + { url = "https://files.pythonhosted.org/packages/ff/89/cc78e05f3248949c6d9ecb489d0e422247d11b08533ab02e177a10a01610/hypothesis-6.168.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcc5bad4300a751804ce41f0e10d77f85272668160708ce39ec579bca8984843", size = 1123994, upload-time = "2026-09-08T18:47:12.647Z" }, + { url = "https://files.pythonhosted.org/packages/f6/e3/7d483a79e2ed9a6c868cac8783b3c6c2bb82303f7db364c9da9a406df10d/hypothesis-6.168.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53469a1a7c4861b12c9a8622f762d7d1fd7bcf171884e1018ed5a8f063a5c063", size = 1170271, upload-time = "2026-09-08T18:47:54.322Z" }, + { url = "https://files.pythonhosted.org/packages/78/87/bffaf5b5e6b4566aa40d8a4ae25d8d84b47dfe0d9e771f454e4974cdb428/hypothesis-6.168.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a9650c4882fdbdd8e90bdae602a8bfa8c6f09dc5d06afec5b9b23982e8f60a04", size = 1300162, upload-time = "2026-09-08T18:46:27.022Z" }, + { url = "https://files.pythonhosted.org/packages/25/06/6c30a00fc2f5c58546e858259cf2261c6a3ef6a060c373e00d00311267be/hypothesis-6.168.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:348d9b93fd4129f67f9bab94f3d70709a9372bbe0e0d22731325ce85d5eb409f", size = 1336294, upload-time = "2026-09-08T18:46:37.324Z" }, + { url = "https://files.pythonhosted.org/packages/fa/b1/501910e5a6a9c245f2394fda016376f6c7e209c5dca8c056eaece64ad10e/hypothesis-6.168.0-cp311-cp311-win_amd64.whl", hash = "sha256:719b45b0512e3535a6a0077c2f7c6053b02ac0e72d60693f66f98790a33855b2", size = 684464, upload-time = "2026-09-08T18:47:19.779Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5e/0035896c101f0484c364353f8ee30175eef8936b49171618677287fdd85d/hypothesis-6.168.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6b750390dac4429da0cb70ab3fe758457f0cea3d9c843d48c59d0690d1189fda", size = 793115, upload-time = "2026-09-08T18:48:21.352Z" }, + { url = "https://files.pythonhosted.org/packages/11/5c/938173e27df771cc6e92bc47f117a1b1be4a88fc6dc214f7fc65f9c7ad93/hypothesis-6.168.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8e4b2d434e0dd134f3d31ac1efc1825bf99730dfe70fec005ff66d7211836d79", size = 784634, upload-time = "2026-09-08T18:47:21.406Z" }, + { url = "https://files.pythonhosted.org/packages/88/00/0b2c6ac07d519131f97712f2533750ffe9b2490eec8f518ef3a5ed2dd514/hypothesis-6.168.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d4d36ed2fd62de11382f1d608169c1ffa9a49d3b9351146d8ff87cb81a66f7", size = 1122855, upload-time = "2026-09-08T18:46:21.967Z" }, + { url = "https://files.pythonhosted.org/packages/8d/21/dde930fe43171cab37572bd993d70c2a271f240f428f8ccd64ec4c2d661b/hypothesis-6.168.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5920d267f7d8cfd376672f2bde5905cdf284d47519582e41ce7c142d48ee46c4", size = 1168932, upload-time = "2026-09-08T18:46:39.856Z" }, + { url = "https://files.pythonhosted.org/packages/4c/5d/92b83c3d06194ec626e92723d0b0f70221ebf42d7cb355ed36929df6d735/hypothesis-6.168.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:fb8cdf45361e259df86e19f8cd042ce2d6c7e6ad88fa631b78a4e3a83c2e572d", size = 1298566, upload-time = "2026-09-08T18:48:19.485Z" }, + { url = "https://files.pythonhosted.org/packages/9c/67/52de8bf3446e3d2d555b812d96673b31bb213b5b9d5804a666e5d0bba76e/hypothesis-6.168.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3b3ce1cce70b25a37ed1a38a53ce7204785726c675c0f41a0f83c338a7e47b3d", size = 1335074, upload-time = "2026-09-08T18:48:01.561Z" }, + { url = "https://files.pythonhosted.org/packages/14/fd/e592773c1c0ce55e35d26ec55f75546bf1fd72ef5a5c520ed685969b40cb/hypothesis-6.168.0-cp312-cp312-win_amd64.whl", hash = "sha256:f62bdabf278db9ff61df5f3203d608949f0d893d0e30cdac3f2330e67e41ae68", size = 682026, upload-time = "2026-09-08T18:47:01.471Z" }, + { url = "https://files.pythonhosted.org/packages/ef/e9/39bb8fcccfbafd10fcc777d583c6ebad5d2148e7d743cb350562f28e974f/hypothesis-6.168.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d55562bf8d41cfa18559c33f30cadf44ceac8e517509d7a022a9feace621f28", size = 793050, upload-time = "2026-09-08T18:47:56.045Z" }, + { url = "https://files.pythonhosted.org/packages/f7/dd/00fd32e8ec470535e0065cb8d6e175f9fc54b4d3a6f1269f6c487f8bd79d/hypothesis-6.168.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92cff497b92e2285ff6a94193fdee04aba483a4115d501c1f9a570bd103fcd20", size = 784558, upload-time = "2026-09-08T18:46:32.054Z" }, + { url = "https://files.pythonhosted.org/packages/d2/4d/553c47093f68bdbac0438e16c024ce97649b5804dc6972ef86b9bd2db8a1/hypothesis-6.168.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ff259260015f9be3756dcd4bc11c08e007314dec6b43d9a89084c4f34f94475", size = 1122841, upload-time = "2026-09-08T18:48:09.374Z" }, + { url = "https://files.pythonhosted.org/packages/43/d6/0b5940aa75e617c8fd12200bae24d1b71347362514e8210735c581d4d3d1/hypothesis-6.168.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35f1262831b5acc74ded15f629965daffcd657f6016ee04fc9605f6eb2b334c0", size = 1168825, upload-time = "2026-09-08T18:47:33.702Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c0/800de1231b2869b51409bbf85799d6f1bf49a00e0afff0aabc097aa8f1b7/hypothesis-6.168.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:046fe4bcfce2a2fa186ba9d96bbb62c25c2f6c2e4071f0783ed6b5cc481d0669", size = 1298433, upload-time = "2026-09-08T18:46:38.53Z" }, + { url = "https://files.pythonhosted.org/packages/be/35/9907667a30c1dbabbc44a09b4c25a0f575570937fcbbada924ae3a1dbf2a/hypothesis-6.168.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:24b52a2b1c8db6e1e516f9295c8e4ef7ef63303ff24fbbc5b35f4ff71dcd732c", size = 1334988, upload-time = "2026-09-08T18:48:29.395Z" }, + { url = "https://files.pythonhosted.org/packages/98/8a/7bf214e703fff532ed47cb52ab93ce4b7ea41e4e7084593fa02b740828b7/hypothesis-6.168.0-cp313-cp313-win_amd64.whl", hash = "sha256:ec0886fe0be9091669937989f9a662beca42ae14a4a6dab25491c2c63365f88d", size = 681990, upload-time = "2026-09-08T18:47:49.173Z" }, + { url = "https://files.pythonhosted.org/packages/16/f7/5adae1bf1d4877aca2e8c8e007e57077237e9ac3765e430ffda490b17e19/hypothesis-6.168.0-cp315-abi3.abi3t-macosx_10_12_x86_64.whl", hash = "sha256:754016594fe78cef91790e0922f60d183c52f531255fbfa30dac495b813e2128", size = 791056, upload-time = "2026-09-08T18:48:27.398Z" }, + { url = "https://files.pythonhosted.org/packages/f2/93/b1b2770b87591db5cf564b9aa0265cf21e9207d28645e0abdeb8df63225b/hypothesis-6.168.0-cp315-abi3.abi3t-macosx_11_0_arm64.whl", hash = "sha256:6f0dd437ec01140676192422b61f2f833b3ce6a3213da9b7e196ad6b3777e795", size = 782980, upload-time = "2026-09-08T18:48:34.087Z" }, + { url = "https://files.pythonhosted.org/packages/de/bd/673171c1d2423379a7d4a0f9f009cca735a422d0c4a4ac6422d1d1736cab/hypothesis-6.168.0-cp315-abi3.abi3t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f77af7721ff35a58fa8797decd14c932c350a2548686c6e9b844db710a3a2441", size = 1120964, upload-time = "2026-09-08T18:46:59.92Z" }, + { url = "https://files.pythonhosted.org/packages/7d/00/33a9bd941b22a4fd8a8c805b1563e0db17efc020422f5bd15bdd0fdf258f/hypothesis-6.168.0-cp315-abi3.abi3t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a0d28418c104d7268fdebcc09bc49f7b6569b5eb942430c6859f53ec8d4edf63", size = 1143869, upload-time = "2026-09-08T18:46:49.875Z" }, + { url = "https://files.pythonhosted.org/packages/0f/c1/963460976f41721eff8f67f30d31059ea407cc1b41c737c8859aabf37197/hypothesis-6.168.0-cp315-abi3.abi3t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:812a84c4cc7f7ae4fcb39a5647cc2698e6c18254f8423126425578f1dcdac782", size = 1146453, upload-time = "2026-09-08T18:46:25.757Z" }, + { url = "https://files.pythonhosted.org/packages/ce/53/09db238098ad66f4e6d2fe883f26c270c2595b90e21c8f969d4cb21cad7a/hypothesis-6.168.0-cp315-abi3.abi3t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6de30e559eb151de14a5f74bceb4d97792a9315ada2a1816b5da825cd7d28edc", size = 1166918, upload-time = "2026-09-08T18:48:23.436Z" }, + { url = "https://files.pythonhosted.org/packages/b9/31/e1b7b452c8a6166e445ba2ad80a864f6a9eee0fe4c8cecdb9af5c1ee0aa5/hypothesis-6.168.0-cp315-abi3.abi3t-manylinux_2_31_riscv64.whl", hash = "sha256:9018b20acdb061b2ef4b2fa7f558ca5db97ffea316e0a528bc003a24b2ac996e", size = 1126637, upload-time = "2026-09-08T18:47:50.878Z" }, + { url = "https://files.pythonhosted.org/packages/97/2b/4eceed248afb46fb6b2df21cf2239362de5b25d295c2dc67a82ec8657d5e/hypothesis-6.168.0-cp315-abi3.abi3t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bc935a5d5f86fd8f5af951b8fbe00307f6f7c596f82a9a27c17d974f6ab0a26c", size = 1155682, upload-time = "2026-09-08T18:47:30.234Z" }, + { url = "https://files.pythonhosted.org/packages/b5/3e/f3414cda4f325004d5774485e8983b7d1b99e4b91013f013dd088fd778cf/hypothesis-6.168.0-cp315-abi3.abi3t-musllinux_1_2_aarch64.whl", hash = "sha256:45fcfa05f746e253350f55f216bcef59754f5f2b85745f1fc2bb8ba81dd517a9", size = 1296464, upload-time = "2026-09-08T18:46:34.833Z" }, + { url = "https://files.pythonhosted.org/packages/aa/40/ca79cf96545e1f172b36b8df56bfeb02b61f351f283026f9a57c4631e368/hypothesis-6.168.0-cp315-abi3.abi3t-musllinux_1_2_armv7l.whl", hash = "sha256:f89d8e998d3c936ffbbd1c3686c96f0378f6558aecc5967a3035a857f2bab0ad", size = 1421853, upload-time = "2026-09-08T18:47:23.083Z" }, + { url = "https://files.pythonhosted.org/packages/77/bc/657d5386740c1f4ac518ff05e4c5b132f1e6df2e7c865ba8fda4279adfa5/hypothesis-6.168.0-cp315-abi3.abi3t-musllinux_1_2_ppc64le.whl", hash = "sha256:d0620fa320fa66649e6bfd71e94f3f86115fffebb7e3c6dcece19d1aaff8e07f", size = 1278216, upload-time = "2026-09-08T18:46:30.871Z" }, + { url = "https://files.pythonhosted.org/packages/51/54/2328cdb70489a36634534478d9b238594a269d8bec4630ea0e6897048347/hypothesis-6.168.0-cp315-abi3.abi3t-musllinux_1_2_riscv64.whl", hash = "sha256:4085b61e25d3dcc6c9151d4115269870aee8cdb921611ee5c989b2786449be09", size = 1297593, upload-time = "2026-09-08T18:46:41.395Z" }, + { url = "https://files.pythonhosted.org/packages/9c/ca/d803fa57e3ff7f460f6e262d2b74822cf143343d378501fd3da601b12040/hypothesis-6.168.0-cp315-abi3.abi3t-musllinux_1_2_x86_64.whl", hash = "sha256:b5449a64eb37d9a4aa6ac9cd2ab0fd1a24145adf421ef1536884f73f39824887", size = 1333785, upload-time = "2026-09-08T18:48:25.434Z" }, + { url = "https://files.pythonhosted.org/packages/86/8f/b9799ae6ba6074f151db2c63f9f6f12d844512821ffaba1a3672d7e59f07/hypothesis-6.168.0-cp315-abi3.abi3t-win32.whl", hash = "sha256:91e3de666a6c4f7543000d1710e25055d63ef3032c98bd2ab338b3087bdaa780", size = 675173, upload-time = "2026-09-08T18:47:52.601Z" }, + { url = "https://files.pythonhosted.org/packages/61/54/14c3e277b451ff24128ecc2673cac59dd7e535bce1a433c466912fd682e1/hypothesis-6.168.0-cp315-abi3.abi3t-win_amd64.whl", hash = "sha256:9a2079cd09919956dd388f1a1f8ea5a79f2b2437650fbeda31d8661217ffefef", size = 681491, upload-time = "2026-09-08T18:46:51.437Z" }, + { url = "https://files.pythonhosted.org/packages/99/f3/827e4a48ffee7e40244b0bf064ba47c2171e053ab7edf1cf770105e23401/hypothesis-6.168.0-cp315-abi3.abi3t-win_arm64.whl", hash = "sha256:085c9aa246487c56a40ca89003d285cbffdbb5be4097ba6d0139f9c21003c04a", size = 679197, upload-time = "2026-09-08T18:47:32.112Z" }, + { url = "https://files.pythonhosted.org/packages/1f/96/7f15e1b10d13e266b0f09cf3117dd2206104a1f6ac3b3baf6ec4c43b7c2e/hypothesis-6.168.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:16864797de4b024e4c6cebd44598af932f870aad811341bc5bc24c738801ff76", size = 792946, upload-time = "2026-09-08T18:47:35.438Z" }, + { url = "https://files.pythonhosted.org/packages/10/fb/32487bdcf68b3805ec5efcb7f92fb002b573ad3f90c252d2f2913c5d3124/hypothesis-6.168.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:283eda952bcb1987ccba1c8b634db0e8a960e1e92e2daa7003bc2392f19cea01", size = 788783, upload-time = "2026-09-08T18:46:42.862Z" }, + { url = "https://files.pythonhosted.org/packages/13/26/1aa42e0069ebc68d29d980d47420af324fa002494e01c1c0321c34f609ba/hypothesis-6.168.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5427a3c951080c18170486f775df6a82153882b819eca6b8e7ed77693634e5ab", size = 1124762, upload-time = "2026-09-08T18:48:07.512Z" }, + { url = "https://files.pythonhosted.org/packages/ae/4e/99509045ed55aaa05d8408663051a93168d1a8a5bfc132d92ec1584ce47a/hypothesis-6.168.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a838218ff1eab8d7b4bf66b96037fce0a802f61f2fa5fd4b784696cac365ce7", size = 1171748, upload-time = "2026-09-08T18:46:47.029Z" }, + { url = "https://files.pythonhosted.org/packages/a2/c9/96e57dfd89913322b5180b031b9e36a29bfe67842108b19904466476cd24/hypothesis-6.168.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:34e3c8b66047ba92f8b8df5e427074058d92db58038f007da4bf9d14e934ad3c", size = 685447, upload-time = "2026-09-08T18:46:35.97Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "librt" +version = "0.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/36/9b/356320fbae2ac8467e21c5e73e1389c80468e4998c62cc7d3536cc51b614/librt-0.15.0.tar.gz", hash = "sha256:4e66cbe84437497d951b799d3e1551291b6fb3d643820a7014b3655d57a59162", size = 214338, upload-time = "2026-08-07T10:49:42.663Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/12/e2e9ca532cf5a0e08c9489826c4a35c6958c92ba0313fda70e8c6c3912be/librt-0.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e1a49adf16a7c9d9646816c2946135527197b6fcf4347c7b8b761cf1bfbf4489", size = 148673, upload-time = "2026-08-07T10:46:22.569Z" }, + { url = "https://files.pythonhosted.org/packages/6d/7c/02005e23478bd5950618d9712e0fd2b4c511657857f3efd8ba6a5feabcdd/librt-0.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81a398f45b45a59200e13cd5ad1ae1d3f44334de98b148331afe2cdfee701c52", size = 153547, upload-time = "2026-08-07T10:46:23.931Z" }, + { url = "https://files.pythonhosted.org/packages/a0/90/d8848a735f5642077fc4b3b4bebcdb08edf10178e3add45597f5201a368f/librt-0.15.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4eafbaff06b9563f8b1c850621ce51605de05208e09d4d71ce490bc972b7b9e8", size = 494355, upload-time = "2026-08-07T10:46:25.122Z" }, + { url = "https://files.pythonhosted.org/packages/e1/0b/8604f41ea02feace490e9e405a338a15f9905369f55b239a9ce31c946f24/librt-0.15.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:b0411b4066db926b80258c60dcb0e6db4c9cee312eab45b7e8866b17ddf9ada1", size = 485459, upload-time = "2026-08-07T10:46:26.447Z" }, + { url = "https://files.pythonhosted.org/packages/a0/ac/84153bda1ce0da609182527ab92b40d961809e544eefdc5a1c2422971416/librt-0.15.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:febb1ce6cac545a54e6b769982824e955a700fdd9fbf3a08a3d82c990968b57d", size = 498398, upload-time = "2026-08-07T10:46:27.701Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3a/5ca6cd282b2c244bec8ec84102e09773264e9c02891d56ab3a8f0e4d7083/librt-0.15.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b230acc1c3bfe2d6f2627ba2b95dc92e58aa494600e9722d0e6ccbc931e59702", size = 515474, upload-time = "2026-08-07T10:46:28.9Z" }, + { url = "https://files.pythonhosted.org/packages/73/d3/bd34110234779eb843c6ed66aba7c9b2091d3dd85989f1fb9922f564cb7a/librt-0.15.0-cp310-cp310-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6da110e5f314c19ab8478464d02ae18808ae73d522c15260fa4918acdcd64da9", size = 509484, upload-time = "2026-08-07T10:46:30.124Z" }, + { url = "https://files.pythonhosted.org/packages/1b/6c/43c3f7f071d71631a7daa3b835ef2168ea39f20692d81464d4e47fbaa6d6/librt-0.15.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:eab9208b00ca55bf75983ec99f7bf13acc746a36102e98953addaad7f7ea1e1b", size = 532534, upload-time = "2026-08-07T10:46:31.511Z" }, + { url = "https://files.pythonhosted.org/packages/c5/1c/b854adf036ea817c40408873a5b794d65a91d9f0f39826f2ad2a2d5d7f48/librt-0.15.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6c013cd3a1721e69e14380ada97eaa4b7b0cdf1c6b96fa765d4ea47c875088db", size = 537087, upload-time = "2026-08-07T10:46:32.734Z" }, + { url = "https://files.pythonhosted.org/packages/25/5c/c9a890e244e7dd725d3bd8b560e41f0aec787eaf343b46956a290ab7b841/librt-0.15.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:567b1c430f8bd560e689421468278ac5941bab4a05303b5d95b6ae10db03f451", size = 536575, upload-time = "2026-08-07T10:46:33.965Z" }, + { url = "https://files.pythonhosted.org/packages/5f/c5/c8e70b60b704299555f55db468eb46b1c81bfc60201ffbfe20407d89870c/librt-0.15.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:29c4cab9df457b19672c39be7f384ebb2bc925c4e2684b8780c222b43eb36389", size = 517142, upload-time = "2026-08-07T10:46:35.577Z" }, + { url = "https://files.pythonhosted.org/packages/56/d1/767a90c41f5d381b3195bc88ac0ec4afda35777c9c781e1f9848fedd965e/librt-0.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bccbd8e5b0bffb7106cf18eb1baa3d7194b1cebb3b4b1cdbd4bdb19382a6ee6c", size = 558714, upload-time = "2026-08-07T10:46:36.829Z" }, + { url = "https://files.pythonhosted.org/packages/f9/b4/3c0624b8dc8301ab808f2b3a910995bcabe28df070fb9a0e5505ae997dae/librt-0.15.0-cp310-cp310-win32.whl", hash = "sha256:8ae493ed5f659a7761c43d42f183db514536073ded9bcf671d2d1df47e29a07e", size = 104426, upload-time = "2026-08-07T10:46:38.594Z" }, + { url = "https://files.pythonhosted.org/packages/31/98/e91c0382304bedb2db9c6801897319a9dcb68daac5e975819b562362f20d/librt-0.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:bc25fb356d0c7810bb49ff3df908ad1fda6995d660ab099ded69244ed7ab6053", size = 125057, upload-time = "2026-08-07T10:46:40.052Z" }, + { url = "https://files.pythonhosted.org/packages/59/52/06790ced2ac7117f890c21bda43c39c958ec82aa665c0718e821d33ff939/librt-0.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:823b92cf3c18ecd08afc70c42473888b41b6e8ef5046f3b82c05c154a2fa3d22", size = 148039, upload-time = "2026-08-07T10:46:41.165Z" }, + { url = "https://files.pythonhosted.org/packages/e7/1d/8e150b7fc449a1f33c8a760965cc1f43b14fc1577d9d0b50ab2701420e74/librt-0.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c70bc1b602cf59917e8f0c7a2cbc8bcc6fbc14d5486136b00707a79619121d63", size = 153067, upload-time = "2026-08-07T10:46:42.418Z" }, + { url = "https://files.pythonhosted.org/packages/51/87/a162bc5a66a35599dc619ecb215145f4de7d68e886b479b6d12593139f7c/librt-0.15.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:814ff83a25b5fce8b9c80c4dd803153fb5c5599fc74db9e022466938368957ef", size = 493087, upload-time = "2026-08-07T10:46:43.657Z" }, + { url = "https://files.pythonhosted.org/packages/e5/3a/aeea1fc620cf48060d3065b37614edbf97043c099d0f50782bc8ca61d897/librt-0.15.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:57f5eeb6ad4c180de583b1038e61fe5fbd9796bb69a8a1c1a0c7ddbec4c8c60f", size = 485608, upload-time = "2026-08-07T10:46:45.038Z" }, + { url = "https://files.pythonhosted.org/packages/52/ff/fe571ad416f0856fd0d5578ffc2e6dc531891e586e36b647bcf50569cab8/librt-0.15.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:82909c8f7eb9952656b65d3147afde4cf8e6d5a991eebc86418b5e65843b0ab8", size = 498723, upload-time = "2026-08-07T10:46:46.35Z" }, + { url = "https://files.pythonhosted.org/packages/0f/e1/7a65eb5dedb1f00aebd948cdd8e17add48bf066cab3514e9daf84ab45a6c/librt-0.15.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f779070399f991400fc451719e0ea388eb7de313388bada2c127a35de05f798a", size = 516002, upload-time = "2026-08-07T10:46:47.599Z" }, + { url = "https://files.pythonhosted.org/packages/5f/45/59832b0ebfbd08c2742e6ece372ceb53f18bf1faef5d33c8daf3abebf749/librt-0.15.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bac89069bc496ebdf4f79ebb57bbd10d0b214c8454225deb672d91002bd17e18", size = 508607, upload-time = "2026-08-07T10:46:48.873Z" }, + { url = "https://files.pythonhosted.org/packages/ea/0d/37fa73f3b43ebd8259f91ae9102a15e5a54e65d581e48dea72df3e81d7a4/librt-0.15.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e0d00c708fb2f5822b152429b1ac80a58dbbbc3f6c232c4d13a3f7fcf2ea5b4c", size = 530422, upload-time = "2026-08-07T10:46:50.45Z" }, + { url = "https://files.pythonhosted.org/packages/26/02/e046c6fe7a5881ac34623242192f484426ba8a75595fd18f22c53a3f530f/librt-0.15.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6c6624fe268625869485553dd7cc1daf30d22558215bb2a4ff16f67a9801a31a", size = 534303, upload-time = "2026-08-07T10:46:51.693Z" }, + { url = "https://files.pythonhosted.org/packages/95/32/d5e6d861ab0366f3edf74f887ab0c9eb9f535aaf01d32b80b4f734daa179/librt-0.15.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f56b397858a23dacf35ede366ed2212fdc03a6a57a1ad36468ad6e9dc5fac091", size = 536084, upload-time = "2026-08-07T10:46:52.951Z" }, + { url = "https://files.pythonhosted.org/packages/2a/de/d69d725513fe53fc90c6d7a1f86e4428939bad2fb905b17fe4c18d413dde/librt-0.15.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:4388184646efe2054911c5b00a1077d6d1ee86a95b7e8ba96dc7850a809f3f40", size = 514307, upload-time = "2026-08-07T10:46:54.194Z" }, + { url = "https://files.pythonhosted.org/packages/36/93/f8aded0d6682b4f25820fa86e0690f87f01df9fd7bd09ddb04d9167ad021/librt-0.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:97335f59082f9fe2ce6c2a9cc6433a0114bbb6cd4d5c09dd76c95c68b9f9a8b0", size = 557686, upload-time = "2026-08-07T10:46:55.443Z" }, + { url = "https://files.pythonhosted.org/packages/74/09/ffeb6bdeb6cd862b4272fddc8ad05f938dd25d020ed517e631813917d80a/librt-0.15.0-cp311-cp311-win32.whl", hash = "sha256:83380ffde38062a2e9bb55d83e74474f6614665528b98a6928720fc006dfffbb", size = 104917, upload-time = "2026-08-07T10:46:56.605Z" }, + { url = "https://files.pythonhosted.org/packages/96/28/7e2313a3ffbf0b4de7ba3da58a09e488507b4bd1ea2b5e69378354a23415/librt-0.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:f75720477ee05d509a310e856cacc8d909adc182f7b91193c207bcc26d7ee6db", size = 125886, upload-time = "2026-08-07T10:46:57.729Z" }, + { url = "https://files.pythonhosted.org/packages/39/9e/04b8c3cde014ef255ee785730425268354543acc38902093a40afa0dc164/librt-0.15.0-cp311-cp311-win_arm64.whl", hash = "sha256:256237037a3ab001ae8d9803b2d43562a4c3aa38739843694349e4d5ebb0fd56", size = 111885, upload-time = "2026-08-07T10:46:58.787Z" }, + { url = "https://files.pythonhosted.org/packages/ba/39/99c25030e782bdfb7a21be8c05254806a2e4bbb05c8d50c2a2130acbfa05/librt-0.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e87bc679f86a99aa3b26e3c78eeb821a247c9a28eae48eaafcc32c3bf4c3bb9e", size = 151021, upload-time = "2026-08-07T10:47:00.057Z" }, + { url = "https://files.pythonhosted.org/packages/14/43/f4b1bd1b2888798a1409808889a25ea1ba49eaabce7d681ed27734c2df9d/librt-0.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:71599e011ac880e8e45d46047d714871894c7d4ab6f25626f8d4f89da21f368d", size = 155267, upload-time = "2026-08-07T10:47:01.311Z" }, + { url = "https://files.pythonhosted.org/packages/0c/db/3ad9c965c72f1e1d6beeec44ec10a54e17be8ae042fbb4baade16cbadced/librt-0.15.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c802434092b769b1d613ed2e13fac15fbfce1934a74bd10283b03c0fae231cd1", size = 503136, upload-time = "2026-08-07T10:47:02.45Z" }, + { url = "https://files.pythonhosted.org/packages/4b/07/5888a6d76acd62ebce66c61b74d94e9370b9c32929f111e487bb6546f8ed/librt-0.15.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5500eeae393a184d14e1f35645962c27129d20c81afa4069e6ef826ebc2b3aaa", size = 496670, upload-time = "2026-08-07T10:47:03.675Z" }, + { url = "https://files.pythonhosted.org/packages/29/39/ab57cc2f5b276156da02bb7f5a8921bada1cb1993ffec99acf811c602c23/librt-0.15.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6ecfc32dfb46fb7b565bcd6abf9412acf978775a998273d22888a6d7953730dd", size = 513688, upload-time = "2026-08-07T10:47:04.981Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b9/bdbb0b648b5c2befb031f4c6f3b1dd857415e8fb492a25a3c764a6681e6c/librt-0.15.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cc46cfd15022e35084355478c9ac809d90b1152222706ac9a7655ec21df6fa", size = 531904, upload-time = "2026-08-07T10:47:06.211Z" }, + { url = "https://files.pythonhosted.org/packages/93/26/473c2e4b6c104e9e58e27ce95fc8005c8bd4fc36cae4f254371125a92db8/librt-0.15.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d5f51401d102c885b9ca509e62c79b1dbff286e1b9b047fde6f763780789356d", size = 524427, upload-time = "2026-08-07T10:47:07.592Z" }, + { url = "https://files.pythonhosted.org/packages/26/60/03b3abb82b41714671b907bf6989b228e31e6a8af52dec82b5b0728dc250/librt-0.15.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cc30523e3f1a23fb7511cc659834a0d01a1042bb9de359bc1c131cc4ec6c9656", size = 543155, upload-time = "2026-08-07T10:47:08.866Z" }, + { url = "https://files.pythonhosted.org/packages/f2/0e/9bb1f0a4affbd0a1888f4f79dc03ed2a299d9a2c26c59ab2a97dcbf11903/librt-0.15.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:59fe030d8ae4a57e3fb7756bf35a858de74e04066fc8555c53d0af979132af81", size = 546890, upload-time = "2026-08-07T10:47:10.327Z" }, + { url = "https://files.pythonhosted.org/packages/dc/84/6937a280d461f7de6e031ffb02edc2b7c3c90d49d630565ce8ff27cbc5f2/librt-0.15.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5a6526a2a956bbb1e4ae3568c82e650fc99119c66bb011ea60715744955a2b4d", size = 555163, upload-time = "2026-08-07T10:47:11.798Z" }, + { url = "https://files.pythonhosted.org/packages/bc/95/2a2853c1ee014bf102116e7f897a04beeaeb2461b45b79af98bdfb95f1ef/librt-0.15.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:85ea21ec6730194d67156b0e0b5430ccb1d61f8b8b907e39b37f9812b74a13f0", size = 535812, upload-time = "2026-08-07T10:47:13.279Z" }, + { url = "https://files.pythonhosted.org/packages/c9/4c/cf9601c1b4c5f09280acd5d83abdb2e68527a2be8257136eb42304218622/librt-0.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1e47b8ba865d7ede071a91a7163073bbaeb72541f1ef8a07d512c45c7b5007f2", size = 573688, upload-time = "2026-08-07T10:47:14.727Z" }, + { url = "https://files.pythonhosted.org/packages/47/6d/9ac7cbec46189a7625af4b5acbd25f10d827f4141b2002181848c8418923/librt-0.15.0-cp312-cp312-win32.whl", hash = "sha256:a5207ec414d1c4a2a7231b2086970dc036f94293cdf338190984958a013a42f1", size = 106138, upload-time = "2026-08-07T10:47:15.973Z" }, + { url = "https://files.pythonhosted.org/packages/38/d0/2ae99c83be86ce23f925ac1aeeedc777e97f427c4a8d190c70d0a16e9a87/librt-0.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:73b30cfa976659b3917c8f6153bdb0591c6a9ec6583599fd24a689b690622022", size = 126974, upload-time = "2026-08-07T10:47:17.049Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ef/dd24f9635c730b86b87587967dda7516b1845e8b17684603d31607fed598/librt-0.15.0-cp312-cp312-win_arm64.whl", hash = "sha256:a54cf9e0ef47b96af580849db5471142200568ce1e02cbf416addab551369570", size = 112292, upload-time = "2026-08-07T10:47:18.222Z" }, + { url = "https://files.pythonhosted.org/packages/e7/42/467b53a601b406ccd7b97c1fd54b59cb34f9185ad5ce7e9d5c3c4e8961c8/librt-0.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:db13ca398005abcbe538deda87b686d9bd08b7001cf40c4c06b444960ae10a26", size = 151029, upload-time = "2026-08-07T10:47:19.312Z" }, + { url = "https://files.pythonhosted.org/packages/3e/e6/36c2299b7a94b84fdd01220d8a777a71be5be0925bb0dbdf71c0a06a34d9/librt-0.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa1f1995789dca3698bc550aaceb09a51bd5df0a057ff84ff15296cd1975b801", size = 155194, upload-time = "2026-08-07T10:47:20.398Z" }, + { url = "https://files.pythonhosted.org/packages/c9/b6/ed5071f9325845e670bd36012757419767fbf56af77ed483077b9e4db541/librt-0.15.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55456ea87d8df21808446d03817be2f65e20391c1c615d9187440dff28cd08dc", size = 502568, upload-time = "2026-08-07T10:47:21.652Z" }, + { url = "https://files.pythonhosted.org/packages/7f/81/6450c67c3615d87704bcbc21323fafc69c799b06a044c447529f725d4b01/librt-0.15.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5a86a5a08c2235316bdb359d5dbb6ce0abfca7fac06363103e2c5af571d92f95", size = 496153, upload-time = "2026-08-07T10:47:22.925Z" }, + { url = "https://files.pythonhosted.org/packages/e1/d6/5f52b722bc75076954b3bfd49be15ea362df4d580c6fb315d0f617100d30/librt-0.15.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e56b6a368529bed262da40ce13f8fef590db0479819cca84f16a1f01ac356d0b", size = 513336, upload-time = "2026-08-07T10:47:24.213Z" }, + { url = "https://files.pythonhosted.org/packages/8d/e2/c08fd1d36ce63ea5a12b85c5d37f4550b5f86a692167e41e5a74222607ae/librt-0.15.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:234d8d394721fa0d786af15ebf1f3fb7f3ed82fd1cd0cde45c2f247b5d4281d2", size = 531661, upload-time = "2026-08-07T10:47:25.507Z" }, + { url = "https://files.pythonhosted.org/packages/3f/d8/d9482fcbeb177b9eb87bb3899eeb3b42be690313c652f9e146b1d0681fb2/librt-0.15.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d8363d7accb0286ac3a0e633f396e93800dafb8150494505daf9515bbda591f3", size = 524487, upload-time = "2026-08-07T10:47:26.79Z" }, + { url = "https://files.pythonhosted.org/packages/10/cc/075171517b41f861753034fbb151b42cfc83bcc853849f24f5e66fd60ccf/librt-0.15.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0f0ee3644d951f31055ad07d77d92520e84505dd7a432cc4cd501dd70ee06785", size = 543201, upload-time = "2026-08-07T10:47:27.999Z" }, + { url = "https://files.pythonhosted.org/packages/b0/03/42c2330f37eeb475b6affeedd06518f60035f323af3a839335e3fc9fef2d/librt-0.15.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2cfd1a81a648806e6a7717be4cc4d1bb392fa229752bf8444ba365e381e984d6", size = 546467, upload-time = "2026-08-07T10:47:29.396Z" }, + { url = "https://files.pythonhosted.org/packages/57/1e/1ad4c5638f7e64d8560328bd25c54b409a661bdb6ff254b38ff90744288d/librt-0.15.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a6cd22c9da0d866558e46a041f1cc0c2bbb26b61b137b2347fa834c332e1d101", size = 555139, upload-time = "2026-08-07T10:47:30.815Z" }, + { url = "https://files.pythonhosted.org/packages/49/41/39fa7d15db1204cd1cbe6514680fbdc243adf754a0885061308f43afc013/librt-0.15.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6d5225ef8801e4ea5e482fa9b5dfb891dd9ef6f6d870f1f25d449ca2c70ac218", size = 536050, upload-time = "2026-08-07T10:47:32.222Z" }, + { url = "https://files.pythonhosted.org/packages/1e/88/c6dcf0dd8e26dc0c9a499a2abab8646c86dcaf9ecea9524cb46d3686331a/librt-0.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d28a05796b99f749bf8794f17ba9ba1612d0076b802e9cfc62c554634e9ce3b", size = 573700, upload-time = "2026-08-07T10:47:33.527Z" }, + { url = "https://files.pythonhosted.org/packages/1b/9b/ab54c71a7918a7c34fa5327fb61390a77446a07a146fbfb1165250a61035/librt-0.15.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:2067ff438048cead9d223ca5675bae2a25e520a7c3e6c1498bf9c6892d22caab", size = 82194, upload-time = "2026-08-07T10:47:34.835Z" }, + { url = "https://files.pythonhosted.org/packages/8d/b2/4f9a243bb892395f3becb80789ade13771701091f9f07ab8230247953ba8/librt-0.15.0-cp313-cp313-win32.whl", hash = "sha256:1cd3b721f24c206398b9e26da3c3a9c011e6e89d06f318ba8ebefc30f1003890", size = 106231, upload-time = "2026-08-07T10:47:36.251Z" }, + { url = "https://files.pythonhosted.org/packages/bf/af/64aff4885a40b93132382f2c314647d722574605416504379184ef3045ea/librt-0.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:f395a4a9a03ac062dbe9a9f82e0c720502e590a38feee6a757bc82e9c63afbd8", size = 126996, upload-time = "2026-08-07T10:47:37.453Z" }, + { url = "https://files.pythonhosted.org/packages/27/83/335bccf6c7cb9028cb0b54aead27d9ece3f01f83bc6baa2abace5da655c1/librt-0.15.0-cp313-cp313-win_arm64.whl", hash = "sha256:0a15cb554761247d84a3ec0cbdf4078d70725384f0e4662c0fa3b26266eb60ad", size = 112188, upload-time = "2026-08-07T10:47:38.729Z" }, +] + +[[package]] +name = "mypy" +version = "1.20.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, + { name = "mypy-extensions" }, + { name = "pathspec" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/af/e3d4b3e9ec91a0ff9aabfdb38692952acf49bbb899c2e4c29acb3a6da3ae/mypy-1.20.2.tar.gz", hash = "sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665", size = 3817349, upload-time = "2026-04-21T17:12:28.473Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/97/ce2502df2cecf2ef997b6c6527c4a223b92feb9e7b790cdc8dcd683f3a8a/mypy-1.20.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cf5a4db6dca263010e2c7bff081c89383c72d187ba2cf4c44759aac970e2f0c4", size = 14457059, upload-time = "2026-04-21T17:06:14.935Z" }, + { url = "https://files.pythonhosted.org/packages/c9/34/417ee60b822cc80c0f3dc9f495ad7fd8dbb8d8b2cf4baf22d4046d25d01d/mypy-1.20.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7b0e817b518bff7facd7f85ea05b643ad8bdcce684cf29784987b0a7c8e1f997", size = 13346816, upload-time = "2026-04-21T17:10:41.433Z" }, + { url = "https://files.pythonhosted.org/packages/4a/85/e20951978702df58379d0bcc2e8f7ccdca4e78cd7dc66dd3ddbf9b29d517/mypy-1.20.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97d7b9a485b40f8ca425460e89bf1da2814625b2da627c0dcc6aa46c92631d14", size = 13772593, upload-time = "2026-04-21T17:08:11.24Z" }, + { url = "https://files.pythonhosted.org/packages/63/a5/5441a13259ec516c56fd5de0fd96a69a9590ae6c5e5d3e5174aa84b97973/mypy-1.20.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1e1c12f6d2db3d78b909b5f77513c11eb7f2dd2782b96a3ab6dffc7d44575c99", size = 14656635, upload-time = "2026-04-21T17:09:54.042Z" }, + { url = "https://files.pythonhosted.org/packages/3b/51/b89c69157c5e1f19fd125a65d991166a26906e7902f026f00feebbcfa2b9/mypy-1.20.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:89dce27e142d25ffbc154c1819383b69f2e9234dc4ed4766f42e0e8cb264ab5c", size = 14943278, upload-time = "2026-04-21T17:09:15.599Z" }, + { url = "https://files.pythonhosted.org/packages/e9/44/6b0eeecfe96d7cce1d71c66b8e03cb304aa70ec11f1955dc1d6b46aca3c3/mypy-1.20.2-cp310-cp310-win_amd64.whl", hash = "sha256:f376e37f9bf2a946872fc5fd1199c99310748e3c26c7a26683f13f8bdb756cbd", size = 10851915, upload-time = "2026-04-21T17:06:03.5Z" }, + { url = "https://files.pythonhosted.org/packages/3c/36/6593dc88545d75fb96416184be5392da5e2a8e8c2802a8597913e16ae25c/mypy-1.20.2-cp310-cp310-win_arm64.whl", hash = "sha256:6e2b469efd811707bc530fd1effef0f5d6eebcb7fe376affae69025da4b979a2", size = 9786676, upload-time = "2026-04-21T17:07:02.035Z" }, + { url = "https://files.pythonhosted.org/packages/1f/4d/9ebeae211caccbdaddde7ed5e31dfcf57faac66be9b11deb1dc6526c8078/mypy-1.20.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4077797a273e56e8843d001e9dfe4ba10e33323d6ade647ff260e5cd97d9758c", size = 14371307, upload-time = "2026-04-21T17:08:56.442Z" }, + { url = "https://files.pythonhosted.org/packages/95/d7/93473d34b61f04fac1aecc01368485c89c5c4af7a4b9a0cab5d77d04b63f/mypy-1.20.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cdecf62abcc4292500d7858aeae87a1f8f1150f4c4dd08fb0b336ee79b2a6df3", size = 13258917, upload-time = "2026-04-21T17:05:50.978Z" }, + { url = "https://files.pythonhosted.org/packages/e2/30/3dd903e8bafb7b5f7bf87fcd58f8382086dea2aa19f0a7b357f21f63071b/mypy-1.20.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c566c3a88b6ece59b3d70f65bedef17304f48eb52ff040a6a18214e1917b3254", size = 13700516, upload-time = "2026-04-21T17:11:33.161Z" }, + { url = "https://files.pythonhosted.org/packages/07/05/c61a140aba4c729ac7bc99ae26fc627c78a6e08f5b9dd319244ea71a3d7e/mypy-1.20.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0deb80d062b2479f2c87ae568f89845afc71d11bc41b04179e58165fd9f31e98", size = 14562889, upload-time = "2026-04-21T17:05:27.674Z" }, + { url = "https://files.pythonhosted.org/packages/fd/87/da78243742ffa8a36d98c3010f0d829f93d5da4e6786f1a1a6f2ad616502/mypy-1.20.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bba9ad231e92a3e424b3e56b65aa17704993425bba97e302c832f9466bb85bac", size = 14803844, upload-time = "2026-04-21T17:10:06.2Z" }, + { url = "https://files.pythonhosted.org/packages/37/52/10a1ddf91b40f843943a3c6db51e2df59c9e237f29d355e95eaab427461f/mypy-1.20.2-cp311-cp311-win_amd64.whl", hash = "sha256:baf593f2765fa3a6b1ef95807dbaa3d25b594f6a52adcc506a6b9cb115e1be67", size = 10846300, upload-time = "2026-04-21T17:12:23.886Z" }, + { url = "https://files.pythonhosted.org/packages/20/02/f9a4415b664c53bd34d6709be59da303abcae986dc4ac847b402edb6fa1e/mypy-1.20.2-cp311-cp311-win_arm64.whl", hash = "sha256:20175a1c0f49863946ec20b7f63255768058ac4f07d2b9ded6a6b46cfb5a9100", size = 9779498, upload-time = "2026-04-21T17:09:23.695Z" }, + { url = "https://files.pythonhosted.org/packages/71/4e/7560e4528db9e9b147e4c0f22660466bf30a0a1fe3d63d1b9d3b0fd354ee/mypy-1.20.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4dbfcf869f6b0517f70cf0030ba6ea1d6645e132337a7d5204a18d8d5636c02b", size = 14539393, upload-time = "2026-04-21T17:07:12.52Z" }, + { url = "https://files.pythonhosted.org/packages/32/d9/34a5efed8124f5a9234f55ac6a4ced4201e2c5b81e1109c49ad23190ec8c/mypy-1.20.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b6481b228d072315b053210b01ac320e1be243dc17f9e5887ef167f23f5fae4", size = 13361642, upload-time = "2026-04-21T17:06:53.742Z" }, + { url = "https://files.pythonhosted.org/packages/d1/14/eb377acf78c03c92d566a1510cda8137348215b5335085ef662ab82ecd3a/mypy-1.20.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:34397cdced6b90b836e38182076049fdb41424322e0b0728c946b0939ebdf9f6", size = 13740347, upload-time = "2026-04-21T17:12:04.73Z" }, + { url = "https://files.pythonhosted.org/packages/b9/94/7e4634a32b641aa1c112422eed1bbece61ee16205f674190e8b536f884de/mypy-1.20.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5da6976f20cae27059ea8d0c86e7cef3de720e04c4bb9ee18e3690fdb792066", size = 14734042, upload-time = "2026-04-21T17:07:43.16Z" }, + { url = "https://files.pythonhosted.org/packages/7a/f3/f7e62395cb7f434541b4491a01149a4439e28ace4c0c632bbf5431e92d1f/mypy-1.20.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:56908d7e08318d39f85b1f0c6cfd47b0cac1a130da677630dac0de3e0623e102", size = 14964958, upload-time = "2026-04-21T17:11:00.665Z" }, + { url = "https://files.pythonhosted.org/packages/3e/0d/47e3c3a0ec2a876e35aeac365df3cac7776c36bbd4ed18cc521e1b9d255b/mypy-1.20.2-cp312-cp312-win_amd64.whl", hash = "sha256:d52ad8d78522da1d308789df651ee5379088e77c76cb1994858d40a426b343b9", size = 10911340, upload-time = "2026-04-21T17:10:49.179Z" }, + { url = "https://files.pythonhosted.org/packages/d6/b2/6c852d72e0ea8b01f49da817fb52539993cde327e7d010e0103dc12d0dac/mypy-1.20.2-cp312-cp312-win_arm64.whl", hash = "sha256:785b08db19c9f214dc37d65f7c165d19a30fcecb48abfa30f31b01b5acaabb58", size = 9833947, upload-time = "2026-04-21T17:09:05.267Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c4/b93812d3a192c9bcf5df405bd2f30277cd0e48106a14d1023c7f6ed6e39b/mypy-1.20.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:edfbfca868cdd6bd8d974a60f8a3682f5565d3f5c99b327640cedd24c4264026", size = 14524670, upload-time = "2026-04-21T17:10:30.737Z" }, + { url = "https://files.pythonhosted.org/packages/f3/47/42c122501bff18eaf1e8f457f5c017933452d8acdc52918a9f59f6812955/mypy-1.20.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e2877a02380adfcdbc69071a0f74d6e9dbbf593c0dc9d174e1f223ffd5281943", size = 13336218, upload-time = "2026-04-21T17:08:44.069Z" }, + { url = "https://files.pythonhosted.org/packages/92/8f/75bbc92f41725fbd585fb17b440b1119b576105df1013622983e18640a93/mypy-1.20.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7488448de6007cd5177c6cea0517ac33b4c0f5ee9b5e9f2be51ce75511a85517", size = 13724906, upload-time = "2026-04-21T17:08:01.02Z" }, + { url = "https://files.pythonhosted.org/packages/a1/32/4c49da27a606167391ff0c39aa955707a00edc500572e562f7c36c08a71f/mypy-1.20.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bb9c2fa06887e21d6a3a868762acb82aec34e2c6fd0174064f27c93ede68ad15", size = 14726046, upload-time = "2026-04-21T17:11:22.354Z" }, + { url = "https://files.pythonhosted.org/packages/7f/fc/4e354a1bd70216359deb0c9c54847ee6b32ef78dfb09f5131ff99b494078/mypy-1.20.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d56a78b646f2e3daa865bc70cd5ec5a46c50045801ca8ff17a0c43abc97e3ee", size = 14955587, upload-time = "2026-04-21T17:12:16.033Z" }, + { url = "https://files.pythonhosted.org/packages/62/b2/c0f2056e9eb8f08c62cafd9715e4584b89132bdc832fcf85d27d07b5f3e5/mypy-1.20.2-cp313-cp313-win_amd64.whl", hash = "sha256:2a4102b03bb7481d9a91a6da8d174740c9c8c4401024684b9ca3b7cc5e49852f", size = 10922681, upload-time = "2026-04-21T17:06:35.842Z" }, + { url = "https://files.pythonhosted.org/packages/e5/14/065e333721f05de8ef683d0aa804c23026bcc287446b61cac657b902ccac/mypy-1.20.2-cp313-cp313-win_arm64.whl", hash = "sha256:a95a9248b0c6fd933a442c03c3b113c3b61320086b88e2c444676d3fd1ca3330", size = 9830560, upload-time = "2026-04-21T17:07:51.023Z" }, + { url = "https://files.pythonhosted.org/packages/28/9a/f23c163e25b11074188251b0b5a0342625fc1cdb6af604757174fa9acc9b/mypy-1.20.2-py3-none-any.whl", hash = "sha256:a94c5a76ab46c5e6257c7972b6c8cff0574201ca7dc05647e33e795d78680563", size = 2637314, upload-time = "2026-04-21T17:05:54.5Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "packaging" +version = "26.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" }, +] + +[[package]] +name = "pathspec" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pygments" +version = "2.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" }, +] + +[[package]] +name = "pytest" +version = "8.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, +] + +[[package]] +name = "ruff" +version = "0.16.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/78/449cb84790bd5cc3823b2652ee405a4558856e5c4195aee3a16bf7b3eb5d/ruff-0.16.8.tar.gz", hash = "sha256:9247bf92b5f04d825c8639a4fe423ec2e4222acd9222e58412b0dab7e442798b", size = 4938814, upload-time = "2026-09-16T15:54:46.688Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/25/6071aabc530e9be7e2c195e8fe3f7aea2735405b6cf447212832d7811831/ruff-0.16.8-py3-none-linux_armv6l.whl", hash = "sha256:6ffbd6d87383c1edf5f6fa890f10200950240d7c1a16052a19a09d3a2307dd38", size = 10048966, upload-time = "2026-09-16T15:53:57.605Z" }, + { url = "https://files.pythonhosted.org/packages/54/98/07f90ecbc74dd5fb5764f11f2bc774d6a7cffef92d2ff5f5b4e9e23c754e/ruff-0.16.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:42ed6b878ed61e3acca92f2730a17acff39286944ea82398544696366a6f925e", size = 10165498, upload-time = "2026-09-16T15:54:01.14Z" }, + { url = "https://files.pythonhosted.org/packages/fe/1f/e6a712e3b47cad4a40600134105ed193cb773f618a42eb7ba323cb812cc0/ruff-0.16.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7ea781c7f2afba8c6a505ea0fb3f994020249e0c450635f5381286fea6b46170", size = 9830004, upload-time = "2026-09-16T15:54:03.998Z" }, + { url = "https://files.pythonhosted.org/packages/23/f2/311a08776d75d81c7676e20b6b020ae63cbe881fcdc7a8dd64e6e18bdd93/ruff-0.16.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8efeae3bbe414a5efefda11a792dfb51ef90ac48d50c4830de2f644caf3e8659", size = 9986558, upload-time = "2026-09-16T15:54:06.804Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ed/37b6cb3d3ba8c73e68ae3eb1d502383beb5aa05a582bb7bb3a922f929f54/ruff-0.16.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a79b795469fef7fc6e908b218eed2eb17332afd85031db6480dc864560e69b2", size = 9877332, upload-time = "2026-09-16T15:54:09.552Z" }, + { url = "https://files.pythonhosted.org/packages/22/cc/40873a8f36ad084cc540d55fcca7077264d5b13b24659e9180c176fb2b08/ruff-0.16.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fdc5563cdc50555e6fba39322850860e9267c1b3d12c26a74729d8604c3c812", size = 10507125, upload-time = "2026-09-16T15:54:12.152Z" }, + { url = "https://files.pythonhosted.org/packages/c3/e4/fc91a642b78ccbab6b9477720f3644ae7a10a9bcce69a934679cd64f62bc/ruff-0.16.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34508983c70665578dab88f5223d8e6228307e1135398ca8bfc8b7e9501e282b", size = 11336694, upload-time = "2026-09-16T15:54:15.489Z" }, + { url = "https://files.pythonhosted.org/packages/c2/3d/bbd2a9a600a4e73dc3e7548a249c8d1671273464b55822c6fae50f602dff/ruff-0.16.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:644bb578569e0ffc575741232bd385dacdd6fbe123f1a729e7a225f54aa3957f", size = 10774448, upload-time = "2026-09-16T15:54:18.16Z" }, + { url = "https://files.pythonhosted.org/packages/1a/41/d83af9879a7b6e8bf5fe16b1da0b134049d2f5d3afac12defb0897cb84bd/ruff-0.16.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15e7d226246961db9235098333caa13063906d3851136b84c2900b82f5daa1df", size = 10323796, upload-time = "2026-09-16T15:54:20.743Z" }, + { url = "https://files.pythonhosted.org/packages/f5/2c/cefd07bfe914b84943ea769ade8d607bd22750b965d3228eefd7cebd15d0/ruff-0.16.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a2bf6bc3e9ebdd4449abc6f06cf64b98051a2c61cf94d2fe9596518c881f1a1e", size = 10514115, upload-time = "2026-09-16T15:54:23.497Z" }, + { url = "https://files.pythonhosted.org/packages/f3/9d/76a2e26c79a23be6e6e3664c57bec9e9fc8de155cfb9e4b67ea91b64f9d7/ruff-0.16.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6ca111ba0849539165e9e59d2b442542f3c1e8060ebbdea82494f1ffbccb1e1f", size = 10072582, upload-time = "2026-09-16T15:54:26.185Z" }, + { url = "https://files.pythonhosted.org/packages/2e/d4/f42edddb39668af1a559ceafa3823aedd65633a48dc9768e775485faa2c1/ruff-0.16.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:359a1e5b495448ee1e91018064382ebc86f90e8aac2fed222c7d0e4e8df85fd2", size = 9879644, upload-time = "2026-09-16T15:54:29.278Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d4/913e3195d95e0378786c6656945c865f534a3560e29139da4882aff630d1/ruff-0.16.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:59e8f5681349474110b24d62e93cfda6593f5fa3473446ca3705200cac1a08b9", size = 10231569, upload-time = "2026-09-16T15:54:32.036Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c4/8aa6ea0bdcedbd1bf87397e2fc4ed8406448ea5842f8660bc6e5f163039d/ruff-0.16.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:efa3e7a16d1baaa79957888dfdf8be9ef2e44db81cb032af06d76632ab59e773", size = 10663666, upload-time = "2026-09-16T15:54:34.838Z" }, + { url = "https://files.pythonhosted.org/packages/3d/02/7f10ef4700bc223c30a3fdd10631a29830c45524b810a3c7ed947af64591/ruff-0.16.8-py3-none-win32.whl", hash = "sha256:55793ba85c69921e89be061426d91a78652d6e50317c962240922747a4eb713f", size = 10093472, upload-time = "2026-09-16T15:54:37.47Z" }, + { url = "https://files.pythonhosted.org/packages/1e/5d/a509c07d714b6da88f2c518b4637cf6f1d46b074be8f0f1e5fb9ff5126fe/ruff-0.16.8-py3-none-win_amd64.whl", hash = "sha256:a6b85621fd3c81e31fc5f5add09c9c078b430db3595ca632efafdec9e64ebfaa", size = 10586899, upload-time = "2026-09-16T15:54:40.488Z" }, + { url = "https://files.pythonhosted.org/packages/fe/a0/50787329e4f20bf9dc9f6230015d46ec69c51a97ace5bc202dae4755365d/ruff-0.16.8-py3-none-win_arm64.whl", hash = "sha256:d075e820af612102ce217f07cc93e69f9490b10ec13ea85fa87bd03d996cef8a", size = 10386316, upload-time = "2026-09-16T15:54:43.332Z" }, +] + +[[package]] +name = "sortedcontainers" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, +] + +[[package]] +name = "tomli" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" }, + { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" }, + { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" }, + { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" }, + { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" }, + { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" }, + { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" }, + { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" }, + { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" }, + { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" }, + { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" }, + { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" }, + { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" }, + { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" }, + { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" }, + { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" }, + { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" }, + { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" }, + { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" }, + { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" }, + { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, +] diff --git a/plugins/capability/darrow-verification/skills/verify-change/evals/README.md b/plugins/capability/darrow-verification/skills/verify-change/evals/README.md index f69aaea5..80062cdd 100644 --- a/plugins/capability/darrow-verification/skills/verify-change/evals/README.md +++ b/plugins/capability/darrow-verification/skills/verify-change/evals/README.md @@ -20,9 +20,10 @@ Cases use semantic output checks for conclusions and repository checks for effects. Fixture provider templates are inert source files and become skills only in isolated repositories. The real-review case mounts the existing plugin and requires its own canonical evidence, independently from the replacement -fixtures. The report renderer has a separate Bash contract test for body -preservation, absolute references, special paths and unavailable input refusal. -Run that test through the authoring skill's version-aware shell matrix helper. +fixtures. The report renderer has focused example and generated Python tests for +byte preservation, validation order, absolute references, special-path escaping +and malformed or unavailable input refusal. Fresh copied-plugin tests exercise +the locked runtime entrypoint on Linux, macOS and native Windows. Run individual cases sequentially with `--trials 1 --jobs 1`, classify the first failure before continuing, and retain host/model/effort/threshold and absolute diff --git a/plugins/capability/darrow-verification/skills/verify-change/evals/criterion-gap.yaml b/plugins/capability/darrow-verification/skills/verify-change/evals/criterion-gap.yaml index e8706585..3ffa55a8 100644 --- a/plugins/capability/darrow-verification/skills/verify-change/evals/criterion-gap.yaml +++ b/plugins/capability/darrow-verification/skills/verify-change/evals/criterion-gap.yaml @@ -52,8 +52,7 @@ checks: run: | report=$(cd .git && pwd -P)/inspect-candidate-report.md last=$(awk 'NF { line=$0 } END { print line }' .git/last-message.md) - test "$last" = "Complete provider result: [report](<$report>)" || - test "$last" = "Complete provider result: [report]($report)" + test "$last" = "Complete provider result: [report](<$report>)" - name: complete replacement evidence retained and referenced run: test -s .git/inspect-candidate-report.md && report=$(cd .git && pwd -P)/inspect-candidate-report.md && grep -F "$report" .git/last-message.md diff --git a/plugins/capability/darrow-verification/skills/verify-change/evals/existing-review.yaml b/plugins/capability/darrow-verification/skills/verify-change/evals/existing-review.yaml index 70051e5e..a674726f 100644 --- a/plugins/capability/darrow-verification/skills/verify-change/evals/existing-review.yaml +++ b/plugins/capability/darrow-verification/skills/verify-change/evals/existing-review.yaml @@ -54,8 +54,7 @@ checks: test -n "$record" || exit 1 report=$(cd "$(dirname "$record")" && pwd -P)/review.md last=$(awk 'NF { line=$0 } END { print line }' .git/last-message.md) - test "$last" = "Complete provider result: [report](<$report>)" || - test "$last" = "Complete provider result: [report]($report)" + test "$last" = "Complete provider result: [report](<$report>)" - name: existing provider retains a canonical failing current review run: record=$(find .git -name result.tsv -type f) && test -n "$record" && test diff --git a/plugins/capability/darrow-verification/skills/verify-change/evals/followup-clear.yaml b/plugins/capability/darrow-verification/skills/verify-change/evals/followup-clear.yaml index 717df799..170a90d4 100644 --- a/plugins/capability/darrow-verification/skills/verify-change/evals/followup-clear.yaml +++ b/plugins/capability/darrow-verification/skills/verify-change/evals/followup-clear.yaml @@ -70,8 +70,7 @@ checks: test -e .git/inspect-candidate-report.md || exit 0 report=$(cd .git && pwd -P)/inspect-candidate-report.md last=$(awk 'NF { line=$0 } END { print line }' .git/last-message.md) - test "$last" = "Complete provider result: [report](<$report>)" || - test "$last" = "Complete provider result: [report]($report)" + test "$last" = "Complete provider result: [report](<$report>)" - name: complete replacement evidence retained and referenced run: test -s .git/inspect-candidate-report.md && report=$(cd .git && pwd -P)/inspect-candidate-report.md && grep -F "$report" .git/last-message.md diff --git a/plugins/capability/darrow-verification/skills/verify-change/evals/followup-no-progress.yaml b/plugins/capability/darrow-verification/skills/verify-change/evals/followup-no-progress.yaml index 843b630e..3785c953 100644 --- a/plugins/capability/darrow-verification/skills/verify-change/evals/followup-no-progress.yaml +++ b/plugins/capability/darrow-verification/skills/verify-change/evals/followup-no-progress.yaml @@ -70,8 +70,7 @@ checks: test -e .git/inspect-candidate-report.md || exit 0 report=$(cd .git && pwd -P)/inspect-candidate-report.md last=$(awk 'NF { line=$0 } END { print line }' .git/last-message.md) - test "$last" = "Complete provider result: [report](<$report>)" || - test "$last" = "Complete provider result: [report]($report)" + test "$last" = "Complete provider result: [report](<$report>)" - name: complete replacement evidence retained and referenced run: test -s .git/inspect-candidate-report.md && report=$(cd .git && pwd -P)/inspect-candidate-report.md && grep -F "$report" .git/last-message.md diff --git a/plugins/capability/darrow-verification/skills/verify-change/evals/followup-progress.yaml b/plugins/capability/darrow-verification/skills/verify-change/evals/followup-progress.yaml index cf750180..ba135a63 100644 --- a/plugins/capability/darrow-verification/skills/verify-change/evals/followup-progress.yaml +++ b/plugins/capability/darrow-verification/skills/verify-change/evals/followup-progress.yaml @@ -70,8 +70,7 @@ checks: test -e .git/inspect-candidate-report.md || exit 0 report=$(cd .git && pwd -P)/inspect-candidate-report.md last=$(awk 'NF { line=$0 } END { print line }' .git/last-message.md) - test "$last" = "Complete provider result: [report](<$report>)" || - test "$last" = "Complete provider result: [report]($report)" + test "$last" = "Complete provider result: [report](<$report>)" - name: complete replacement evidence retained and referenced run: test -s .git/inspect-candidate-report.md && report=$(cd .git && pwd -P)/inspect-candidate-report.md && grep -F "$report" .git/last-message.md diff --git a/plugins/capability/darrow-verification/skills/verify-change/evals/followup-regression.yaml b/plugins/capability/darrow-verification/skills/verify-change/evals/followup-regression.yaml index 7955af14..802b5107 100644 --- a/plugins/capability/darrow-verification/skills/verify-change/evals/followup-regression.yaml +++ b/plugins/capability/darrow-verification/skills/verify-change/evals/followup-regression.yaml @@ -73,8 +73,7 @@ checks: test -e .git/inspect-candidate-report.md || exit 0 report=$(cd .git && pwd -P)/inspect-candidate-report.md last=$(awk 'NF { line=$0 } END { print line }' .git/last-message.md) - test "$last" = "Complete provider result: [report](<$report>)" || - test "$last" = "Complete provider result: [report]($report)" + test "$last" = "Complete provider result: [report](<$report>)" - name: complete replacement evidence retained and referenced run: test -s .git/inspect-candidate-report.md && report=$(cd .git && pwd -P)/inspect-candidate-report.md && grep -F "$report" .git/last-message.md diff --git a/plugins/capability/darrow-verification/skills/verify-change/evals/indirect.yaml b/plugins/capability/darrow-verification/skills/verify-change/evals/indirect.yaml index ba2e12a4..1ff79755 100644 --- a/plugins/capability/darrow-verification/skills/verify-change/evals/indirect.yaml +++ b/plugins/capability/darrow-verification/skills/verify-change/evals/indirect.yaml @@ -53,8 +53,7 @@ checks: test -e .git/inspect-candidate-report.md || exit 0 report=$(cd .git && pwd -P)/inspect-candidate-report.md last=$(awk 'NF { line=$0 } END { print line }' .git/last-message.md) - test "$last" = "Complete provider result: [report](<$report>)" || - test "$last" = "Complete provider result: [report]($report)" + test "$last" = "Complete provider result: [report](<$report>)" - name: complete replacement evidence retained and referenced run: test -s .git/inspect-candidate-report.md && report=$(cd .git && pwd -P)/inspect-candidate-report.md && grep -F "$report" .git/last-message.md diff --git a/plugins/capability/darrow-verification/skills/verify-change/evals/replacement.yaml b/plugins/capability/darrow-verification/skills/verify-change/evals/replacement.yaml index 59ab1724..e64ed574 100644 --- a/plugins/capability/darrow-verification/skills/verify-change/evals/replacement.yaml +++ b/plugins/capability/darrow-verification/skills/verify-change/evals/replacement.yaml @@ -54,8 +54,7 @@ checks: test -e .git/inspect-candidate-report.md || exit 0 report=$(cd .git && pwd -P)/inspect-candidate-report.md last=$(awk 'NF { line=$0 } END { print line }' .git/last-message.md) - test "$last" = "Complete provider result: [report](<$report>)" || - test "$last" = "Complete provider result: [report]($report)" + test "$last" = "Complete provider result: [report](<$report>)" - name: complete replacement evidence retained and referenced run: test -s .git/inspect-candidate-report.md && report=$(cd .git && pwd -P)/inspect-candidate-report.md && grep -F "$report" .git/last-message.md diff --git a/plugins/capability/darrow-verification/skills/verify-change/evals/required-check-unavailable.yaml b/plugins/capability/darrow-verification/skills/verify-change/evals/required-check-unavailable.yaml index 8467778b..6d41180c 100644 --- a/plugins/capability/darrow-verification/skills/verify-change/evals/required-check-unavailable.yaml +++ b/plugins/capability/darrow-verification/skills/verify-change/evals/required-check-unavailable.yaml @@ -43,8 +43,7 @@ checks: test -e .git/inspect-candidate-report.md || exit 0 report=$(cd .git && pwd -P)/inspect-candidate-report.md last=$(awk 'NF { line=$0 } END { print line }' .git/last-message.md) - test "$last" = "Complete provider result: [report](<$report>)" || - test "$last" = "Complete provider result: [report]($report)" + test "$last" = "Complete provider result: [report](<$report>)" semantic_output_checks: - name: unavailable required check blocks proposition: diff --git a/plugins/capability/darrow-verification/skills/verify-change/evals/required-extra.yaml b/plugins/capability/darrow-verification/skills/verify-change/evals/required-extra.yaml index 24f1265d..69206f89 100644 --- a/plugins/capability/darrow-verification/skills/verify-change/evals/required-extra.yaml +++ b/plugins/capability/darrow-verification/skills/verify-change/evals/required-extra.yaml @@ -53,8 +53,7 @@ checks: test -e .git/inspect-candidate-report.md || exit 0 report=$(cd .git && pwd -P)/inspect-candidate-report.md last=$(awk 'NF { line=$0 } END { print line }' .git/last-message.md) - test "$last" = "Complete provider result: [report](<$report>)" || - test "$last" = "Complete provider result: [report]($report)" + test "$last" = "Complete provider result: [report](<$report>)" semantic_output_checks: - name: selected unsupported requirement cannot disappear proposition: The result is blocked because required QA is unsupported or diff --git a/plugins/capability/darrow-verification/skills/verify-change/scripts/render-assessment b/plugins/capability/darrow-verification/skills/verify-change/scripts/render-assessment deleted file mode 100755 index 9864f8c2..00000000 --- a/plugins/capability/darrow-verification/skills/verify-change/scripts/render-assessment +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -fail() { - printf 'render-assessment: %s\n' "$1" >&2 - exit 2 -} - -[ "$#" -eq 4 ] && [ "$1" = --assessment ] && [ "$3" = --provider-report ] || - fail 'usage: render-assessment --assessment ABSOLUTE_FILE --provider-report ABSOLUTE_FILE' - -canonical_input() { - local input=$1 label=$2 parent name - case "$input" in - /*) ;; - *) fail "$label requires an absolute path" ;; - esac - case "$input" in - *$'\n'*) fail "$label path contains a control character" ;; - esac - if LC_ALL=C grep -q '[[:cntrl:]]' <<<"$input"; then - fail "$label path contains a control character" - fi - [ -f "$input" ] && [ -r "$input" ] && [ -s "$input" ] || - fail "$label is not a readable nonempty regular file: $input" - parent=${input%/*} - name=${input##*/} - [ -n "$parent" ] || parent=/ - parent=$(cd "$parent" && pwd -P) || fail "cannot resolve $label parent: $input" - case "$parent" in - /) printf '/%s\n' "$name" ;; - *) printf '%s/%s\n' "$parent" "$name" ;; - esac -} - -assessment=$(canonical_input "$2" assessment) -provider_report=$(canonical_input "$4" provider-report) -# The angle-delimited Markdown destination allows spaces and parentheses; -# encode URI delimiters and backslashes without parsing provider contents. -destination=$(printf '%s\n' "$provider_report" | LC_ALL=C sed \ - -e 's/%/%25/g' -e 's/ /%20/g' -e 's/#/%23/g' -e 's/?/%3F/g' \ - -e 's//%3E/g' -e 's/\\/%5C/g') - -cat "$assessment" -printf '\n\nComplete provider result: [report](<%s>)\n' "$destination" diff --git a/plugins/capability/darrow-verification/skills/verify-change/scripts/render-assessment.test.sh b/plugins/capability/darrow-verification/skills/verify-change/scripts/render-assessment.test.sh deleted file mode 100755 index 27148674..00000000 --- a/plugins/capability/darrow-verification/skills/verify-change/scripts/render-assessment.test.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -script_dir=$(cd "$(dirname "$0")" && pwd -P) -renderer=$script_dir/render-assessment -temp_parent=${TMPDIR:-/tmp} -case "$temp_parent" in - /) template=/verification-render-test.XXXXXX ;; - */) template=${temp_parent}verification-render-test.XXXXXX ;; - *) template=$temp_parent/verification-render-test.XXXXXX ;; -esac -test_dir=$(mktemp -d "$template") -test_dir=$(cd "$test_dir" && pwd -P) -trap 'rm -rf "$test_dir"' EXIT - -mkdir "$test_dir/elsewhere" "$test_dir/bin" -printf '#!/bin/sh\nexit 97\n' >"$test_dir/bin/bash" -chmod +x "$test_dir/bin/bash" -export PATH="$test_dir/bin:$PATH" -cd "$test_dir/elsewhere" - -printf 'Conclusion: progress.\nF1 remains blocking; A1 is advisory.\n' >"$test_dir/assessment.md" -printf 'Complete provider report, unchanged.\n' >"$test_dir/report.md" -cp "$test_dir/report.md" "$test_dir/report-before.md" -"$BASH" "$renderer" --assessment "$test_dir/assessment.md" --provider-report "$test_dir/report.md" >"$test_dir/output.md" -body_bytes=$(wc -c <"$test_dir/assessment.md") -dd if="$test_dir/output.md" of="$test_dir/prefix.md" bs=1 count="$body_bytes" 2>/dev/null -cmp "$test_dir/assessment.md" "$test_dir/prefix.md" -grep -F "Complete provider result: [report](<$test_dir/report.md>)" "$test_dir/output.md" -cmp "$test_dir/report-before.md" "$test_dir/report.md" -ln -s "$test_dir" "$test_dir/alias" -"$BASH" "$renderer" --assessment "$test_dir/assessment.md" --provider-report "$test_dir/alias/report.md" >"$test_dir/alias-output.md" -grep -F "Complete provider result: [report](<$test_dir/report.md>)" "$test_dir/alias-output.md" - -odd_name='report [one](two) #%?<>\.md' -cp "$test_dir/report.md" "$test_dir/$odd_name" -"$BASH" "$renderer" --assessment "$test_dir/assessment.md" --provider-report "$test_dir/$odd_name" >"$test_dir/odd-output.md" -grep -F '/report%20[one](two)%20%23%25%3F%3C%3E%5C.md>)' "$test_dir/odd-output.md" - -refuses() { - if "$BASH" "$renderer" "$@" >"$test_dir/refusal-out" 2>"$test_dir/refusal-err"; then - printf 'expected refusal\n' >&2 - exit 1 - fi - test ! -s "$test_dir/refusal-out" - test -s "$test_dir/refusal-err" -} -refuses --assessment relative.md --provider-report "$test_dir/report.md" -refuses --assessment "$test_dir/assessment.md" --provider-report relative.md -refuses --assessment "$test_dir/missing.md" --provider-report "$test_dir/report.md" -refuses --assessment "$test_dir/assessment.md" --provider-report "$test_dir/missing.md" -refuses --assessment "$test_dir/assessment.md" --provider-report "$test_dir/elsewhere" -: >"$test_dir/empty.md" -refuses --assessment "$test_dir/empty.md" --provider-report "$test_dir/report.md" -refuses --assessment "$test_dir/assessment.md" --provider-report "$test_dir/empty.md" -cp "$test_dir/report.md" "$test_dir/"$'bad\npath' -refuses --assessment "$test_dir/assessment.md" --provider-report "$test_dir/"$'bad\npath' -grep -F 'control character' "$test_dir/refusal-err" -refuses --assessment "$test_dir/assessment.md" -refuses --assessment "$test_dir/assessment.md" --provider-report "$test_dir/report.md" extra -printf 'render-assessment contract passed\n' diff --git a/plugins/capability/darrow-verification/tests/fresh-install.test.ps1 b/plugins/capability/darrow-verification/tests/fresh-install.test.ps1 new file mode 100644 index 00000000..57965e80 --- /dev/null +++ b/plugins/capability/darrow-verification/tests/fresh-install.test.ps1 @@ -0,0 +1,135 @@ +$ErrorActionPreference = "Stop" + +$pluginRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path +$fixture = Join-Path ([System.IO.Path]::GetTempPath()) ("darrow verification " + [guid]::NewGuid()) +$copy = Join-Path $fixture "plugin copy" +$utf8 = [System.Text.UTF8Encoding]::new($false) + +function Assert-Bytes([string]$Path, [byte[]]$Expected, [string]$Message) { + $actual = [System.IO.File]::ReadAllBytes($Path) + if ([Convert]::ToBase64String($actual) -ne [Convert]::ToBase64String($Expected)) { + throw $Message + } +} + +function Invoke-Renderer( + [string]$Backend, + [string]$WorkingDirectory, + [string[]]$Arguments, + [string]$StdoutPath, + [string]$StderrPath +) { + $startInfo = [System.Diagnostics.ProcessStartInfo]::new() + $startInfo.FileName = (Get-Command uv).Source + $startInfo.UseShellExecute = $false + $startInfo.RedirectStandardOutput = $true + $startInfo.RedirectStandardError = $true + $startInfo.WorkingDirectory = $WorkingDirectory + foreach ($argument in @( + "run", "--quiet", "--isolated", "--frozen", "--no-dev", "--project", $Backend, + "darrow-render-assessment" + ) + $Arguments) { + [void]$startInfo.ArgumentList.Add($argument) + } + + $process = [System.Diagnostics.Process]::new() + $process.StartInfo = $startInfo + $stdoutStream = $null + $stderrStream = $null + try { + if (-not $process.Start()) { throw "renderer process did not start" } + $stdoutStream = [System.IO.File]::Open( + $StdoutPath, + [System.IO.FileMode]::Create, + [System.IO.FileAccess]::Write, + [System.IO.FileShare]::None + ) + $stderrStream = [System.IO.File]::Open( + $StderrPath, + [System.IO.FileMode]::Create, + [System.IO.FileAccess]::Write, + [System.IO.FileShare]::None + ) + $stdoutCopy = $process.StandardOutput.BaseStream.CopyToAsync($stdoutStream) + $stderrCopy = $process.StandardError.BaseStream.CopyToAsync($stderrStream) + $process.WaitForExit() + [System.Threading.Tasks.Task]::WaitAll( + [System.Threading.Tasks.Task[]]@($stdoutCopy, $stderrCopy) + ) + return $process.ExitCode + } + finally { + if ($null -ne $stdoutStream) { $stdoutStream.Dispose() } + if ($null -ne $stderrStream) { $stderrStream.Dispose() } + $process.Dispose() + } +} + +try { + New-Item -ItemType Directory -Path $fixture | Out-Null + Copy-Item -LiteralPath $pluginRoot -Destination $copy -Recurse + $backend = Join-Path $copy "skills/verify-change/backend" + foreach ($generatedDirectory in @(".venv", ".hypothesis", ".mypy_cache", ".pytest_cache", ".ruff_cache")) { + Remove-Item -LiteralPath (Join-Path $backend $generatedDirectory) -Recurse -Force -ErrorAction SilentlyContinue + } + Get-ChildItem -LiteralPath $backend -Directory -Recurse -Filter "__pycache__" | + Remove-Item -Recurse -Force + Get-ChildItem -LiteralPath $backend -File -Filter ".coverage*" | + Remove-Item -Force + Remove-Item -LiteralPath (Join-Path $backend "coverage.json") -Force -ErrorAction SilentlyContinue + + & uv sync --locked --no-dev --project $backend + if ($LASTEXITCODE -ne 0) { throw "runtime sync failed" } + $runtimeTree = (& uv tree --locked --no-dev --project $backend) -join "`n" + if ($LASTEXITCODE -ne 0) { throw "runtime tree failed" } + foreach ($tool in @("coverage", "hypothesis", "mypy", "pytest", "ruff")) { + if ($runtimeTree -match "(?m)(^|\s)$tool(\s|$)") { + throw "development dependency installed at runtime: $tool" + } + } + Remove-Item -LiteralPath (Join-Path $backend ".venv") -Recurse -Force + + $outside = Join-Path $fixture "outside" + New-Item -ItemType Directory -Path $outside | Out-Null + $assessment = Join-Path $fixture "assessment.md" + $report = Join-Path $fixture "provider report #%.md" + $stdout = Join-Path $fixture "stdout" + $stderr = Join-Path $fixture "stderr" + $assessmentText = "Conclusion: progress.`n`nF1 remains blocking; A1 is advisory.`n" + [System.IO.File]::WriteAllText($assessment, $assessmentText, $utf8) + [System.IO.File]::WriteAllText($report, "Complete provider report, unchanged.`n", $utf8) + $reportBefore = [System.IO.File]::ReadAllBytes($report) + + $exitCode = Invoke-Renderer $backend $outside @( + "--assessment", $assessment, "--provider-report", $report + ) $stdout $stderr + if ($exitCode -ne 0) { throw "renderer failed" } + $destination = ([System.IO.Path]::GetFullPath($report)).Replace("%", "%25") + $destination = $destination.Replace(" ", "%20").Replace("#", "%23") + $destination = $destination.Replace("?", "%3F").Replace("<", "%3C").Replace(">", "%3E") + $destination = $destination.Replace("\", "%5C") + $expected = $assessmentText + "`n`nComplete provider result: [report](<$destination>)`n" + Assert-Bytes $stdout ($utf8.GetBytes($expected)) "renderer output bytes differed from the public contract" + Assert-Bytes $stderr ([byte[]]@()) "valid renderer invocation wrote stderr" + Assert-Bytes $report $reportBefore "renderer changed the provider report" + + $exitCode = Invoke-Renderer $backend $outside @() $stdout $stderr + if ($exitCode -ne 2) { throw "invalid renderer invocation did not exit 2" } + Assert-Bytes $stdout ([byte[]]@()) "invalid renderer invocation wrote stdout" + $usage = "render-assessment: usage: render-assessment --assessment ABSOLUTE_FILE --provider-report ABSOLUTE_FILE`n" + Assert-Bytes $stderr ($utf8.GetBytes($usage)) "renderer usage bytes differed" + + $exitCode = Invoke-Renderer $backend $outside @( + "--assessment", $assessment, "--provider-report", "relative.md" + ) $stdout $stderr + if ($exitCode -ne 2) { throw "relative report path did not exit 2" } + Assert-Bytes $stdout ([byte[]]@()) "relative report path wrote stdout" + $diagnostic = "render-assessment: provider-report requires an absolute path`n" + Assert-Bytes $stderr ($utf8.GetBytes($diagnostic)) "relative report diagnostic differed" + if (Test-Path -LiteralPath (Join-Path $backend ".venv")) { + throw "renderer wrote a virtual environment into the installed skill" + } +} +finally { + Remove-Item -LiteralPath $fixture -Recurse -Force -ErrorAction SilentlyContinue +} diff --git a/plugins/capability/darrow-verification/tests/fresh-install.test.sh b/plugins/capability/darrow-verification/tests/fresh-install.test.sh new file mode 100755 index 00000000..1bbb0a78 --- /dev/null +++ b/plugins/capability/darrow-verification/tests/fresh-install.test.sh @@ -0,0 +1,97 @@ +#!/bin/sh +set -eu + +plugin_root=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd) +temp_parent=${TMPDIR:-/tmp} +case "$temp_parent" in + /) temp_template=/darrow-verification-install.XXXXXX ;; + */) temp_template=${temp_parent}darrow-verification-install.XXXXXX ;; + *) temp_template=$temp_parent/darrow-verification-install.XXXXXX ;; +esac +fixture=$(mktemp -d "$temp_template") +fixture=$(CDPATH='' cd -- "$fixture" && pwd -P) +trap 'rm -rf "$fixture"' EXIT HUP INT TERM + +copy="$fixture/plugin copy" +mkdir -p "$copy" +cp -R "$plugin_root/." "$copy/" +backend="$copy/skills/verify-change/backend" +rm -rf \ + "$backend/.venv" \ + "$backend/.hypothesis" \ + "$backend/.mypy_cache" \ + "$backend/.pytest_cache" \ + "$backend/.ruff_cache" +rm -f "$backend"/.coverage* "$backend/coverage.json" +find "$backend" -type d -name __pycache__ -prune -exec rm -rf {} \; + +uv sync --locked --no-dev --project "$backend" +runtime_tree=$(uv tree --locked --no-dev --project "$backend") +for development_tool in coverage hypothesis mypy pytest ruff; do + if printf '%s\n' "$runtime_tree" | grep -E "(^|[[:space:]])${development_tool}([[:space:]]|$)" >/dev/null; then + echo "error: development dependency installed at runtime: $development_tool" >&2 + exit 1 + fi +done +rm -rf "$backend/.venv" + +mkdir "$fixture/outside" +assessment="$fixture/assessment.md" +report="$fixture/provider report #%<>.md" +actual="$fixture/actual" +errors="$fixture/errors" +expected="$fixture/expected" +printf 'Conclusion: progress.\n\nF1 remains blocking; A1 is advisory.\n' >"$assessment" +printf 'Complete provider report, unchanged.\n' >"$report" +cp "$report" "$fixture/report-before.md" + +cd "$fixture/outside" +uv run --quiet --isolated --frozen --no-dev --project "$backend" \ + darrow-render-assessment \ + --assessment "$assessment" --provider-report "$report" \ + >"$actual" 2>"$errors" + +canonical_report=$(CDPATH='' cd -- "${report%/*}" && pwd -P)/${report##*/} +destination=$(printf '%s\n' "$canonical_report" | LC_ALL=C sed \ + -e 's/%/%25/g' -e 's/ /%20/g' -e 's/#/%23/g' -e 's/?/%3F/g' \ + -e 's//%3E/g' -e 's/\\/%5C/g') +{ + cat "$assessment" + printf '\n\nComplete provider result: [report](<%s>)\n' "$destination" +} >"$expected" +cmp "$expected" "$actual" +cmp "$fixture/report-before.md" "$report" +test ! -s "$errors" + +status=0 +uv run --quiet --isolated --frozen --no-dev --project "$backend" \ + darrow-render-assessment >"$actual" 2>"$errors" || status=$? +test "$status" -eq 2 +test ! -s "$actual" +test "$(cat "$errors")" = \ + 'render-assessment: usage: render-assessment --assessment ABSOLUTE_FILE --provider-report ABSOLUTE_FILE' + +status=0 +uv run --quiet --isolated --frozen --no-dev --project "$backend" \ + darrow-render-assessment \ + --assessment "$assessment" --provider-report relative.md \ + >"$actual" 2>"$errors" || status=$? +test "$status" -eq 2 +test ! -s "$actual" +test "$(cat "$errors")" = \ + 'render-assessment: provider-report requires an absolute path' + +empty="$fixture/empty.md" +: >"$empty" +status=0 +uv run --quiet --isolated --frozen --no-dev --project "$backend" \ + darrow-render-assessment \ + --assessment "$empty" --provider-report "$report" \ + >"$actual" 2>"$errors" || status=$? +test "$status" -eq 2 +test ! -s "$actual" +test "$(cat "$errors")" = \ + "render-assessment: assessment is not a readable nonempty regular file: $empty" +test ! -e "$backend/.venv" + +printf '%s\n' 'verification fresh install passed' diff --git a/python-packages.txt b/python-packages.txt index 201152d2..2e8c2a26 100644 --- a/python-packages.txt +++ b/python-packages.txt @@ -1,4 +1,5 @@ # Repository-relative Python package roots, one per line. plugins/capability/darrow-discovery/skills/plan-implementation/backend plugins/capability/darrow-observability-langfuse/backend +plugins/capability/darrow-verification/skills/verify-change/backend plugins/foundation/darrow-skill-authoring/skills/author-agent-skill/backend diff --git a/scripts/detect-python-quality-changes b/scripts/detect-python-quality-changes index af5a611d..90cdf8bc 100755 --- a/scripts/detect-python-quality-changes +++ b/scripts/detect-python-quality-changes @@ -52,6 +52,9 @@ append_package() { plugins/capability/darrow-discovery/skills/plan-implementation/backend) discovery=true ;; + plugins/capability/darrow-verification/skills/verify-change/backend) + verification=true + ;; esac } @@ -107,6 +110,7 @@ package_selected=false skill_authoring=false observability_langfuse=false discovery=false +verification=false seen_packages='|' while IFS= read -r package; do @@ -144,3 +148,4 @@ printf 'package_selected=%s\n' "$package_selected" printf 'skill_authoring=%s\n' "$skill_authoring" printf 'observability_langfuse=%s\n' "$observability_langfuse" printf 'discovery=%s\n' "$discovery" +printf 'verification=%s\n' "$verification" diff --git a/scripts/test-python-quality-changes b/scripts/test-python-quality-changes index c57a4b67..a350cdeb 100755 --- a/scripts/test-python-quality-changes +++ b/scripts/test-python-quality-changes @@ -16,6 +16,7 @@ chmod +x "$fixture/scripts/detect-python-quality-changes" cat >"$fixture/python-packages.txt" <<'EOF' plugins/capability/darrow-discovery/skills/plan-implementation/backend plugins/capability/darrow-observability-langfuse/backend +plugins/capability/darrow-verification/skills/verify-change/backend plugins/foundation/darrow-skill-authoring/skills/author-agent-skill/backend EOF printf '%s\n' '# Fixture' >"$fixture/README.md" @@ -46,42 +47,56 @@ expect_scope \ package_selected=false skill_authoring=false observability_langfuse=false -discovery=false' +discovery=false +verification=false' expect_scope \ plugins/foundation/darrow-skill-authoring/README.md \ 'packages=["plugins/foundation/darrow-skill-authoring/skills/author-agent-skill/backend"] package_selected=true skill_authoring=true observability_langfuse=false -discovery=false' +discovery=false +verification=false' expect_scope \ plugins/capability/darrow-observability-langfuse/README.md \ 'packages=["plugins/capability/darrow-observability-langfuse/backend"] package_selected=true skill_authoring=false observability_langfuse=true -discovery=false' +discovery=false +verification=false' expect_scope \ plugins/capability/darrow-discovery/README.md \ 'packages=["plugins/capability/darrow-discovery/skills/plan-implementation/backend"] package_selected=true skill_authoring=false observability_langfuse=false -discovery=true' +discovery=true +verification=false' +expect_scope \ + plugins/capability/darrow-verification/README.md \ + 'packages=["plugins/capability/darrow-verification/skills/verify-change/backend"] +package_selected=true +skill_authoring=false +observability_langfuse=false +discovery=false +verification=true' expect_scope \ scripts/check-python \ - 'packages=["plugins/capability/darrow-discovery/skills/plan-implementation/backend","plugins/capability/darrow-observability-langfuse/backend","plugins/foundation/darrow-skill-authoring/skills/author-agent-skill/backend"] + 'packages=["plugins/capability/darrow-discovery/skills/plan-implementation/backend","plugins/capability/darrow-observability-langfuse/backend","plugins/capability/darrow-verification/skills/verify-change/backend","plugins/foundation/darrow-skill-authoring/skills/author-agent-skill/backend"] package_selected=true skill_authoring=true observability_langfuse=true -discovery=true' +discovery=true +verification=true' all=$(cd "$fixture" && scripts/detect-python-quality-changes --all) -if [ "$all" != 'packages=["plugins/capability/darrow-discovery/skills/plan-implementation/backend","plugins/capability/darrow-observability-langfuse/backend","plugins/foundation/darrow-skill-authoring/skills/author-agent-skill/backend"] +if [ "$all" != 'packages=["plugins/capability/darrow-discovery/skills/plan-implementation/backend","plugins/capability/darrow-observability-langfuse/backend","plugins/capability/darrow-verification/skills/verify-change/backend","plugins/foundation/darrow-skill-authoring/skills/author-agent-skill/backend"] package_selected=true skill_authoring=true observability_langfuse=true -discovery=true' ]; then +discovery=true +verification=true' ]; then printf 'error: --all did not select every Python quality scope\n' >&2 exit 1 fi @@ -102,18 +117,20 @@ if [ "$third" != 'packages=["plugins/capability/darrow-third/backend"] package_selected=true skill_authoring=false observability_langfuse=false -discovery=false' ]; then +discovery=false +verification=false' ]; then printf 'error: registered third package was not selected\nactual:\n%s\n' \ "$third" >&2 exit 1 fi third_all=$(cd "$fixture" && scripts/detect-python-quality-changes --all) -if [ "$third_all" != 'packages=["plugins/capability/darrow-discovery/skills/plan-implementation/backend","plugins/capability/darrow-observability-langfuse/backend","plugins/foundation/darrow-skill-authoring/skills/author-agent-skill/backend","plugins/capability/darrow-third/backend"] +if [ "$third_all" != 'packages=["plugins/capability/darrow-discovery/skills/plan-implementation/backend","plugins/capability/darrow-observability-langfuse/backend","plugins/capability/darrow-verification/skills/verify-change/backend","plugins/foundation/darrow-skill-authoring/skills/author-agent-skill/backend","plugins/capability/darrow-third/backend"] package_selected=true skill_authoring=true observability_langfuse=true -discovery=true' ]; then +discovery=true +verification=true' ]; then printf 'error: --all omitted a registered third package\nactual:\n%s\n' \ "$third_all" >&2 exit 1 @@ -129,24 +146,35 @@ fi true success \ true success success \ false skipped skipped \ + false skipped skipped \ false skipped skipped "$verifier" \ success success \ true success \ false skipped skipped \ true success success \ + false skipped skipped \ false skipped skipped "$verifier" \ success success \ true success \ false skipped skipped \ false skipped skipped \ + true success success \ + false skipped skipped +"$verifier" \ + success success \ + true success \ + false skipped skipped \ + false skipped skipped \ + false skipped skipped \ true success success "$verifier" \ success success \ false skipped \ false skipped skipped \ false skipped skipped \ + false skipped skipped \ false skipped skipped if "$verifier" \ @@ -154,6 +182,7 @@ if "$verifier" \ false success \ false skipped skipped \ false skipped skipped \ + false skipped skipped \ false skipped skipped >/dev/null 2>&1; then printf 'error: aggregate accepted a job that ran for an unchanged scope\n' >&2 exit 1 @@ -164,6 +193,7 @@ if "$verifier" \ true failure \ false skipped skipped \ false skipped skipped \ + false skipped skipped \ false skipped skipped >/dev/null 2>&1; then printf 'error: aggregate accepted a failed registered-package job\n' >&2 exit 1 @@ -174,6 +204,7 @@ if "$verifier" \ true success \ true failure success \ false skipped skipped \ + false skipped skipped \ false skipped skipped >/dev/null 2>&1; then printf 'error: aggregate accepted a failed selected job\n' >&2 exit 1 @@ -184,6 +215,7 @@ if "$verifier" \ true success \ true success success \ true success success \ + true success success \ true success success >/dev/null 2>&1; then printf 'error: aggregate accepted failed change detection\n' >&2 exit 1 diff --git a/scripts/verify-python-quality-results b/scripts/verify-python-quality-results index dd51cfbe..abe286b1 100755 --- a/scripts/verify-python-quality-results +++ b/scripts/verify-python-quality-results @@ -1,8 +1,8 @@ #!/bin/sh set -eu -if [ "$#" -ne 13 ]; then - echo "usage: scripts/verify-python-quality-results " >&2 +if [ "$#" -ne 16 ]; then + echo "usage: scripts/verify-python-quality-results " >&2 exit 2 fi @@ -20,6 +20,9 @@ observability_performance_result=$1 discovery_selected=$2 discovery_windows_result=$3 discovery_install_result=$4 +verification_selected=$5 +verification_windows_result=$6 +verification_install_result=$7 require_result() { name=$1 @@ -54,3 +57,5 @@ require_scope observability-langfuse "$observability_selected" \ "$observability_install_result" "$observability_performance_result" require_scope discovery "$discovery_selected" \ "$discovery_windows_result" "$discovery_install_result" +require_scope verification "$verification_selected" \ + "$verification_windows_result" "$verification_install_result"