diff --git a/.github/workflows/python-quality.yml b/.github/workflows/python-quality.yml index c8531394..496044bb 100644 --- a/.github/workflows/python-quality.yml +++ b/.github/workflows/python-quality.yml @@ -367,6 +367,47 @@ jobs: - uses: astral-sh/setup-uv@v6 - run: uv run --quiet --frozen --no-dev --project plugins/foundation/darrow-information-architecture/backend python plugins/foundation/darrow-information-architecture/backend/tests/fresh_install.py + adaptive-delivery-windows: + name: Adaptive delivery Python ${{ matrix.python-version }} on windows-latest + needs: changes + if: contains(needs.changes.outputs.packages, 'plugins/orchestration/darrow-adaptive-delivery/backend') + 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/orchestration/darrow-adaptive-delivery/backend + + adaptive-delivery-fresh-install: + name: Fresh install adaptive delivery on ${{ matrix.os }} + needs: changes + if: contains(needs.changes.outputs.packages, 'plugins/orchestration/darrow-adaptive-delivery/backend') + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + 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 + - run: uv run --quiet --frozen --no-dev --project plugins/orchestration/darrow-adaptive-delivery/backend python plugins/orchestration/darrow-adaptive-delivery/backend/tests/fresh_install.py + inventory: name: Python inventory guard runs-on: ubuntu-latest @@ -510,6 +551,8 @@ jobs: - decisions-fresh-install - information-architecture-windows - information-architecture-fresh-install + - adaptive-delivery-windows + - adaptive-delivery-fresh-install - inventory - skill-authoring-fresh-install - observability-fresh-install @@ -552,6 +595,9 @@ jobs: IA_SELECTED: ${{ contains(needs.changes.outputs.packages, 'plugins/foundation/darrow-information-architecture/backend') }} IA_WINDOWS_RESULT: ${{ needs.information-architecture-windows.result }} IA_INSTALL_RESULT: ${{ needs.information-architecture-fresh-install.result }} + ADAPTIVE_SELECTED: ${{ contains(needs.changes.outputs.packages, 'plugins/orchestration/darrow-adaptive-delivery/backend') }} + ADAPTIVE_WINDOWS_RESULT: ${{ needs.adaptive-delivery-windows.result }} + ADAPTIVE_INSTALL_RESULT: ${{ needs.adaptive-delivery-fresh-install.result }} run: | scripts/verify-python-quality-results \ "$CHANGES_RESULT" "$INVENTORY_RESULT" \ @@ -583,3 +629,7 @@ jobs: if [ "$REVIEW_SELECTED" = true ]; then expected=success; fi test "$REVIEW_WINDOWS_RESULT" = "$expected" test "$REVIEW_INSTALL_RESULT" = "$expected" + expected=skipped + if [ "$ADAPTIVE_SELECTED" = true ]; then expected=success; fi + test "$ADAPTIVE_WINDOWS_RESULT" = "$expected" + test "$ADAPTIVE_INSTALL_RESULT" = "$expected" diff --git a/docs/specs/adaptive-delivery.md b/docs/specs/adaptive-delivery.md index aefc3a0e..5c948c40 100644 --- a/docs/specs/adaptive-delivery.md +++ b/docs/specs/adaptive-delivery.md @@ -87,12 +87,43 @@ reason: explicit-orchestration-entrypoint-required ## Read-only preflight -The bundled helper is `/bin/adaptive-delivery-preflight`. On Codex, the activated -file `/skills/adaptive-delivery/SKILL.md` binds that same plugin root. -Relative traversal to `../../bin` starts at the directory containing -`SKILL.md`, not at the file path. Check the helper at that exact location; +The contained Python package is `/backend`, invoked through +`uv run --quiet --frozen --no-dev --project ` followed by +`adaptive-delivery-preflight` or `claude-agent-route`. On Codex, the activated +file `/skills/adaptive-delivery/SKILL.md` binds that same plugin root; +`../../backend` starts at the directory containing `SKILL.md`. Claude binds +`${CLAUDE_PLUGIN_ROOT}/backend`. Check the package and lock at that exact location; unavailability does not authorize searching for a different installation. +The package supports Python 3.10–3.13, UV, and Git on Linux, macOS, and native +Windows, with no runtime dependencies or Bash entrypoint adapters. Native paths +and argument-vector subprocesses preserve spaces, Unicode, and linked-worktree +identity. It retains the prepared-v2, route-v2, and Claude-agent-route-v1 records, +record ordering, policy precedence, refusal exit 2, and help exit 0. Commands emit +UTF-8 stdout/stderr with LF records regardless of the host's redirected console +encoding, including native Windows code pages. JSON uses +the standard-library parser; syntax diagnostics may name its line/column while +semantic validation, duplicate rejection, and fail-closed behavior remain required. +No helper starts a host process or implements orchestration authority checks; +authority, provider discovery by advertised intent, and native launch remain +owned by the skill and its host guides. + +Editable installation must preserve Unicode source paths when Python 3.10/3.11 +reads startup path files using a Windows legacy code page. Package bootstrap +records use ASCII-safe Python string literals; the runtime retains the original +paths. Subprocess test diagnostics retain undecodable startup error bytes. + +Eval-only readiness, review, verification, and completion-proof mechanics live +in the same contained package, with inert fixture templates. They preserve +candidate fingerprints, closed finding sets, delayed combined assessment, +canonical artifact validation, and passive evidence beneath Git metadata. +Fresh copied-artifact tests exercise runtime-only installation and both host +routes on all three native platforms; Python quality gates independently +require 95% statement and branch coverage. Python regression tests preserve +route refusals, installed fixture contracts, closed review histories, and +mutation during independent and combined assessment on all three native +platforms, without a Bash test runner. + The helper binds discovery and every Git operation to the requested `--repo` working tree, including a linked worktree or its subdirectory. Ambient Git repository, worktree, common-directory, index, object-store, discovery, and @@ -679,9 +710,9 @@ Nested host processes are not an adaptive-delivery fallback. The bundled helper may prepare deterministic repository evidence and resolve a configured route. It does not launch models, persist objectives, record owner -lifecycle, render completion, or supervise work. Plugin-shipped scripts remain -portable across Bash 5 and macOS Bash 3.2 and use only baseline Unix utilities -plus the host CLIs they explicitly wrap. +lifecycle, render completion, or supervise work. The contained Python package +and its regression tests support Python 3.10–3.13 on macOS, Linux, and native +Windows, using UV and Git without a Bash dependency. Every required runtime file remains inside the plugin. Bundled helpers are resolved without filesystem search: Claude uses its host-substituted plugin diff --git a/evals/runner/adapters/claude.ts b/evals/runner/adapters/claude.ts index ea9834a0..003fa5b0 100644 --- a/evals/runner/adapters/claude.ts +++ b/evals/runner/adapters/claude.ts @@ -4345,7 +4345,15 @@ async function claudeHarnessResult(options: { } function hasAdaptiveDeliveryPreflight(pluginRoot: string): boolean { - return existsSync(join(pluginRoot, "bin", "adaptive-delivery-preflight")); + return existsSync( + join( + pluginRoot, + "backend", + "src", + "darrow_adaptive_delivery", + "preflight.py", + ), + ); } async function executeClaude( diff --git a/evals/runner/adapters/codex.test.ts b/evals/runner/adapters/codex.test.ts index 30f5d4ee..19504b13 100644 --- a/evals/runner/adapters/codex.test.ts +++ b/evals/runner/adapters/codex.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test"; -import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises"; +import { cp, mkdir, mkdtemp, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { @@ -15,6 +15,7 @@ import { codexSpawnGuardRequested, codexThreadId, codexTokenUsage, + legacyAdaptiveDeliveryPreflight, retainedCodexEvidence, retainedCodexEvidenceForThread, } from "./codex"; @@ -28,6 +29,91 @@ import { } from "../codex-spawn-guard"; const REPO = "/tmp/eval"; + +test("Python plugin commands do not claim historical shell enforcement", async () => { + const root = await mkdtemp(join(tmpdir(), "darrow-python-plugin-")); + const plugin = join(root, "plugin"); + const repo = join(root, "repo"); + const run = (cwd: string, argv: string[]) => { + const result = Bun.spawnSync(argv, { cwd, stdout: "pipe", stderr: "pipe" }); + expect(result.exitCode).toBe(0); + return result.stdout.toString(); + }; + try { + await cp( + join( + import.meta.dir, + "../../../plugins/orchestration/darrow-adaptive-delivery", + ), + plugin, + { + recursive: true, + filter: (path) => + !path + .split("/") + .some((part) => + [ + ".venv", + "__pycache__", + ".mypy_cache", + ".pytest_cache", + ".ruff_cache", + ".hypothesis", + ].includes(part), + ), + }, + ); + await mkdir(repo); + run(repo, ["git", "init", "-qb", "main"]); + run(repo, [ + "git", + "-c", + "user.name=Fixture", + "-c", + "user.email=fixture@example.invalid", + "commit", + "--allow-empty", + "-qm", + "fixture", + ]); + const argv = [ + "uv", + "run", + "--quiet", + "--frozen", + "--no-dev", + "--project", + join(plugin, "backend"), + "adaptive-delivery-preflight", + ]; + expect( + run(repo, [...argv, "prepare", "--repo", repo, "--host", "codex"]), + ).toContain("working_tree\tclean"); + expect( + run(repo, [ + ...argv, + "route", + "--repo", + repo, + "--host", + "codex", + "--profile", + "routine", + ]), + ).toContain("selected_route\tcodex"); + expect(await legacyAdaptiveDeliveryPreflight([plugin])).toBeUndefined(); + await mkdir(join(plugin, "bin")); + await writeFile( + join(plugin, "bin/adaptive-delivery-preflight"), + "legacy fixture", + ); + expect(await legacyAdaptiveDeliveryPreflight([plugin])).toEndWith( + "/bin/adaptive-delivery-preflight", + ); + } finally { + await rm(root, { recursive: true, force: true }); + } +}, 30000); const COMPLETE_CONTRACT = [ "Role: You are the already-launched sole engineering owner. Perform this contract directly; do not invoke adaptive-delivery or seek another owner.", "Outcome: Implement the requested fixture behavior.", diff --git a/evals/runner/adapters/codex.ts b/evals/runner/adapters/codex.ts index edacbbc9..ab636cca 100644 --- a/evals/runner/adapters/codex.ts +++ b/evals/runner/adapters/codex.ts @@ -4046,6 +4046,16 @@ async function installedCodexPluginContext( }; } +/** Historical step attestations require their actual shell helper. */ +export async function legacyAdaptiveDeliveryPreflight( + pluginRoots: string[], +): Promise { + const candidate = pluginRoots + .map((root) => join(root, "bin", "adaptive-delivery-preflight")) + .find((path) => existsSync(path)); + return candidate ? await realpath(candidate) : undefined; +} + async function codexProcessContext( repoDir: string, prompt: string, @@ -4061,14 +4071,8 @@ async function codexProcessContext( env.TMPDIR = objectiveRoot; const { installedSkillsRoots, installedPluginRoots } = await installedCodexPluginContext(repoDir, env); - const adaptiveDeliveryPreflightCandidate = installedPluginRoots - .map((pluginRoot) => join(pluginRoot, "bin", "adaptive-delivery-preflight")) - .find((path) => existsSync(path)); const adaptiveDeliveryPreflightPath = - adaptiveDeliveryPreflightCandidate && - existsSync(adaptiveDeliveryPreflightCandidate) - ? await realpath(adaptiveDeliveryPreflightCandidate) - : undefined; + await legacyAdaptiveDeliveryPreflight(installedPluginRoots); const spawnGuard = adaptiveDeliveryPreflightPath && enableSpawnGuard ? await installCodexSpawnGuard(canonicalRepoDir, env, { diff --git a/evals/runner/fixture.test.ts b/evals/runner/fixture.test.ts index 3c732af6..6460d0b0 100644 --- a/evals/runner/fixture.test.ts +++ b/evals/runner/fixture.test.ts @@ -386,7 +386,7 @@ describe("eval fixture skill mounts", () => { } await mkdir(recipe, { recursive: true }); await mkdir(adaptive, { recursive: true }); - await mkdir(join(orchestrationRoot, "bin"), { recursive: true }); + await mkdir(join(orchestrationRoot, "backend"), { recursive: true }); await writeFile( join(recipe, "SKILL.md"), "---\nname: recipe\ndescription: Recipe\n---\n", @@ -414,7 +414,7 @@ describe("eval fixture skill mounts", () => { ); } await writeFile( - join(orchestrationRoot, "bin", "adaptive-delivery-preflight"), + join(orchestrationRoot, "backend", "pyproject.toml"), "fixture runner\n", ); @@ -444,7 +444,7 @@ describe("eval fixture skill mounts", () => { ).toBe(true); expect(existsSync(join(claudeAdditional, "skills", "recipe"))).toBe(false); expect( - existsSync(join(claudeAdditional, "bin", "adaptive-delivery-preflight")), + existsSync(join(claudeAdditional, "backend", "pyproject.toml")), ).toBe(true); const marketplace = join(fixture, ".git", "eval-marketplace"); diff --git a/evals/runner/goal-readiness-fixture.test.ts b/evals/runner/goal-readiness-fixture.test.ts index 40a7238a..8f268b59 100644 --- a/evals/runner/goal-readiness-fixture.test.ts +++ b/evals/runner/goal-readiness-fixture.test.ts @@ -45,7 +45,7 @@ for (const [name, trace, passes] of [ } finally { await destroyFixture(repo); } - }); + }, 30_000); } async function postLaunchFixture() { @@ -88,7 +88,7 @@ test("caller inspection explicitly assigns the new behavior to the normalization const [initial] = await runChecks(repo, [ { name: "initial readiness", - run: 'bash .agents/bin/implementation-readiness-fixture "$PWD"', + run: 'uv run --quiet --frozen --no-dev --project .agents/backend adaptive-delivery-fixture readiness "$PWD"', }, ]); expect(initial?.passed).toBe(true); @@ -108,7 +108,8 @@ test("caller inspection explicitly assigns the new behavior to the normalization for (const mode of ["before-edit", "omitted", "after-edit"] as const) { test(`post-launch readiness gate: reassessment ${mode}`, async () => { const { repo, evalCase } = await postLaunchFixture(); - const assess = 'bash .agents/bin/implementation-readiness-fixture "$PWD"'; + const assess = + 'uv run --quiet --frozen --no-dev --project .agents/backend adaptive-delivery-fixture readiness "$PWD"'; try { const [initial] = await runChecks(repo, [ { @@ -155,5 +156,5 @@ for (const mode of ["before-edit", "omitted", "after-edit"] as const) { } finally { await destroyFixture(repo); } - }); + }, 30_000); } diff --git a/evals/runner/goal-review-fixture.test.ts b/evals/runner/goal-review-fixture.test.ts index ae676574..eebe86e6 100644 --- a/evals/runner/goal-review-fixture.test.ts +++ b/evals/runner/goal-review-fixture.test.ts @@ -12,7 +12,36 @@ const backendSource = new URL( "../../plugins/capability/darrow-review/backend/", import.meta.url, ); -const proofSource = new URL("fixtures/require-clear-review.sh", source); +const fixtureBackend = new URL("../../../backend/", source); + +async function proofFiles() { + const files: Record = {}; + for (const name of ["pyproject.toml", "uv.lock"]) + files[`.git/fixture-backend/${name}`] = await Bun.file( + new URL(name, fixtureBackend), + ).text(); + const glob = new Bun.Glob("src/**/*.{py,typed,md}"); + for await (const name of glob.scan(fixtureBackend.pathname)) + files[`.git/fixture-backend/${name}`] = await Bun.file( + new URL(name, fixtureBackend), + ).text(); + return files; +} + +function proofCommand(...args: string[]) { + return [ + "uv", + "run", + "--quiet", + "--frozen", + "--no-dev", + "--project", + ".git/fixture-backend", + "adaptive-delivery-fixture", + "proof", + ...args, + ]; +} async function reviewFiles(prefix = ".git/review-plugin/backend") { const files: Record = {}; @@ -256,7 +285,7 @@ for (const scenario of [ ".git/darrow-review.fixture/result.tsv": records.map((row) => row.join("\t")).join("\n") + "\n", ...(await reviewFiles()), - ".git/fixture-bin/review-proof": await Bun.file(proofSource).text(), + ...(await proofFiles()), }, }, skillDir: "", @@ -337,7 +366,7 @@ for (const scenario of [ commits: [{ message: "Initial", files: { "value.txt": "before\n" } }], files: { ...(await reviewFiles()), - ".git/fixture-bin/review-proof": await Bun.file(proofSource).text(), + ...(await proofFiles()), }, }, skillDir: "", @@ -347,7 +376,7 @@ for (const scenario of [ Bun.spawnSync( args[0]!.startsWith("review-") ? reviewCommand(args[0]!, ...args.slice(1)) - : ["/bin/bash", ...args], + : proofCommand(...args.slice(1)), { cwd: repo }, ); const scope = () => { @@ -459,7 +488,7 @@ for (const scenario of [ if (scenario.stale) await Bun.write(`${repo}/value.txt`, "changed after review\n"); const artifact = await selectedArtifact(repo, record, scenario); - const result = run(".git/fixture-bin/review-proof", "complete", artifact); + const result = run("fixture-proof", "complete", artifact); expect({ passes: result.exitCode === 0, detail: result.stderr.toString(), @@ -468,16 +497,10 @@ for (const scenario of [ scenario.passes, ); if (scenario.passes) { - expect(run(".git/fixture-bin/review-proof", "artifact").exitCode).toBe( - 0, - ); - expect(run(".git/fixture-bin/review-proof", "current").exitCode).toBe( - 0, - ); + expect(run("fixture-proof", "artifact").exitCode).toBe(0); + expect(run("fixture-proof", "current").exitCode).toBe(0); await Bun.write(`${repo}/value.txt`, "changed after completion\n"); - expect( - run(".git/fixture-bin/review-proof", "current").exitCode, - ).not.toBe(0); + expect(run("fixture-proof", "current").exitCode).not.toBe(0); } } finally { await destroyFixture(repo); diff --git a/plugins/orchestration/darrow-adaptive-delivery/.claude-plugin/plugin.json b/plugins/orchestration/darrow-adaptive-delivery/.claude-plugin/plugin.json index 7771d92d..cc389581 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/.claude-plugin/plugin.json +++ b/plugins/orchestration/darrow-adaptive-delivery/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "darrow-adaptive-delivery", "description": "Adaptive Delivery: ready and compile bounded engineering requests, then launch one routed subagent owner", - "version": "0.21.5", + "version": "0.22.3", "license": "BUSL-1.1", "author": { "name": "Björn Rochel", diff --git a/plugins/orchestration/darrow-adaptive-delivery/.codex-plugin/plugin.json b/plugins/orchestration/darrow-adaptive-delivery/.codex-plugin/plugin.json index 4268cda9..306fbcfd 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/.codex-plugin/plugin.json +++ b/plugins/orchestration/darrow-adaptive-delivery/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "darrow-adaptive-delivery", - "version": "0.21.5", + "version": "0.22.3", "description": "Adaptive Delivery: ready and compile bounded engineering requests, then launch one routed subagent owner", "author": { "name": "Björn Rochel", diff --git a/plugins/orchestration/darrow-adaptive-delivery/README.md b/plugins/orchestration/darrow-adaptive-delivery/README.md index f9e8e276..a50242d9 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/README.md +++ b/plugins/orchestration/darrow-adaptive-delivery/README.md @@ -124,9 +124,11 @@ or lifecycle-ledger state machine. The owner still observes current external state before repeating an ambiguous effect and never duplicates an effect that already completed. -## `bin/adaptive-delivery-preflight` +## Python helpers -The portable Bash helper exposes only two read-only operations: +The contained `backend` package exposes two read-only preflight operations. +Prefix each command with +`uv run --quiet --frozen --no-dev --project "/backend"`: ```text adaptive-delivery-preflight prepare --repo --host @@ -144,7 +146,7 @@ render completion reports, supervise work, or provide nested host sessions. ## Claude route agents -`bin/claude-agent-route` maps an exact supported Claude model and effort to one +`claude-agent-route` (through the same frozen UV command) maps an exact supported Claude model and effort to one plugin-shipped route agent and rejects conflicting environment overrides. The current bundled routes are: @@ -176,7 +178,10 @@ Use this for an explicitly invoked bounded engineering outcome. Ordinary complex ## Hosts and prerequisites -Codex with native subagent support or Claude Code with the bundled foreground route agents; Git, Bash, baseline Unix tools, available routes, and the capabilities matching authorized operations. +Codex with native subagent support or Claude Code with the bundled foreground route agents; UV, Python 3.10–3.13, Git, available routes, and the capabilities matching authorized operations. +The helpers support Linux, macOS, and native Windows without Bash or runtime +Python dependencies. Regression tests run through the repository's Python +quality gate on all three platforms. ## Installation diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/pyproject.toml b/plugins/orchestration/darrow-adaptive-delivery/backend/pyproject.toml new file mode 100644 index 00000000..dd8e736b --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/pyproject.toml @@ -0,0 +1,54 @@ +[project] +name = "darrow-adaptive-delivery" +version = "0.22.3" +description = "Contained preflight, route resolution, and eval mechanics for Darrow" +requires-python = ">=3.10,<3.14" +dependencies = [] + +[project.scripts] +adaptive-delivery-preflight = "darrow_adaptive_delivery.cli:preflight" +claude-agent-route = "darrow_adaptive_delivery.cli:claude_route" +adaptive-delivery-fixture = "darrow_adaptive_delivery.fixtures.cli:main" + +[dependency-groups] +dev = ["coverage[toml]>=7.10,<8", "editables>=0.3,<1", "hatchling==1.32.3", "hypothesis>=6.138,<7", "mypy>=1.18,<2", "pytest>=8.4,<9", "ruff>=0.12,<1"] + +[build-system] +requires = ["hatchling==1.32.3"] +build-backend = "darrow_adaptive_delivery._build_backend" +backend-path = ["src"] + +[tool.uv] +default-groups = [] + +[tool.hatch.build.targets.wheel] +packages = ["src/darrow_adaptive_delivery"] + +[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_adaptive_delivery"] + +[tool.coverage.report] +show_missing = true +skip_covered = false diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/__init__.py b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/__init__.py new file mode 100644 index 00000000..916d8fde --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/__init__.py @@ -0,0 +1 @@ +"""Read-only goal preparation; execution remains with the native host.""" diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/_build_backend.py b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/_build_backend.py new file mode 100644 index 00000000..342b828e --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/_build_backend.py @@ -0,0 +1,62 @@ +"""Keep Hatch's editable source paths readable by legacy Windows Python.""" + +from __future__ import annotations + +import csv +import hashlib +import io +from base64 import urlsafe_b64encode +from pathlib import Path +from zipfile import ZipFile + +from hatchling.build import build_editable as hatch_build_editable +from hatchling.build import build_sdist as build_sdist +from hatchling.build import build_wheel as build_wheel +from hatchling.build import ( + get_requires_for_build_editable as get_requires_for_build_editable, +) +from hatchling.build import get_requires_for_build_sdist as get_requires_for_build_sdist +from hatchling.build import get_requires_for_build_wheel as get_requires_for_build_wheel +from hatchling.build import ( + prepare_metadata_for_build_editable as prepare_metadata_for_build_editable, +) +from hatchling.build import ( + prepare_metadata_for_build_wheel as prepare_metadata_for_build_wheel, +) + + +def portable_editable(wheel: Path) -> None: + with ZipFile(wheel) as archive: + members = { + info.filename: (info, archive.read(info)) for info in archive.infolist() + } + name = "_editable_impl_darrow_adaptive_delivery.pth" + info, content = members[name] + paths = content.decode("utf-8").splitlines() + members[name] = (info, f"import sys; sys.path.extend({paths!a})\n".encode("ascii")) + record_name = next(name for name in members if name.endswith(".dist-info/RECORD")) + record = io.StringIO(newline="") + writer = csv.writer(record, lineterminator="\n") + with ZipFile(wheel, "w") as archive: + for name, (info, data) in members.items(): + if name == record_name: + continue + archive.writestr(info, data) + digest = ( + urlsafe_b64encode(hashlib.sha256(data).digest()) + .rstrip(b"=") + .decode("ascii") + ) + writer.writerow((name, "sha256=" + digest, len(data))) + writer.writerow((record_name, "", "")) + archive.writestr(members[record_name][0], record.getvalue().encode("utf-8")) + + +def build_editable( + wheel_directory: str, + config_settings: dict[str, object] | None = None, + metadata_directory: str | None = None, +) -> str: + name = hatch_build_editable(wheel_directory, config_settings, metadata_directory) + portable_editable(Path(wheel_directory) / name) + return name diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/arguments.py b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/arguments.py new file mode 100644 index 00000000..3c6f00e8 --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/arguments.py @@ -0,0 +1,24 @@ +"""Small option grammar preserving the helpers' refusal contract.""" + +from __future__ import annotations + +from .common import RefusalError + + +def options(args: list[str], names: set[str], label: str) -> dict[str, str]: + result: dict[str, str] = {} + position = 0 + while position < len(args): + name = args[position] + if name not in names: + raise RefusalError(f"unknown {label}option: {name}") + if position + 1 == len(args) or not args[position + 1]: + raise RefusalError(f"missing value for {name}") + result[name.removeprefix("--")] = args[position + 1] + position += 2 + return result + + +def require(values: dict[str, str], names: set[str], message: str) -> None: + if not names <= values.keys(): + raise RefusalError(message) diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/claude.py b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/claude.py new file mode 100644 index 00000000..5e689701 --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/claude.py @@ -0,0 +1,92 @@ +"""Bind a direct-Anthropic route to its shipped native Claude Agent.""" + +from __future__ import annotations + +import os +import re +from collections.abc import Mapping +from pathlib import Path + +from .arguments import options, require +from .common import PLUGIN, RefusalError, read_text, record + +USAGE = "usage: claude-agent-route --provider --model --effort \n" +SELECTORS = ( + "CLAUDE_CODE_USE_BEDROCK", + "CLAUDE_CODE_USE_MANTLE", + "CLAUDE_CODE_USE_VERTEX", + "CLAUDE_CODE_USE_FOUNDRY", + "CLAUDE_CODE_USE_ANTHROPIC_AWS", +) + + +def direct_provider(env: Mapping[str, str]) -> None: + for selector in SELECTORS: + if env.get(selector): + raise RefusalError( + f"Claude provider is not observably direct Anthropic: {selector} is set" + ) + if env.get("ANTHROPIC_BASE_URL", "") not in { + "", + "https://api.anthropic.com", + "https://api.anthropic.com/", + }: + raise RefusalError( + "Claude provider is not observably direct Anthropic: ANTHROPIC_BASE_URL is custom" + ) + + +def model_suffix(model: str) -> str: + if not model.startswith("claude-"): + raise RefusalError(f"model must be a concrete Claude model ID: {model}") + suffix = model.removeprefix("claude-") + if not re.fullmatch(r"[a-zA-Z0-9.-]+", suffix): + raise RefusalError(f"unsafe model ID: {model}") + return suffix + + +def overrides(env: Mapping[str, str], model: str, effort: str) -> None: + for key, selected, label in ( + ("CLAUDE_CODE_SUBAGENT_MODEL", model, "model"), + ("CLAUDE_CODE_EFFORT_LEVEL", effort, "effort"), + ): + value = env.get(key, "") + if value and value != selected: + raise RefusalError(f"{key} overrides selected {label}: {value}") + + +def resolve(model: str, effort: str, plugin: Path, env: Mapping[str, str]) -> str: + suffix = model_suffix(model) + if effort not in {"low", "medium", "high", "xhigh", "max"}: + raise RefusalError(f"unsupported effort: {effort}") + name = f"adaptive-delivery-{suffix}-{effort}" + path = plugin / "agents" / f"{name}.md" + try: + lines = read_text(path, "runner is unreadable").splitlines() + except RefusalError as error: + raise RefusalError(f"no bundled runner for {model}/{effort}") from error + for field, value in (("name", name), ("model", model), ("effort", effort)): + if f"{field}: {value}" not in lines: + raise RefusalError(f"runner {field} does not match route: {path}") + overrides(env, model, effort) + return ( + record("format", "darrow-claude-agent-route-v1") + + record("selected_route", "claude", "anthropic", model, effort) + + record("subagent_type", f"darrow-adaptive-delivery:{name}") + + record("agent_file", str(path)) + ) + + +def run(args: list[str], plugin: Path = PLUGIN) -> str: + if any(arg in {"-h", "--help"} for arg in args): + return USAGE + values = options(args, {"--provider", "--model", "--effort"}, "") + require( + values, + {"provider", "model", "effort"}, + "--provider, --model, and --effort are required", + ) + if values["provider"] != "anthropic": + raise RefusalError(f"unsupported provider: {values['provider']}") + direct_provider(os.environ) + return resolve(values["model"], values["effort"], plugin, os.environ) diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/cli.py b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/cli.py new file mode 100644 index 00000000..26b1ff6e --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/cli.py @@ -0,0 +1,32 @@ +"""Console boundaries: stable stdout records, stderr refusals, exit 2.""" + +from __future__ import annotations + +import sys +from collections.abc import Callable +from io import TextIOWrapper + +from . import claude +from . import preflight as preparation +from .common import RefusalError + + +def execute(name: str, run: Callable[[list[str]], str]) -> int: + for stream in (sys.stdout, sys.stderr): + if isinstance(stream, TextIOWrapper): + stream.reconfigure(encoding="utf-8", newline="\n") + try: + output = run(sys.argv[1:]) + except (RefusalError, OSError, UnicodeError) as error: + print(f"{name}: {error}", file=sys.stderr) + return 2 + sys.stdout.write(output) + return 0 + + +def preflight() -> int: + return execute("adaptive-delivery-preflight", preparation.run) + + +def claude_route() -> int: + return execute("claude-agent-route", claude.run) diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/common.py b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/common.py new file mode 100644 index 00000000..5693e067 --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/common.py @@ -0,0 +1,88 @@ +"""Filesystem and subprocess boundaries shared by the contained helpers.""" + +from __future__ import annotations + +import os +import subprocess +from pathlib import Path + +PLUGIN = Path(__file__).resolve().parents[3] +GIT_SELECTORS = frozenset( + [ + "GIT_DIR", + "GIT_WORK_TREE", + "GIT_COMMON_DIR", + "GIT_INDEX_FILE", + "GIT_OBJECT_DIRECTORY", + "GIT_ALTERNATE_OBJECT_DIRECTORIES", + "GIT_CEILING_DIRECTORIES", + "GIT_DISCOVERY_ACROSS_FILESYSTEM", + "GIT_CONFIG", + "GIT_CONFIG_PARAMETERS", + "GIT_CONFIG_COUNT", + "GIT_CONFIG_GLOBAL", + "GIT_CONFIG_SYSTEM", + "GIT_SHALLOW_FILE", + "GIT_GRAFT_FILE", + "GIT_NAMESPACE", + ] +) + + +class RefusalError(Exception): + """Invalid input or unavailable evidence; public helpers exit 2.""" + + +class CheckRequiredError(Exception): + """A fixture prerequisite failed; retain the fixture's exit 1.""" + + +def git_environment() -> dict[str, str]: + return { + key: value + for key, value in os.environ.items() + if key not in GIT_SELECTORS + and not key.startswith(("GIT_CONFIG_KEY_", "GIT_CONFIG_VALUE_")) + } + + +def git(repo: Path, *args: str, data: bytes | None = None) -> bytes: + result = subprocess.run( + ["git", "-C", str(repo), *args], + input=data, + capture_output=True, + env=git_environment(), + check=False, + ) + if result.returncode: + raise RefusalError(result.stderr.decode("utf-8", errors="replace").strip()) + return result.stdout + + +def git_text(repo: Path, *args: str) -> str: + return git(repo, *args).decode("utf-8").rstrip("\r\n") + + +def repository(path: str) -> Path: + directory = Path(path) + if not directory.is_dir(): + raise RefusalError(f"directory does not exist: {path}") + directory = directory.resolve() + try: + inside = git_text(directory, "rev-parse", "--is-inside-work-tree") + if inside != "true": + raise RefusalError("outside working tree") + return Path(git_text(directory, "rev-parse", "--show-toplevel")).resolve() + except (RefusalError, OSError) as error: + raise RefusalError(f"not a git working tree: {directory}") from error + + +def read_text(path: Path, label: str) -> str: + try: + return path.read_text(encoding="utf-8") + except (OSError, UnicodeError) as error: + raise RefusalError(f"{label}: {path}") from error + + +def record(*fields: str) -> str: + return "\t".join(fields) + "\n" diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/__init__.py b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/__init__.py new file mode 100644 index 00000000..783e9a1c --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/__init__.py @@ -0,0 +1 @@ +"""Eval-only synthetic providers; never an orchestration runtime.""" diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/cli.py b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/cli.py new file mode 100644 index 00000000..f704fd77 --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/cli.py @@ -0,0 +1,54 @@ +"""One eval-only entrypoint for synthetic capabilities and their installation.""" + +from __future__ import annotations + +import sys +from pathlib import Path + +from ..cli import execute +from ..common import CheckRequiredError, RefusalError +from . import install, proof, readiness, review + +USAGE = """usage: adaptive-delivery-fixture [arguments] + install readiness|review|verification REPO FIXTURE_DIR codex|claude|both + readiness REPO [record|render-only] + review REPO comprehensive|verify|fingerprint [contract] + verification REPO initial|follow-up + proof complete ARTIFACT|artifact|current +""" + + +def dispatch(args: list[str]) -> str: + if not args or args[0] in {"-h", "--help"}: + return USAGE + command, *rest = args + if command == "install" and len(rest) == 4: + return install.install(rest[0], Path(rest[1]), Path(rest[2]), rest[3]) + if command == "readiness" and 1 <= len(rest) <= 2: + return readiness.assess(Path(rest[0]), rest[1] if len(rest) == 2 else "record") + return assess(command, rest) + + +def assess(command: str, args: list[str]) -> str: + if command == "review" and 2 <= len(args) <= 3: + return review.assess(Path(args[0]), args[1], args[2] if len(args) == 3 else "") + if command == "verification" and len(args) == 2: + if not Path(__file__).with_name("verification.py").is_file(): + raise RefusalError("verification fixture is not installed") + from . import verification + + return verification.assess(Path(args[0]), args[1]) + if command == "proof" and 1 <= len(args) <= 2: + return proof.validate(Path.cwd(), args[0], args[1] if len(args) == 2 else "") + raise RefusalError(USAGE.rstrip()) + + +def main() -> int: + try: + return execute("adaptive-delivery-fixture", dispatch) + except CheckRequiredError as error: + print(error) + return 1 + except proof.InvalidProofError as error: + print(f"review proof: {error}", file=sys.stderr) + return 1 diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/install.py b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/install.py new file mode 100644 index 00000000..3a2d0674 --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/install.py @@ -0,0 +1,79 @@ +"""Copy isolated fixture providers and inert templates into host-visible locations.""" + +from __future__ import annotations + +import shutil +from pathlib import Path + +from ..common import PLUGIN, RefusalError +from . import readiness +from .state import append, metadata + +IGNORE = shutil.ignore_patterns( + ".venv", + "__pycache__", + ".hypothesis", + ".mypy_cache", + ".pytest_cache", + ".ruff_cache", + ".coverage*", + "coverage.json", + "tests", +) +HOSTS = {"codex": (".agents",), "claude": (".claude",), "both": (".agents", ".claude")} +SKILLS = { + "readiness": ("implementation-readiness", "assess-implementation-readiness"), + "review": ("independent-review", "independent-code-review"), + "verification": ("verification", "assess-candidate"), +} + + +def template(kind: str, root: str) -> str: + return ( + "SKILL.claude.fixture.md" + if (kind, root) == ("readiness", ".claude") + else "SKILL.fixture.md" + ) + + +def install_skills( + repo: Path, root: str, fixtures: Path, kinds: tuple[str, ...] +) -> None: + for selected in kinds: + source, name = SKILLS[selected] + destination = repo / root / "skills" / name + destination.mkdir(parents=True, exist_ok=True) + shutil.copyfile( + fixtures / source / template(selected, root), destination / "SKILL.md" + ) + + +def copy_backend(plugin: Path, backend: Path, kind: str) -> None: + shutil.copytree(plugin / "backend", backend, dirs_exist_ok=True, ignore=IGNORE) + if kind != "verification": + (backend / "src/darrow_adaptive_delivery/fixtures/verification.py").unlink( + missing_ok=True + ) + + +def install( + kind: str, repo: Path, fixtures: Path, host: str, plugin: Path = PLUGIN +) -> str: + if host not in HOSTS or kind not in SKILLS: + raise RefusalError( + "install requires readiness|review|verification REPO FIXTURE_DIR codex|claude|both" + ) + git_dir = metadata(repo) + (git_dir / "fixture-state").mkdir(exist_ok=True) + kinds = ("review", "verification") if kind == "verification" else (kind,) + for root in HOSTS[host]: + backend = repo / root / "backend" + copy_backend(plugin, backend, kind) + install_skills(repo, root, fixtures, kinds) + if kind in {"readiness", "verification"}: + append(git_dir / "info/exclude", f"/{root}/\n") + if kind == "readiness" and host in {"claude", "both"}: + (git_dir / "fixture-state/implementation-readiness-result").write_text( + readiness.assess(repo, "render-only"), encoding="utf-8", newline="\n" + ) + return "" diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/proof.py b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/proof.py new file mode 100644 index 00000000..9648bf71 --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/proof.py @@ -0,0 +1,190 @@ +"""Eval completion gate delegating canonical artifact validation to its provider.""" + +from __future__ import annotations + +import subprocess +from pathlib import Path + +from ..common import RefusalError +from .state import metadata + + +class InvalidProofError(Exception): + """Fixture completion is refused with exit 1.""" + + +def field(path: Path, name: str) -> str: + return "\n".join( + row.split("\t")[1] + for row in path.read_text(encoding="utf-8").splitlines() + if row.startswith(name + "\t") + ) + + +def provider(git_dir: Path) -> Path: + candidates = sorted(git_dir.rglob("backend/src/darrow_review/result.py")) + if not candidates: + raise InvalidProofError("installed review-result tool is missing") + first = candidates[0] + for candidate in candidates[1:]: + for name in ("result.py", "scope.py"): + if (first.parent / name).read_bytes() != ( + candidate.parent / name + ).read_bytes(): + raise InvalidProofError("conflicting fixture review-tool copies") + return first.parents[2] + + +def invoke(backend: Path, repo: Path, *args: str) -> str: + result = subprocess.run( + [ + "uv", + "run", + "--quiet", + "--frozen", + "--no-dev", + "--project", + str(backend), + *args, + ], + cwd=repo, + capture_output=True, + text=True, + encoding="utf-8", + check=False, + ) + if result.returncode: + raise InvalidProofError(result.stderr.strip() or result.stdout.strip()) + return result.stdout + + +def canonical_record(git_dir: Path, selected: str) -> Path: + path = Path(selected).resolve() + if not path.is_file(): + raise InvalidProofError(f"unreadable artifact: {selected}") + if not path.is_relative_to(git_dir) or not path.parent.name.startswith( + "darrow-review." + ): + raise InvalidProofError(f"not a canonical fixture review artifact: {path}") + if path.name not in { + "result.tsv", + "verification.tsv", + "review.md", + "verification.md", + }: + raise InvalidProofError(f"not a canonical fixture review artifact: {path}") + return path + + +def machine_record(backend: Path, repo: Path, path: Path) -> Path: + if path.suffix != ".md": + return path + if not (backend / "src/darrow_review/report.py").is_file(): + raise InvalidProofError("installed review renderer is missing") + name, render = ( + ("result.tsv", "render") + if path.name == "review.md" + else ("verification.tsv", "render-verification") + ) + record = path.with_name(name) + rendered = invoke(backend, repo, "review-report", render, str(record)) + if rendered.encode() != path.read_bytes(): + raise InvalidProofError("human report differs from its canonical result") + return record + + +def original_record(git_dir: Path, target: str) -> Path: + matches = [ + path + for path in git_dir.rglob("result.tsv") + if path.parent.name.startswith("darrow-review.") + and field(path, "target") == target + ] + if len(matches) != 1: + reason = "ambiguous" if matches else "missing" + raise InvalidProofError(f"original comprehensive result is {reason}") + return matches[0] + + +def reviewed_target(backend: Path, repo: Path, git_dir: Path, path: Path) -> str: + format_name = field(path, "format") + if format_name == "darrow-review-result-v1": + return comprehensive_target(backend, repo, path) + if format_name == "darrow-review-verification-v1": + return verification_target(backend, repo, git_dir, path) + raise InvalidProofError(f"unsupported format: {format_name}") + + +def comprehensive_target(backend: Path, repo: Path, path: Path) -> str: + if path.name != "result.tsv": + raise InvalidProofError("wrong comprehensive artifact name") + invoke(backend, repo, "review-result", "validate", str(path)) + if field(path, "verdict") != "pass": + raise InvalidProofError("review is not clear") + if field(path, "next_action") != "return control to enclosing goal": + raise InvalidProofError("review did not return control") + return field(path, "target") + + +def verification_target(backend: Path, repo: Path, git_dir: Path, path: Path) -> str: + if path.name != "verification.tsv": + raise InvalidProofError("wrong verification artifact name") + invoke(backend, repo, "review-result", "validate-verification", str(path)) + if field(path, "outcome") != "clear": + raise InvalidProofError("verification is not clear") + original = original_record(git_dir, field(path, "original_target")) + invoke( + backend, repo, "review-result", "validate-original", str(original), str(path) + ) + return field(path, "current_target") + + +def current(backend: Path, repo: Path, target: str) -> None: + if not (backend / "src/darrow_review/scope.py").is_file(): + raise InvalidProofError("installed review-scope tool is missing") + scope = invoke( + backend, + repo, + "review-scope", + "prepare", + "--repo", + str(repo), + "--base", + "HEAD", + "--target", + "WORKTREE", + ) + actual = "\n".join( + row.split("\t")[1] for row in scope.splitlines() if row.startswith("target\t") + ) + if not target or target != actual: + raise InvalidProofError(f"stale review target: {target}; current: {actual}") + + +def validate(repo: Path, mode: str, selected: str = "") -> str: + if mode not in {"complete", "artifact", "current"}: + raise InvalidProofError("expected complete ARTIFACT, artifact, or current") + try: + return validate_selected(repo, mode, selected) + except (OSError, UnicodeError, RefusalError) as error: + raise InvalidProofError(str(error)) from error + + +def validate_selected(repo: Path, mode: str, selected: str) -> str: + git_dir = metadata(repo) + proof = git_dir / "fixture-state/high-risk-review-proof" + if mode != "complete": + selected = proof.read_text(encoding="utf-8").strip() + path = canonical_record(git_dir, selected) + backend = provider(git_dir) + path = machine_record(backend, repo, path) + target = reviewed_target(backend, repo, git_dir, path) + if mode != "artifact": + current(backend, repo, target) + if mode == "complete": + proof.parent.mkdir(exist_ok=True) + proof.write_text(str(path) + "\n", encoding="utf-8", newline="\n") + (git_dir / "goal-complete").write_text( + "complete\n", encoding="utf-8", newline="\n" + ) + return f"valid clear review: {path}\n" diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/readiness.py b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/readiness.py new file mode 100644 index 00000000..6f486ce7 --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/readiness.py @@ -0,0 +1,38 @@ +"""Render the existing readiness reports and optional passive invocation evidence.""" + +from __future__ import annotations + +from pathlib import Path + +from ..common import RefusalError, git, read_text +from .state import append, lines, metadata + + +def assess(repo: Path, mode: str = "record") -> str: + if mode not in {"record", "render-only"}: + raise RefusalError(f"unsupported readiness mode: {mode}") + configured_lines = read_text( + repo / ".readiness-verdict", + "implementation-readiness-fixture: missing .readiness-verdict", + ).splitlines() + configured = configured_lines[0] if configured_lines else "" + state = metadata(repo) / "fixture-state" + count = 1 + if mode == "record": + (state / "implementation-readiness-pre-status").write_bytes( + git(repo, "status", "--porcelain", "--untracked-files=all") + ) + invocations = state / "implementation-readiness-invocations" + append(invocations, configured + "\n") + count = len(lines(invocations)) + verdict = configured + if configured == "needs-decision-then-ready": + verdict = "ready" if count >= 2 else "needs-decision" + if verdict not in {"ready", "needs-decision", "needs-discovery", "blocked"}: + raise RefusalError( + f"implementation-readiness-fixture: unsupported verdict: {verdict}" + ) + return read_text( + Path(__file__).parent / "reports" / f"{verdict}.md", + "readiness report is unreadable", + ) diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/reports/blocked.md b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/reports/blocked.md new file mode 100644 index 00000000..1fc6d223 --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/reports/blocked.md @@ -0,0 +1,26 @@ +## Implementation readiness + +**Verdict:** `blocked` + +### Basis + +- **Source:** authoritative fixture specification + - **Authority:** `authoritative` + - **Status:** `available` + - **Summary:** A required external prerequisite is unavailable. + +### Quality bar + +None. + +### Findings + +- **Type:** `dependency` + - **Summary:** Implementation cannot begin while the prerequisite is unavailable. + - **Evidence:** + - The fixture marks the request as externally blocked. + +### Required next action + +- **Type:** `unblock` +- **Description:** Restore the required prerequisite before implementation starts. diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/reports/needs-decision.md b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/reports/needs-decision.md new file mode 100644 index 00000000..ec2bb04b --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/reports/needs-decision.md @@ -0,0 +1,26 @@ +## Implementation readiness + +**Verdict:** `needs-decision` + +### Basis + +- **Source:** authoritative fixture specification + - **Authority:** `authoritative` + - **Status:** `available` + - **Summary:** The request deliberately leaves one product choice unresolved. + +### Quality bar + +None. + +### Findings + +- **Type:** `unresolved-decision` + - **Summary:** The authoritative request does not select the required behavior. + - **Evidence:** + - The fixture marks the request as requiring a decision before implementation. + +### Required next action + +- **Type:** `decision` +- **Description:** Select the required behavior before implementation starts. diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/reports/needs-discovery.md b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/reports/needs-discovery.md new file mode 100644 index 00000000..78d05b4b --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/reports/needs-discovery.md @@ -0,0 +1,26 @@ +## Implementation readiness + +**Verdict:** `needs-discovery` + +### Basis + +- **Source:** authoritative fixture specification + - **Authority:** `authoritative` + - **Status:** `available` + - **Summary:** A required behavior boundary has not yet been discovered. + +### Quality bar + +None. + +### Findings + +- **Type:** `missing-information` + - **Summary:** The request depends on an unverified behavior boundary. + - **Evidence:** + - The fixture marks the request as requiring discovery before implementation. + +### Required next action + +- **Type:** `discovery` +- **Description:** Discover and settle the required behavior boundary before implementation starts. diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/reports/ready.md b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/reports/ready.md new file mode 100644 index 00000000..a084ce59 --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/reports/ready.md @@ -0,0 +1,25 @@ +## Implementation readiness + +**Verdict:** `ready` + +### Basis + +- **Source:** authoritative fixture specification + - **Authority:** `authoritative` + - **Status:** `available` + - **Summary:** The requested outcome and exact verification command are defined. + +### Quality bar + +- **Criterion:** The requested fixture behavior is observable. + - **Oracle:** The fixture's check script exits zero for the requested content. + - **Verification:** Run the repository-provided check script after implementation. + +### Findings + +None. + +### Required next action + +- **Type:** `none` +- **Description:** Begin the bounded implementation under the existing goal authority. diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/review.py b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/review.py new file mode 100644 index 00000000..c2bfdd45 --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/review.py @@ -0,0 +1,103 @@ +"""Candidate-bound review observations, including mutation during assessment.""" + +from __future__ import annotations + +import re +import time +from pathlib import Path + +from ..common import RefusalError, git_text, repository +from .review_policy import Findings, constrain, policy, report +from .state import Candidate, append, capture, lines, metadata, oid + + +def start_observation(repo: Path, git_dir: Path, target: str) -> tuple[str, str] | None: + path = git_dir / "fixture-review-event-path" + if not path.is_file(): + return None + rows = lines(path) + subject = rows[0] if rows else "" + if not subject or not (repo / subject).is_file(): + raise RefusalError("review subject is unavailable") + subject_oid = git_text(repo, "hash-object", "--", subject) + append( + git_dir / "fixture-state/review-events", + f"review_start\t{target}\t{subject_oid}\n", + ) + delay = git_dir / "fixture-review-delay-seconds" + if delay.is_file(): + wait_for_review(delay) + return subject, subject_oid + + +def wait_for_review(path: Path) -> None: + rows = lines(path) + if not rows or not re.fullmatch(r"[0-9]+", rows[0]): + raise RefusalError("invalid review delay") + time.sleep(int(rows[0])) + + +def finish_observation( + repo: Path, git_dir: Path, target: str, subject: str, findings: Findings +) -> None: + end_oid = git_text(repo, "hash-object", "--", subject) + end_target = capture(repo).target + append( + git_dir / "fixture-state/review-events", + f"review_end\t{end_target}\t{end_oid}\n", + ) + if end_target != target: + findings.outcome = "inconclusive" + findings.failure = ( + "Reviewed worktree changed before the review response returned" + ) + + +def require_history(mode: str, path: Path) -> None: + if mode == "comprehensive" and path.is_file(): + raise RefusalError( + "comprehensive review already established the closed finding set" + ) + if mode == "verify" and not path.is_file(): + raise RefusalError( + "fix verification requires the original comprehensive review" + ) + + +def assess(repo_path: Path, mode: str, contract: str = "") -> str: + if mode not in {"comprehensive", "verify", "fingerprint"}: + raise RefusalError( + "usage: independent-review-fixture REPOSITORY comprehensive|verify|fingerprint [contract]" + ) + repo = repository(str(repo_path)) + if ( + mode != "fingerprint" + and oid(repo, contract.encode()) != "b1d8706dabad37d43795dcba094c36681b029df5" + ): + raise RefusalError("review capability contract is required") + candidate = capture(repo) + if mode == "fingerprint": + return candidate.target + "\n" + return review_candidate(repo, mode, candidate) + + +def review_candidate(repo: Path, mode: str, candidate: Candidate) -> str: + git_dir = metadata(repo) + state = git_dir / "fixture-state" + invocations = state / "independent-review-invocations" + require_history(mode, invocations) + history = lines(invocations) + append(state / "independent-review-events", f"review_start\t{candidate.target}\n") + observation = start_observation(repo, git_dir, candidate.target) + findings = policy(git_dir, candidate, len(history)) + # Validate comprehensive outcomes before any delayed end observation. + constrain(findings, mode, candidate.target, []) + if observation: + finish_observation(repo, git_dir, candidate.target, observation[0], findings) + constrain(findings, mode, candidate.target, history) + append(invocations, f"{candidate.target}\t{findings.outcome}\t{mode}\n") + if observation: + append( + state / "review-events", f"review\t{findings.outcome}\t{observation[1]}\n" + ) + return report(findings, mode, candidate, repo, git_dir) diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/review_policy.py b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/review_policy.py new file mode 100644 index 00000000..e72cde37 --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/review_policy.py @@ -0,0 +1,123 @@ +"""Synthetic outcomes and human reports for the independent-review eval seam.""" + +from __future__ import annotations + +import re +from dataclasses import dataclass +from pathlib import Path + +from ..common import RefusalError, read_text +from .state import Candidate, lines + + +@dataclass +class Findings: + outcome: str = "clear" + failure: str = "Fixture policy rejects this candidate" + advisory: str = "" + unrelated: str = "" + + +def sequence(path: Path, invocation: int) -> Findings: + rows = lines(path) + if invocation >= len(rows) or not rows[invocation]: + raise RefusalError("review sequence exhausted") + # Bash's tab IFS coalesces empty fields; retain that fixture contract. + fields = re.split(r"\t+", rows[invocation].strip("\t"), maxsplit=3) + fields += [""] * (4 - len(fields)) + result = Findings(*fields) + allowed = { + "clear", + "blocking", + "continue", + "no_progress", + "blocked", + "inconclusive", + "unavailable", + } + if result.outcome not in allowed: + raise RefusalError("invalid review sequence outcome") + if ( + result.outcome in {"blocking", "continue", "no_progress", "blocked"} + and not result.failure + ): + raise RefusalError("review sequence requires failure evidence") + return result + + +def policy(git_dir: Path, candidate: Candidate, invocation: int) -> Findings: + path = git_dir / "fixture-review-sequence" + if path.is_file(): + return sequence(path, invocation) + for filename, outcome in ( + ("inconclusive", "inconclusive"), + ("blocked", "unavailable"), + ("fail", "blocking"), + ): + if (git_dir / f"fixture-review-{filename}").is_file(): + return Findings(outcome) + patterns = lines(git_dir / "fixture-review-reject-pattern") + if patterns and patterns[0] and patterns[0].encode() in candidate.packet: + return Findings( + "blocking", + f"Fixture policy rejects changed content containing: {patterns[0]}", + ) + return Findings() + + +def constrain(findings: Findings, mode: str, target: str, history: list[str]) -> None: + if mode == "comprehensive": + if findings.outcome not in {"clear", "blocking", "inconclusive", "unavailable"}: + raise RefusalError("comprehensive fixture outcome is invalid") + return + if findings.outcome == "blocking": + findings.outcome = "continue" + prior_targets = {row.split("\t")[0] for row in history} + if ( + findings.outcome not in {"inconclusive", "unavailable", "blocked"} + and target in prior_targets + ): + findings.outcome = "no_progress" + findings.failure = "Target fingerprint repeats an earlier convergence state" + + +def conclusion(findings: Findings, mode: str) -> str: + if mode == "verify": + return f"Fix verification: {findings.outcome}.\n" + wording = {"clear": "no blocking findings", "blocking": "blocking findings"} + return f"Independent review outcome: {wording.get(findings.outcome, findings.outcome)}\n" + + +def details(findings: Findings, git_dir: Path) -> str: + outcome = findings.outcome + if outcome == "clear": + return "Summary: The current change has no blocking findings against the supplied goal and repository instructions.\n" + if outcome in {"blocking", "continue", "no_progress"}: + guidance = git_dir / "fixture-review-repair-guidance" + extra = ( + read_text(guidance, "repair guidance is unreadable") + if guidance.exists() + else "" + ) + return f"Blocking finding: {findings.failure}\n" + extra + gaps = { + "blocked": findings.failure, + "inconclusive": "The review could not reach a reliable conclusion from the available evidence.", + "unavailable": "Required independent review evidence is unavailable.", + } + return f"Evidence gap: {gaps[outcome]}\n" + + +def report( + findings: Findings, mode: str, candidate: Candidate, repo: Path, git_dir: Path +) -> str: + result = conclusion(findings, mode) + f"Reviewed target: {candidate.target}\n" + result += "".join(f"Changed file: {repo / name}\n" for name in candidate.changed) + result += details(findings, git_dir) + for label, value in ( + ("Advisory finding", findings.advisory), + ("Unrelated observation excluded", findings.unrelated), + ): + if value: + result += f"{label}: {value}\n" + return result diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/state.py b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/state.py new file mode 100644 index 00000000..3b5a87b2 --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/state.py @@ -0,0 +1,65 @@ +"""Passive eval evidence and byte-compatible candidate fingerprints.""" + +from __future__ import annotations + +from dataclasses import dataclass +from pathlib import Path + +from ..common import git, git_text + + +def metadata(repo: Path) -> Path: + return Path(git_text(repo, "rev-parse", "--absolute-git-dir")).resolve() + + +def append(path: Path, text: str) -> None: + with path.open("a", encoding="utf-8", newline="\n") as stream: + stream.write(text) + + +def lines(path: Path) -> list[str]: + return path.read_text(encoding="utf-8").splitlines() if path.exists() else [] + + +def oid(repo: Path, data: bytes) -> str: + return git(repo, "hash-object", "--stdin", data=data).decode().strip() + + +@dataclass(frozen=True) +class Candidate: + target: str + packet: bytes + changed: tuple[str, ...] + + +def capture(repo: Path) -> Candidate: + packet = git(repo, "diff", "--binary", "--full-index", "HEAD", "--") + changed = git(repo, "diff", "--name-only", "-z", "HEAD", "--") + untracked = git(repo, "ls-files", "--others", "--exclude-standard", "-z") + for name in untracked.split(b"\0")[:-1]: + path = name.decode("utf-8") + object_id = git(repo, "hash-object", "--", path).strip() + packet += b"untracked_path\0" + name + b"\0untracked_oid\0" + object_id + b"\0" + packet += (repo / path).read_bytes() + b"\0" + changed += name + b"\0" + base = git_text(repo, "rev-parse", "HEAD") + return Candidate( + f"WORKTREE@{base}+{oid(repo, packet)}", + packet, + tuple(name.decode("utf-8") for name in changed.split(b"\0")[:-1]), + ) + + +def checksum(data: bytes) -> str: + """POSIX cksum, including its length suffix, without a Unix executable.""" + crc = 0 + for byte in data + length_bytes(len(data)): + crc ^= byte << 24 + for _ in range(8): + crc = (crc << 1) ^ (0x04C11DB7 if crc & 0x80000000 else 0) + crc &= 0xFFFFFFFF + return f"{crc ^ 0xFFFFFFFF} {len(data)}" + + +def length_bytes(length: int) -> bytes: + return length.to_bytes((length.bit_length() + 7) // 8, "little") diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/verification.py b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/verification.py new file mode 100644 index 00000000..a295eb2f --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/fixtures/verification.py @@ -0,0 +1,126 @@ +"""Bounded synthetic combined assessment; never an owner or repair controller.""" + +from __future__ import annotations + +import tempfile +import time +from pathlib import Path + +from ..common import CheckRequiredError, RefusalError +from . import review +from .state import append, checksum, lines, metadata + + +def prerequisites(state: Path, target: str, mode: str) -> str: + modes = {"initial": "comprehensive", "follow-up": "verify"} + if mode not in modes: + raise RefusalError("initial or follow-up required") + if lines(state / "check-target") != [target]: + raise CheckRequiredError("blocked: successful current checks required") + history = state / "verification-invocations" + if mode == "initial" and history.exists(): + raise CheckRequiredError("initial verification already recorded") + if mode == "follow-up" and not lines(history): + raise CheckRequiredError("follow-up requires initial verification") + return modes[mode] + + +def conclusion(target: str, current: str, review_report: str, qa: str) -> str: + first = review_report.splitlines()[0] + blocked = { + "Independent review outcome: inconclusive", + "Independent review outcome: unavailable", + "Fix verification: blocked.", + "Fix verification: inconclusive.", + "Fix verification: unavailable.", + } + if target != current or first in blocked: + return "blocked" + if ( + first + in { + "Independent review outcome: no blocking findings", + "Fix verification: clear.", + } + and qa == "clear" + ): + return "clear" + return "no-progress" if first == "Fix verification: no_progress." else "progress" + + +def qa_result(subject: Path) -> tuple[str, str]: + data = subject.read_bytes() + return checksum( + data + ), "clear" if b"export const CACHE_SIZE = 5;" in data.splitlines() else "blocking" + + +def report( + repo: Path, + target: str, + mode: str, + review_report: str, + qa: str, + qa_target: str, + outcome: str, + artifact: Path, +) -> str: + result = ( + f"Repository: {repo}\nCandidate: src/config.js cksum {target}, HEAD to WORKTREE\nMode: {mode}\n" + "Selected: independent code review and QA-like cache assessment\n" + "Required current check: bash check.sh passed for this candidate.\n" + "Complete independent review result:\n" + + review_report + + f"Complete QA-like result: {qa}; candidate cksum {qa_target}.\n" + ) + if qa == "blocking": + observed = "".join( + row + "\n" + for row in (repo / "src/config.js").read_text().splitlines() + if "CACHE_SIZE" in row + ) + result += ( + "Q1, QA/cache, blocking: CACHE_SIZE must equal 5; observed:\n" + observed + ) + else: + result += "Q1, QA/cache: CACHE_SIZE = 5 observed; resolved in follow-up, no direct regression.\n" + return result + ( + "Acceptance C1: TIMEOUT_MS = 2500 supported by current check.\n" + "Acceptance C2: RETRY_COUNT = 3 assessed in the complete review result (R1).\n" + "Acceptance C3: CACHE_SIZE = 5 assessed by QA (Q1).\n" + f"Conclusion: {outcome}\n" + "Finding provenance: R1 review/retries; Q1 QA/cache; both blocking when unresolved. No advisories or direct regressions observed.\n" + f"Target and assessment history: {artifact.parent}/verification-invocations\nComplete review result: {artifact}\n" + ) + + +def assess(repo: Path, mode: str) -> str: + repo = repo.resolve() + state = metadata(repo) / "fixture-state" + subject = repo / "src/config.js" + target = checksum(subject.read_bytes()) + review_mode = prerequisites(state, target, mode) + events = state / "verification-events" + append(events, f"assessment-start\t{mode}\t{target}\n") + review_report = review.assess( + repo, review_mode, "independent-review-skill-contract-v1" + ) + with tempfile.NamedTemporaryFile( + mode="w", + encoding="utf-8", + newline="\n", + prefix="review-result.", + dir=state, + delete=False, + ) as stream: + stream.write(review_report) + artifact = Path(stream.name) + append(events, f"review-returned\t{mode}\t{checksum(subject.read_bytes())}\n") + time.sleep(2) + qa_target, qa = qa_result(subject) + append(events, f"qa-returned\t{mode}\t{qa_target}\n") + outcome = conclusion(target, qa_target, review_report, qa) + append(state / "verification-invocations", f"{mode}\t{target}\t{outcome}\n") + output = report(repo, target, mode, review_report, qa, qa_target, outcome, artifact) + append(events, f"assessment-returned\t{mode}\t{checksum(subject.read_bytes())}\n") + return output diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/preflight.py b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/preflight.py new file mode 100644 index 00000000..88a98ff4 --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/preflight.py @@ -0,0 +1,142 @@ +"""Prepare immutable repository evidence and resolve one selected route.""" + +from __future__ import annotations + +import re +from pathlib import Path + +from .arguments import options, require +from .common import PLUGIN, RefusalError, git_text, read_text, record, repository +from .routes import catalog, validate_owner, validate_tuple + +USAGE = """usage: + adaptive-delivery-preflight prepare --repo --host + adaptive-delivery-preflight route --repo --host --profile [--route ] +""" +WORKFLOWS = ( + "fix-bug", + "implement-feature", + "change-feature", + "refactor", + "migration", + "mechanical", +) + + +def observation(repo: Path, label: str, *args: str) -> str: + try: + return git_text(repo, *args) + except (RefusalError, OSError) as error: + raise RefusalError(f"cannot read {label}: {repo}") from error + + +def instruction_records(repo: Path) -> str: + result = "" + for name in ("AGENTS.md", "CLAUDE.md"): + path = repo / name + if path.exists() or path.is_symlink(): + read_text(path, "repository instruction is unreadable") + result += record("instruction", str(path)) + return result + + +def workflow_records(directory: Path) -> str: + result = "" + for name in WORKFLOWS: + path = directory / f"{name}.md" + read_text(path, "workflow document is unreadable") + result += record("workflow", name, str(path)) + return result + + +def prepare(repo: Path, host: str, plugin: Path) -> str: + workflows = plugin / "skills/adaptive-delivery/references/workflows" + if not workflows.is_dir(): + raise RefusalError(f"workflow directory is missing: {workflows}") + revision = observation(repo, "repository revision", "rev-parse", "HEAD") + status = observation( + repo, "working tree state", "status", "--porcelain=v1", "--untracked-files=all" + ) + routes = [route for route in catalog(repo, plugin) if route.host == host] + result = record("format", "darrow-native-goal-prepared-v2") + result += record("repo", str(repo)) + record("base_revision", revision) + result += record("working_tree", "dirty" if status else "clean") + result += instruction_records(repo) + for route in routes: + validate_owner(host, route.model) + result += record("route", route.profile, *route.tuple) + result += record("route_policy_source", route.profile, route.source) + if not routes: + raise RefusalError(f"no routes for host={host}") + return result + workflow_records(workflows) + + +def explicit_tuple(value: str, host: str) -> tuple[str, str, str, str]: + fields = value.split("|") + if len(fields) != 4 or not all(fields): + raise RefusalError("--route must be harness|provider|model|effort") + route = (fields[0], fields[1], fields[2], fields[3]) + validate_tuple("explicit route", host, route) + return route + + +def selected(repo: Path, host: str, profile: str, explicit: str, plugin: Path) -> str: + source = "user" if explicit else "policy" + provenance = "" + if explicit: + route = explicit_tuple(explicit, host) + else: + match = next( + (item for item in catalog(repo, plugin) if item.key == (host, profile)), + None, + ) + if match is None: + raise RefusalError(f"no route for host={host} profile={profile}") + route = match.tuple + provenance = record("policy_route_source", match.source) + validate_owner(host, route[2]) + return ( + record("format", "darrow-native-goal-route-v2") + + record("profile", profile) + + record("selected_route", *route) + + record("route_source", source) + + provenance + ) + + +def parse(args: list[str]) -> tuple[str, dict[str, str]]: + command, *rest = args + if command not in {"prepare", "route"}: + raise RefusalError(f"unknown command: {command}") + names = {"--repo", "--host"} + required = {"repo", "host"} + message = "prepare requires --repo and --host" + if command == "route": + names |= {"--profile", "--route"} + required.add("profile") + message = "route requires --repo, --host, and --profile" + values = options(rest, names, command + " ") + require(values, required, message) + return command, values + + +def run(args: list[str], plugin: Path = PLUGIN) -> str: + if not args: + raise RefusalError(USAGE.rstrip()) + if args[0] in {"-h", "--help", "help"}: + return USAGE + command, values = parse(args) + return dispatch(command, values, plugin) + + +def dispatch(command: str, values: dict[str, str], plugin: Path) -> str: + host = values["host"] + if host not in {"codex", "claude"}: + raise RefusalError(f"unsupported host: {host}") + profile = values.get("profile", "routine") + if not re.fullmatch(r"[a-z0-9-]+", profile): + raise RefusalError(f"unsupported profile: {profile}") + repo = repository(values["repo"]) + if command == "prepare": + return prepare(repo, host, plugin) + return selected(repo, host, profile, values.get("route", ""), plugin) diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/py.typed b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/routes.py b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/routes.py new file mode 100644 index 00000000..4f879919 --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/src/darrow_adaptive_delivery/routes.py @@ -0,0 +1,171 @@ +"""Parse the route catalog and apply repository policy without choosing intent.""" + +from __future__ import annotations + +import json +import re +from dataclasses import dataclass, replace +from pathlib import Path + +from .common import RefusalError, read_text + +FIELDS = ( + "host", + "profile", + "harness", + "provider", + "model", + "effort", + "fallbackModel", + "fallbackEffort", +) +SAFE_VALUE = re.compile(r"[A-Za-z0-9][A-Za-z0-9._-]*\Z") +EFFORTS = {"low", "medium", "high", "xhigh", "max", "ultra"} + + +class Pairs(list[tuple[str, object]]): + """Retain duplicate object keys; reviewers remain opaque to this helper.""" + + +def invalid(reason: str) -> RefusalError: + return RefusalError(f"invalid route configuration: {reason}") + + +def object_fields( + value: object, allowed: tuple[str, ...], label: str +) -> dict[str, object]: + if not isinstance(value, Pairs): + raise invalid(f"{label} must be an object") + fields: dict[str, object] = {} + for key, item in value: + if key not in allowed: + raise invalid(f"unknown {label} field: {key}") + if key in fields: + raise invalid(f"duplicate {label} field: {key}") + fields[key] = item + return fields + + +def safe_field(name: str, value: object) -> str: + if not isinstance(value, str) or not SAFE_VALUE.fullmatch(value): + raise invalid(f"unsafe or empty {name} value") + return value + + +@dataclass(frozen=True) +class Route: + host: str + profile: str + harness: str + provider: str + model: str + effort: str + fallback_model: str + fallback_effort: str + source: str = "bundled" + + @property + def key(self) -> tuple[str, str]: + return self.host, self.profile + + @property + def tuple(self) -> tuple[str, str, str, str]: + return self.harness, self.provider, self.model, self.effort + + +def parse_route(value: object) -> Route: + fields = object_fields(value, FIELDS, "route") + if len(fields) != 8: + raise invalid("each route must contain exactly eight fields") + return Route(*(safe_field(name, fields[name]) for name in FIELDS)) + + +def reject_constant(value: str) -> object: + raise invalid(f"invalid JSON value: {value}") + + +def parse_document(text: str, allow_missing: bool = False) -> list[Route]: + try: + value: object = json.loads( + text, object_pairs_hook=Pairs, parse_constant=reject_constant + ) + except (ValueError, RecursionError) as error: + raise invalid(str(error)) from error + root = object_fields(value, ("routes", "reviewers"), "root") + if "routes" not in root and not allow_missing: + raise invalid("root object must contain routes") + return parse_routes(root.get("routes", []), allow_missing) + + +def parse_routes(value: object, allow_empty: bool) -> list[Route]: + if not isinstance(value, list) or isinstance(value, Pairs): + raise invalid("routes must be an array") + if not value and not allow_empty: + raise invalid("bundled routes array must not be empty") + routes = [parse_route(item) for item in value] + seen: set[tuple[str, str]] = set() + for route in routes: + if route.key in seen: + raise invalid( + f"duplicate route for host={route.host} profile={route.profile}" + ) + seen.add(route.key) + return routes + + +def validate_tuple(label: str, host: str, route: tuple[str, str, str, str]) -> None: + harness, provider, model, effort = route + if harness != host: + raise RefusalError(f"{label} must use current host: {host}") + if (host, provider) not in {("codex", "openai"), ("claude", "anthropic")}: + raise RefusalError(f"{label} provider does not match host: {host}") + if model == "none" or not SAFE_VALUE.fullmatch(model): + raise RefusalError(f"{label} has no concrete safe model") + if effort not in EFFORTS: + raise RefusalError(f"{label} has unsupported effort: {effort}") + + +def validate_owner(host: str, model: str) -> None: + if (host, model) == ("codex", "gpt-5.6-luna"): + raise RefusalError( + f"model is not eligible for adaptive-delivery ownership: {model}" + ) + + +def repository_routes(repo: Path) -> list[Route]: + path = repo / ".darrow/config.json" + if not path.exists() and not path.is_symlink(): + return [] + if path.is_symlink() or not path.is_file(): + raise RefusalError( + f"repository route configuration is unreadable or unsafe: {path}" + ) + text = read_text(path, "repository route configuration is unreadable or unsafe") + try: + return parse_document(text, allow_missing=True) + except RefusalError as error: + raise RefusalError( + f"{error}\nrepository route configuration is invalid: {path}" + ) from error + + +def catalog(repo: Path, plugin: Path) -> list[Route]: + bundled = parse_document( + read_text(plugin / "config/routes.json", "route configuration is unreadable") + ) + overrides = repository_routes(repo) + for source, routes in (("bundled", bundled), ("repository", overrides)): + for route in routes: + validate_tuple( + f"{source} route for profile={route.profile}", route.host, route.tuple + ) + keys = {route.key for route in bundled} + for route in overrides: + if route.key not in keys: + raise RefusalError( + f"repository route is not in bundled host/profile catalog: host={route.host} profile={route.profile}" + ) + replacements = { + route.key: replace(route, source="repository") for route in overrides + } + return [replacements.get(route.key, route) for route in bundled] diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/tests/conftest.py b/plugins/orchestration/darrow-adaptive-delivery/backend/tests/conftest.py new file mode 100644 index 00000000..5edcb615 --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/tests/conftest.py @@ -0,0 +1,32 @@ +from __future__ import annotations + +import os +from pathlib import Path + +import pytest + +from darrow_adaptive_delivery.common import git + + +@pytest.fixture(autouse=True) +def environment(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: + for key in tuple(os.environ): + if key.startswith(("GIT_", "CLAUDE_CODE_")) or key == "ANTHROPIC_BASE_URL": + monkeypatch.delenv(key) + monkeypatch.setenv("GIT_CONFIG_NOSYSTEM", "1") + monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / "config")) + + +@pytest.fixture +def repo(tmp_path: Path) -> Path: + path = tmp_path / "repo with spaces-é" + path.mkdir() + git(path, "init", "-qb", "main") + git(path, "config", "user.name", "Fixture") + git(path, "config", "user.email", "fixture@example.invalid") + git(path, "config", "core.autocrlf", "false") + (path / "value.txt").write_bytes(b"before\n") + git(path, "add", ".") + git(path, "commit", "-qm", "fixture") + (path / ".git/fixture-state").mkdir() + return path.resolve() diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/tests/fresh_install.py b/plugins/orchestration/darrow-adaptive-delivery/backend/tests/fresh_install.py new file mode 100644 index 00000000..c0e730f1 --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/tests/fresh_install.py @@ -0,0 +1,238 @@ +"""Exercise the copied plugin using only frozen runtime dependencies on each OS.""" + +from __future__ import annotations + +import os +import shutil +import subprocess +import tempfile +from pathlib import Path + + +def command(cwd: Path, *args: str, expected: int = 0) -> str: + result = subprocess.run( + args, cwd=cwd, capture_output=True, text=True, encoding="utf-8", check=False + ) + if result.returncode != expected: + raise RuntimeError( + f"{args[0]} exited {result.returncode}: {result.stdout}{result.stderr}" + ) + return result.stdout + + +def runtime(backend: Path, cwd: Path, *args: str, expected: int = 0) -> str: + return command( + cwd, + "uv", + "run", + "--quiet", + "--frozen", + "--no-dev", + "--project", + str(backend), + *args, + expected=expected, + ) + + +def repository(path: Path) -> None: + path.mkdir() + command(path, "git", "init", "-qb", "main") + command(path, "git", "config", "user.name", "Fresh fixture") + command(path, "git", "config", "user.email", "fixture@example.invalid") + command(path, "git", "config", "core.autocrlf", "false") + (path / "value.txt").write_bytes(b"before\n") + command(path, "git", "add", ".") + command(path, "git", "commit", "-qm", "fixture") + + +def routes(backend: Path, repo: Path) -> None: + before = command(repo, "git", "status", "--porcelain=v1") + for host in ("codex", "claude"): + prepared = runtime( + backend, + repo, + "adaptive-delivery-preflight", + "prepare", + "--repo", + str(repo), + "--host", + host, + ) + assert f"repo\t{repo.resolve()}\n" in prepared + assert "working_tree\tclean\n" in prepared + for profile in ("routine", "routine-plus", "scaled", "repo-wide", "judgment"): + route = runtime( + backend, + repo, + "adaptive-delivery-preflight", + "route", + "--repo", + str(repo), + "--host", + host, + "--profile", + profile, + ) + assert f"selected_route\t{host}\t" in route + assert "policy_route_source\tbundled\n" in route + for model, effort in ( + ("claude-sonnet-5", "low"), + ("claude-sonnet-5", "medium"), + ("claude-opus-5", "high"), + ): + assert "subagent_type\tdarrow-adaptive-delivery:" in runtime( + backend, + repo, + "claude-agent-route", + "--provider", + "anthropic", + "--model", + model, + "--effort", + effort, + ) + runtime( + backend, + repo, + "claude-agent-route", + "--provider", + "anthropic", + "--model", + "claude-opus-5", + "--effort", + "low", + expected=2, + ) + assert command(repo, "git", "status", "--porcelain=v1") == before + + +def fixtures(plugin: Path, repo: Path) -> None: + backend = plugin / "backend" + fixture_dir = plugin / "skills/adaptive-delivery/evals/fixtures" + (repo / ".readiness-verdict").write_text( + "needs-decision-then-ready\n", encoding="utf-8" + ) + runtime( + backend, + repo, + "adaptive-delivery-fixture", + "install", + "readiness", + str(repo), + str(fixture_dir), + "both", + ) + installed = repo / ".agents/backend" + for expected in ("needs-decision", "ready"): + assert f"**Verdict:** `{expected}`" in runtime( + installed, repo, "adaptive-delivery-fixture", "readiness", str(repo) + ) + runtime( + backend, + repo, + "adaptive-delivery-fixture", + "install", + "verification", + str(repo), + str(fixture_dir), + "both", + ) + candidate = repo / "src/config.js" + candidate.parent.mkdir() + candidate.write_bytes( + b"export const TIMEOUT_MS = 2500;\nexport const RETRY_COUNT = 3;\nexport const CACHE_SIZE = 5;\n" + ) + checksum = runtime( + installed, + repo, + "python", + "-c", + "from pathlib import Path; from darrow_adaptive_delivery.fixtures.state import checksum; print(checksum(Path('src/config.js').read_bytes()))", + ) + (repo / ".git/fixture-state/check-target").write_text( + checksum, encoding="utf-8", newline="\n" + ) + assert "Conclusion: clear\n" in runtime( + installed, + repo, + "adaptive-delivery-fixture", + "verification", + str(repo), + "initial", + ) + assert "Conclusion: no-progress\n" in runtime( + repo / ".claude/backend", + repo, + "adaptive-delivery-fixture", + "verification", + str(repo), + "follow-up", + ) + assert "WORKTREE@" in runtime( + installed, repo, "adaptive-delivery-fixture", "review", str(repo), "fingerprint" + ) + runtime( + installed, repo, "adaptive-delivery-fixture", "proof", "current", expected=1 + ) + + +def validate(plugin: Path, temporary: Path) -> None: + backend = plugin / "backend" + assert not (plugin / "bin").exists() + command(temporary, "uv", "sync", "--frozen", "--no-dev", "--project", str(backend)) + tree = command( + temporary, "uv", "tree", "--frozen", "--no-dev", "--project", str(backend) + ) + assert not any( + name in tree for name in ("pytest", "ruff", "mypy", "coverage", "hypothesis") + ) + repo = temporary / "repository ' with spaces-é" + repository(repo) + routes(backend, repo) + linked = temporary / "linked worktree-é" + command(repo, "git", "worktree", "add", "-qb", "linked", str(linked)) + nested = linked / "nested" + nested.mkdir() + output = runtime( + backend, + temporary, + "adaptive-delivery-preflight", + "prepare", + "--repo", + str(nested), + "--host", + "codex", + ) + assert f"repo\t{linked.resolve()}\n" in output + fixtures(plugin, repo) + + +def main() -> None: + for key in tuple(os.environ): + if key.startswith(("GIT_", "CLAUDE_CODE_")) or key == "ANTHROPIC_BASE_URL": + del os.environ[key] + os.environ["GIT_CONFIG_NOSYSTEM"] = "1" + plugin = Path(__file__).resolve().parents[2] + ignored = shutil.ignore_patterns( + ".venv", + "__pycache__", + ".hypothesis", + ".mypy_cache", + ".pytest_cache", + ".ruff_cache", + ".coverage*", + "coverage.json", + ) + with tempfile.TemporaryDirectory(prefix="darrow adaptive fresh ") as directory: + temporary = Path(directory).resolve() + copied = temporary / "plugin ' copy-é" + shutil.copytree(plugin, copied, ignore=ignored) + validate(copied, temporary) + print( + "fresh copied adaptive-delivery: both host routes, linked worktree, readiness, review, verification, and proof refusal passed; native owner launch remains host-owned" + ) + + +if __name__ == "__main__": + main() diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/tests/test_editable_install.py b/plugins/orchestration/darrow-adaptive-delivery/backend/tests/test_editable_install.py new file mode 100644 index 00000000..eab70b1a --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/tests/test_editable_install.py @@ -0,0 +1,72 @@ +"""Exercise installed path records under the older Windows startup encoding.""" + +from __future__ import annotations + +import csv +import hashlib +import io +import shutil +import subprocess +import sys +from base64 import urlsafe_b64encode +from pathlib import Path +from zipfile import ZipFile + +import pytest + +from darrow_adaptive_delivery._build_backend import build_editable +from darrow_adaptive_delivery.common import PLUGIN + + +def startup_records(artifact: Path) -> str: + with ZipFile(artifact) as wheel: + startup = "\n".join( + wheel.read(name).decode("cp1252") + for name in wheel.namelist() + if name.endswith(".pth") + ) + record = next( + name for name in wheel.namelist() if name.endswith(".dist-info/RECORD") + ) + for name, digest, size in csv.reader(io.StringIO(wheel.read(record).decode())): + if name == record: + continue + data = wheel.read(name) + assert ( + digest + == "sha256=" + + urlsafe_b64encode(hashlib.sha256(data).digest()).rstrip(b"=").decode() + ) + assert size == str(len(data)) + return startup + + +def test_editable_startup_with_legacy_path_decoding( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + backend = tmp_path / "package with spaces-é漢" / "backend" + shutil.copytree( + PLUGIN / "backend", + backend, + ignore=shutil.ignore_patterns(".venv", "__pycache__", ".*cache", "tests"), + ) + monkeypatch.chdir(backend) + wheels = tmp_path / "wheels" + wheels.mkdir() + artifact = wheels / build_editable(str(wheels)) + startup = startup_records(artifact) + code = ( + "import sys, os\n" + f"for line in {startup!r}.splitlines():\n" + " if line.startswith(('import ', 'import\\t')):\n" + " exec(line)\n" + " elif os.path.exists(line):\n" + " sys.path.append(line)\n" + "from darrow_adaptive_delivery.cli import preflight\n" + "raise SystemExit(preflight())\n" + ) + result = subprocess.run( + [sys.executable, "-S", "-c", code, "--help"], capture_output=True, check=False + ) + assert result.returncode == 0, result.stderr + assert b"adaptive-delivery-preflight prepare" in result.stdout diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/tests/test_fixtures.py b/plugins/orchestration/darrow-adaptive-delivery/backend/tests/test_fixtures.py new file mode 100644 index 00000000..165c507e --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/tests/test_fixtures.py @@ -0,0 +1,326 @@ +from __future__ import annotations + +import sys +from pathlib import Path + +import pytest + +from darrow_adaptive_delivery.common import ( + PLUGIN, + CheckRequiredError, + RefusalError, + git, +) +from darrow_adaptive_delivery.fixtures import ( + cli, + install, + readiness, + review, + verification, +) +from darrow_adaptive_delivery.fixtures.state import capture, checksum, metadata + +CONTRACT = "independent-review-skill-contract-v1" +FIXTURES = PLUGIN / "skills/adaptive-delivery/evals/fixtures" + + +def test_readiness_reports_and_iterations(repo: Path) -> None: + verdict = repo / ".readiness-verdict" + for name in ("ready", "needs-decision", "needs-discovery", "blocked"): + verdict.write_text(name + "\n") + output = readiness.assess(repo) + assert output.startswith( + f"## Implementation readiness\n\n**Verdict:** `{name}`\n" + ) + assert "### Required next action" in output + state = repo / ".git/fixture-state" + invocations = state / "implementation-readiness-invocations" + assert ( + invocations.read_text() == "ready\nneeds-decision\nneeds-discovery\nblocked\n" + ) + assert (state / "implementation-readiness-pre-status").read_bytes() == git( + repo, "status", "--porcelain", "--untracked-files=all" + ) + invocations.unlink() + verdict.write_text("needs-decision-then-ready\n") + assert "`needs-decision`" in readiness.assess(repo, "render-only") + assert not invocations.exists() + assert "`needs-decision`" in readiness.assess(repo) + assert "`ready`" in readiness.assess(repo) + + +def test_readiness_refusals(repo: Path) -> None: + with pytest.raises(RefusalError, match="mode"): + readiness.assess(repo, "invalid") + with pytest.raises(RefusalError, match=r"missing \.readiness-verdict"): + readiness.assess(repo) + (repo / ".readiness-verdict").write_text("unknown\n") + with pytest.raises(RefusalError, match="unsupported verdict"): + readiness.assess(repo) + (repo / ".readiness-verdict").write_text("") + with pytest.raises(RefusalError, match="unsupported verdict"): + readiness.assess(repo) + + +@pytest.mark.parametrize("host", ["codex", "claude", "both"]) +@pytest.mark.parametrize("kind", ["readiness", "review", "verification"]) +def test_install_isolated_providers(repo: Path, host: str, kind: str) -> None: + (repo / ".readiness-verdict").write_text("blocked\n") + assert install.install(kind, repo, FIXTURES, host) == "" + for root in {".agents", ".claude"} - set(install.HOSTS[host]): + assert not (repo / root).exists() + for root in install.HOSTS[host]: + assert (repo / root / "backend/uv.lock").is_file() + assert not (repo / root / "backend/.venv").exists() + assert not (repo / root / "bin").exists() + name = install.SKILLS[kind][1] + content = (repo / root / "skills" / name / "SKILL.md").read_text() + source = FIXTURES / install.SKILLS[kind][0] + assert content == (source / install.template(kind, root)).read_text() + assert not (source / "SKILL.md").exists() + assert not (repo / root / "backend/tests").exists() + if kind == "review": + matches = [ + path.relative_to(repo / root).as_posix() + for path in (repo / root).rglob("*") + if path.is_file() and CONTRACT.encode() in path.read_bytes() + ] + assert matches == ["skills/independent-code-review/SKILL.md"] + with pytest.raises(RefusalError, match="install requires"): + install.install(kind, repo, FIXTURES, "invalid") + + +def test_review_fingerprint_tracks_all_candidate_content(repo: Path) -> None: + before = review.assess(repo, "fingerprint") + (repo / "value.txt").write_bytes(b"after\n") + changed = review.assess(repo, "fingerprint") + assert changed != before + unusual = repo / "untracked-é space.txt" + unusual.write_bytes(b"one\x00two") + untracked = review.assess(repo, "fingerprint") + assert untracked != changed + unusual.write_bytes(b"replacement") + assert review.assess(repo, "fingerprint") != untracked + report = review.assess(repo, "comprehensive", CONTRACT) + assert f"Changed file: {unusual}\n" in report + assert "no blocking findings" in report + with pytest.raises(RefusalError, match="already established"): + review.assess(repo, "comprehensive", CONTRACT) + assert "Fix verification: no_progress." in review.assess(repo, "verify", CONTRACT) + + +def test_review_requires_contract_and_original(repo: Path) -> None: + for mode, contract, error in ( + ("bad", "", "usage"), + ("comprehensive", "bad", "contract"), + ("verify", CONTRACT, "original comprehensive"), + ): + with pytest.raises(RefusalError, match=error): + review.assess(repo, mode, contract) + + +@pytest.mark.parametrize( + "flag,outcome", + [ + ("fail", "blocking"), + ("blocked", "unavailable"), + ("inconclusive", "inconclusive"), + ], +) +def test_review_flags(repo: Path, flag: str, outcome: str) -> None: + git_dir = metadata(repo) + (git_dir / f"fixture-review-{flag}").touch() + (git_dir / "fixture-review-repair-guidance").write_text( + "Advisory repair guidance\n" + ) + output = review.assess(repo, "comprehensive", CONTRACT) + assert outcome in output + if flag == "fail": + assert "Advisory repair guidance\n" in output + + +@pytest.mark.parametrize("final", ["clear", "continue", "no_progress", "unavailable"]) +def test_review_closed_sequence_and_history(repo: Path, final: str) -> None: + git_dir = metadata(repo) + (git_dir / "fixture-review-sequence").write_text( + "blocking\tOriginal blocker\tAdvisory\tUnrelated\nblocking\tNarrowed cause\n" + f"{final}\tFinal observation\n" + ) + result = review.assess(repo, "comprehensive", CONTRACT) + assert ( + "Advisory finding: Advisory\nUnrelated observation excluded: Unrelated\n" + in result + ) + (repo / "value.txt").write_text("repaired once\n") + assert "Fix verification: continue." in review.assess(repo, "verify", CONTRACT) + (repo / "value.txt").write_text("repaired twice\n") + assert f"Fix verification: {final}." in review.assess(repo, "verify", CONTRACT) + history = (git_dir / "fixture-state/independent-review-invocations").read_text() + rows = [row.split("\t") for row in history.splitlines()] + assert [row[2] for row in rows] == ["comprehensive", "verify", "verify"] + assert len({row[0] for row in rows}) == 3 + with pytest.raises(RefusalError, match="already established"): + review.assess(repo, "comprehensive", CONTRACT) + with pytest.raises(RefusalError, match="exhausted"): + review.assess(repo, "verify", CONTRACT) + + +@pytest.mark.parametrize( + "line,error", + [ + ("invalid\tx", "invalid review"), + ("blocked", "requires failure"), + ("continue\tx", "comprehensive fixture"), + ("", "exhausted"), + ], +) +def test_invalid_sequences(repo: Path, line: str, error: str) -> None: + (repo / ".git/fixture-review-sequence").write_text(line + "\n") + with pytest.raises(RefusalError, match=error): + review.assess(repo, "comprehensive", CONTRACT) + + +def test_blocked_followup_and_repeat_protection(repo: Path) -> None: + path = repo / ".git/fixture-review-sequence" + path.write_text("clear\nblocked\tUnavailable input\n") + review.assess(repo, "comprehensive", CONTRACT) + output = review.assess(repo, "verify", CONTRACT) + assert "Fix verification: blocked.\n" in output + assert "Evidence gap: Unavailable input\n" in output + + +def test_review_subject_and_mutation( + repo: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + git_dir = metadata(repo) + (git_dir / "fixture-review-event-path").write_text("value.txt\n") + (git_dir / "fixture-review-delay-seconds").write_text("1\n") + + def mutate(_seconds: float) -> None: + (repo / "value.txt").write_text("changed during assessment\n") + + monkeypatch.setattr("darrow_adaptive_delivery.fixtures.review.time.sleep", mutate) + assert "outcome: inconclusive" in review.assess(repo, "comprehensive", CONTRACT) + events = (git_dir / "fixture-state/review-events").read_text().splitlines() + assert events[0].split("\t")[1] != events[1].split("\t")[1] + assert events[2].startswith("review\tinconclusive\t") + + +def test_review_stable_subject(repo: Path) -> None: + (repo / ".git/fixture-review-event-path").write_text("value.txt\n") + assert "no blocking findings" in review.assess(repo, "comprehensive", CONTRACT) + + +@pytest.mark.parametrize( + "subject,delay,error", + [ + ("missing", None, "subject"), + ("", None, "subject"), + ("value.txt", "invalid", "delay"), + ("value.txt", "", "delay"), + ], +) +def test_review_observation_refusals( + repo: Path, subject: str, delay: str | None, error: str +) -> None: + (repo / ".git/fixture-review-event-path").write_text(subject + "\n") + if delay is not None: + (repo / ".git/fixture-review-delay-seconds").write_text(delay + "\n") + with pytest.raises(RefusalError, match=error): + review.assess(repo, "comprehensive", CONTRACT) + + +def write_candidate(repo: Path, retries: int = 0, cache: int = 0) -> Path: + subject = repo / "src/config.js" + subject.parent.mkdir(exist_ok=True) + subject.write_bytes( + f"export const TIMEOUT_MS = 2500;\nexport const RETRY_COUNT = {retries};\nexport const CACHE_SIZE = {cache};\n".encode() + ) + return subject + + +def test_combined_assessment_requires_current_checks( + repo: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + monkeypatch.setattr( + "darrow_adaptive_delivery.fixtures.verification.time.sleep", + lambda _seconds: None, + ) + subject = write_candidate(repo) + state = repo / ".git/fixture-state" + (repo / ".git/fixture-review-reject-pattern").write_text("RETRY_COUNT = 0\n") + with pytest.raises(CheckRequiredError, match="current checks"): + verification.assess(repo, "initial") + (state / "check-target").write_text(checksum(subject.read_bytes()) + "\n") + with pytest.raises(CheckRequiredError, match="initial verification"): + verification.assess(repo, "follow-up") + output = verification.assess(repo, "initial") + assert "Conclusion: progress\n" in output + assert "Q1, QA/cache, blocking" in output + assert "Independent review outcome: blocking findings" in output + assert "Conclusion: no-progress\n" in verification.assess(repo, "follow-up") + with pytest.raises(CheckRequiredError, match="already recorded"): + verification.assess(repo, "initial") + write_candidate(repo, 3, 5) + with pytest.raises(CheckRequiredError, match="current checks"): + verification.assess(repo, "follow-up") + (state / "check-target").write_text(checksum(subject.read_bytes()) + "\n") + assert "Conclusion: clear\n" in verification.assess(repo, "follow-up") + events = (state / "verification-events").read_text().splitlines() + assert [row.split("\t")[0] for row in events[:4]] == [ + "assessment-start", + "review-returned", + "qa-returned", + "assessment-returned", + ] + with pytest.raises(RefusalError, match="initial or follow-up"): + verification.assess(repo, "bad") + + +@pytest.mark.parametrize( + "review_result", + [ + "Independent review outcome: inconclusive", + "Independent review outcome: unavailable", + "Fix verification: blocked.", + "Fix verification: inconclusive.", + "Fix verification: unavailable.", + ], +) +def test_unavailable_assessment_blocks(review_result: str) -> None: + assert verification.conclusion("same", "same", review_result, "clear") == "blocked" + assert ( + verification.conclusion("old", "new", "Fix verification: clear.", "clear") + == "blocked" + ) + + +def test_fixture_cli( + repo: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] +) -> None: + assert "usage:" in cli.dispatch([]) + assert cli.dispatch(["--help"]) == cli.USAGE + assert ( + cli.dispatch(["review", str(repo), "fingerprint"]) + == capture(repo).target + "\n" + ) + (repo / ".readiness-verdict").write_text("ready\n") + assert "`ready`" in cli.dispatch(["readiness", str(repo)]) + assert "`ready`" in cli.dispatch(["readiness", str(repo), "render-only"]) + assert cli.dispatch(["install", "review", str(repo), str(FIXTURES), "codex"]) == "" + assert "no blocking findings" in cli.dispatch( + ["review", str(repo), "comprehensive", CONTRACT] + ) + monkeypatch.setattr(sys, "argv", ["fixture", "invalid"]) + assert cli.main() == 2 + assert "usage:" in capsys.readouterr().err + write_candidate(repo) + monkeypatch.setattr(sys, "argv", ["fixture", "verification", str(repo), "initial"]) + assert cli.main() == 1 + assert "current checks required" in capsys.readouterr().out + monkeypatch.chdir(repo) + monkeypatch.setattr(sys, "argv", ["fixture", "proof", "invalid"]) + assert cli.main() == 1 + assert "review proof:" in capsys.readouterr().err + monkeypatch.setattr(sys, "argv", ["fixture", "proof", "complete", "missing"]) + assert cli.main() == 1 diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/tests/test_installed_fixtures.py b/plugins/orchestration/darrow-adaptive-delivery/backend/tests/test_installed_fixtures.py new file mode 100644 index 00000000..067fa92d --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/tests/test_installed_fixtures.py @@ -0,0 +1,228 @@ +"""Exercise copied providers through their frozen console entrypoints.""" + +from __future__ import annotations + +import subprocess +import time +from collections.abc import Callable +from pathlib import Path + +import pytest + +from darrow_adaptive_delivery.common import PLUGIN, git_text +from darrow_adaptive_delivery.fixtures import install +from darrow_adaptive_delivery.fixtures.state import checksum + +CONTRACT = "independent-review-skill-contract-v1" +FIXTURES = PLUGIN / "skills/adaptive-delivery/evals/fixtures" + + +def command(repo: Path, *args: str) -> list[str]: + return [ + "uv", + "run", + "--quiet", + "--frozen", + "--no-dev", + "--project", + str(repo / ".agents/backend"), + "adaptive-delivery-fixture", + *args, + ] + + +def invoke(repo: Path, *args: str, status: int = 0) -> str: + result = subprocess.run( + command(repo, *args), + capture_output=True, + timeout=60, + check=False, + ) + assert result.returncode == status, (result.stdout, result.stderr) + return result.stdout.decode("utf-8") + + +def rows(path: Path) -> list[list[str]]: + if not path.exists(): + return [] + return [row.split("\t") for row in path.read_text().splitlines()] + + +def wait_for_event( + process: subprocess.Popen[bytes], path: Path, prefix: str, count: int +) -> None: + deadline = time.monotonic() + 30 + while time.monotonic() < deadline: + if sum(row[0] == prefix and len(row) >= 2 for row in rows(path)) >= count: + return + assert process.poll() is None, process.communicate() + time.sleep(0.01) + pytest.fail(f"Timed out waiting for {prefix} in {path}") + + +def mutate_during( + repo: Path, args: list[str], events: Path, prefix: str, mutate: Callable[[], None] +) -> str: + count = sum(row[0] == prefix for row in rows(events)) + 1 + with subprocess.Popen( + command(repo, *args), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) as process: + try: + wait_for_event(process, events, prefix, count) + mutate() + out, err = process.communicate(timeout=30) + assert process.returncode == 0, (out, err) + return out.decode("utf-8") + finally: + if process.poll() is None: + process.kill() + process.communicate() + + +@pytest.mark.parametrize( + "verdict,expected", + [ + ("blocked", ["blocked"]), + ("needs-decision-then-ready", ["needs-decision", "ready"]), + ], +) +def test_installed_readiness(repo: Path, verdict: str, expected: list[str]) -> None: + (repo / ".readiness-verdict").write_text(verdict + "\n") + install.install("readiness", repo, FIXTURES, "codex") + for outcome in expected: + out = invoke(repo, "readiness", str(repo)) + assert f"**Verdict:** `{outcome}`" in out + if outcome == "blocked": + assert "**Type:** `dependency`" in out + assert "**Type:** `unblock`" in out + assert rows(repo / ".git/fixture-state/implementation-readiness-invocations") == [ + [verdict] for _ in expected + ] + + +def test_claude_readiness_prerender_is_passive(repo: Path) -> None: + (repo / ".readiness-verdict").write_text("needs-discovery\n") + install.install("readiness", repo, FIXTURES, "claude") + state = repo / ".git/fixture-state" + assert not (state / "implementation-readiness-invocations").exists() + result = (state / "implementation-readiness-result").read_text() + for field in ( + "**Verdict:** `needs-discovery`", + "**Type:** `missing-information`", + "**Type:** `discovery`", + ): + assert field in result + skill = repo / ".claude/skills/assess-implementation-readiness/SKILL.md" + assert "Use the native Read tool, never Bash" in skill.read_text() + + +def test_installed_review_detects_mutation_outside_subject(repo: Path) -> None: + install.install("review", repo, FIXTURES, "codex") + state = repo / ".git/fixture-state" + (repo / ".git/fixture-review-event-path").write_text("value.txt\n") + target = invoke(repo, "review", str(repo), "fingerprint").strip() + result = invoke(repo, "review", str(repo), "comprehensive", CONTRACT) + assert f"Reviewed target: {target}\n" in result + assert rows(state / "independent-review-invocations") == [ + [target, "clear", "comprehensive"] + ] + assert rows(state / "independent-review-events") == [["review_start", target]] + subject = git_text(repo, "hash-object", "--", "value.txt") + assert rows(state / "review-events") == [ + ["review_start", target, subject], + ["review_end", target, subject], + ["review", "clear", subject], + ] + (repo / ".git/fixture-review-delay-seconds").write_text("2\n") + + def mutate() -> None: + (repo / "AGENTS.md").write_text("Changed while review was running\n") + + out = mutate_during( + repo, + ["review", str(repo), "verify", CONTRACT], + state / "review-events", + "review_start", + mutate, + ) + assert "Fix verification: inconclusive." in out + start, end, observation = rows(state / "review-events")[-3:] + assert start[1] != end[1] + assert start[2] == end[2] == subject + assert observation == ["review", "inconclusive", subject] + + +def write_candidate(repo: Path, retries: int, cache: int) -> Path: + path = repo / "src/config.js" + path.parent.mkdir(exist_ok=True) + path.write_bytes( + f"export const TIMEOUT_MS = 2500;\nexport const RETRY_COUNT = {retries};\nexport const CACHE_SIZE = {cache};\n".encode() + ) + return path + + +def record_check(repo: Path, subject: Path) -> None: + (repo / ".git/fixture-state/check-target").write_text( + checksum(subject.read_bytes()) + "\n" + ) + + +def test_installed_combined_lifecycle_and_mutation(repo: Path) -> None: + install.install("verification", repo, FIXTURES, "both") + subject = write_candidate(repo, 0, 0) + state = repo / ".git/fixture-state" + history = state / "verification-invocations" + (repo / ".git/fixture-review-reject-pattern").write_text("RETRY_COUNT = 0\n") + assert "current checks" in invoke( + repo, "verification", str(repo), "initial", status=1 + ) + assert not history.exists() + record_check(repo, subject) + out = invoke(repo, "verification", str(repo), "initial") + assert "Conclusion: progress\n" in out + assert "Q1, QA/cache, blocking" in out + assert "Independent review outcome: blocking findings" in out + assert "Conclusion: no-progress\n" in invoke( + repo, "verification", str(repo), "follow-up" + ) + for flag in ("blocked", "inconclusive"): + marker = repo / f".git/fixture-review-{flag}" + marker.touch() + assert "Conclusion: blocked\n" in invoke( + repo, "verification", str(repo), "follow-up" + ) + marker.unlink() + before = history.read_bytes() + write_candidate(repo, 3, 5) + assert "current checks" in invoke( + repo, "verification", str(repo), "follow-up", status=1 + ) + assert history.read_bytes() == before + record_check(repo, subject) + assert "Conclusion: clear\n" in invoke(repo, "verification", str(repo), "follow-up") + assert rows(history)[-1][1] == checksum(subject.read_bytes()) + assert "already recorded" in invoke( + repo, "verification", str(repo), "initial", status=1 + ) + + def mutate() -> None: + write_candidate(repo, 3, 0) + + out = mutate_during( + repo, + ["verification", str(repo), "follow-up"], + state / "verification-events", + "review-returned", + mutate, + ) + assert "Conclusion: blocked\n" in out + events = rows(state / "verification-events")[-4:] + assert [row[0] for row in events] == [ + "assessment-start", + "review-returned", + "qa-returned", + "assessment-returned", + ] + assert events[0][2] == events[1][2] != events[2][2] diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/tests/test_launch_contract.py b/plugins/orchestration/darrow-adaptive-delivery/backend/tests/test_launch_contract.py new file mode 100644 index 00000000..1cc8bdf7 --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/tests/test_launch_contract.py @@ -0,0 +1,50 @@ +"""Static host-control guards; live evals establish actual owner behavior.""" + +import json + +import pytest + +from darrow_adaptive_delivery.common import PLUGIN + + +@pytest.mark.parametrize("host", ["codex", "claude"]) +def test_launch_contract(host: str) -> None: + skill = PLUGIN / "skills/adaptive-delivery" + text = (skill / "SKILL.md").read_text(encoding="utf-8") + guide = (skill / f"references/{host}-launch.md").read_text(encoding="utf-8") + assert "launch exactly one route-selected subagent owner" in text + assert "- phase: adaptive-delivery-owner" in text + assert "A direct shell, Git, forge," in text + required = { + "codex": [ + "`fork_turns` set to `none`", + "`model` set to the selected concrete Codex model", + "`reasoning_effort` set to the selected effort", + "`followup_task`", + "`send_message`", + ], + "claude": ["Invoke the resolved Agent exactly once", "use Claude"], + } + for control in required[host]: + assert control in guide + manifest = json.loads((PLUGIN / f".{host}-plugin/plugin.json").read_text()) + assert manifest["name"] == "darrow-adaptive-delivery" + + +def test_removed_lifecycle_protocol_stays_absent() -> None: + skill = PLUGIN / "skills/adaptive-delivery" + paths = [skill / "SKILL.md", *skill.glob("references/*-launch.md")] + paths.extend(PLUGIN.glob("agents/adaptive-delivery-*.md")) + removed = ( + "adaptive-delivery-preflight step", + "Protocol ledger", + "materialize-objective", + "darrow-native-goal-report", + "claude-owner-route", + "claude-route-gate", + "- phase: human-feedback-request", + "- phase: human-feedback-response", + ) + for path in paths: + text = path.read_text(encoding="utf-8") + assert not any(token in text for token in removed), path diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/tests/test_preflight.py b/plugins/orchestration/darrow-adaptive-delivery/backend/tests/test_preflight.py new file mode 100644 index 00000000..be12882c --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/tests/test_preflight.py @@ -0,0 +1,476 @@ +from __future__ import annotations + +import json +import os +import shutil +import subprocess +import sys +from pathlib import Path + +import pytest + +from darrow_adaptive_delivery import claude, cli, common, preflight +from darrow_adaptive_delivery.common import PLUGIN, RefusalError, git, git_text +from darrow_adaptive_delivery.routes import FIELDS, parse_document + + +def invoke( + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str], *args: str +) -> tuple[int, str, str]: + monkeypatch.setattr(sys, "argv", ["adaptive-delivery-preflight", *args]) + status = cli.preflight() + output = capsys.readouterr() + return status, output.out, output.err + + +def policy(repo: Path, **changes: object) -> Path: + row: dict[str, object] = dict( + zip( + FIELDS, + ( + "codex", + "routine", + "codex", + "openai", + "gpt-5.6-sol", + "high", + "none", + "none", + ), + strict=True, + ) + ) + row.update(changes) + path = repo / ".darrow/config.json" + path.parent.mkdir(exist_ok=True) + path.write_text(json.dumps({"routes": [row]})) + return path + + +def test_console_records_are_utf8_under_legacy_code_page(repo: Path) -> None: + unicode_repo = repo.with_name("repository-é") + repo.rename(unicode_repo) + environment = {**os.environ, "PYTHONIOENCODING": "cp1252"} + command = [ + sys.executable, + "-c", + "from darrow_adaptive_delivery.cli import preflight; raise SystemExit(preflight())", + ] + for target, status in ((unicode_repo, 0), (unicode_repo / "missing-é", 2)): + result = subprocess.run( + [*command, "prepare", "--repo", str(target), "--host", "codex"], + capture_output=True, + env=environment, + check=False, + ) + assert result.returncode == status + output = result.stdout if status == 0 else result.stderr + assert str(target) in output.decode("utf-8") + assert b"\r\n" not in output + + +def test_prepared_records_and_unchanged_repository(repo: Path) -> None: + before = git(repo, "status", "--porcelain=v1") + output = preflight.run(["prepare", "--repo", str(repo), "--host", "codex"]) + assert output.startswith( + f"format\tdarrow-native-goal-prepared-v2\nrepo\t{repo}\nbase_revision\t{git_text(repo, 'rev-parse', 'HEAD')}\nworking_tree\tclean\n" + ) + assert ( + "route\troutine\tcodex\topenai\tgpt-5.6-terra\tmedium\nroute_policy_source\troutine\tbundled\n" + in output + ) + assert output.endswith( + f"workflow\tmechanical\t{PLUGIN / 'skills/adaptive-delivery/references/workflows/mechanical.md'}\n" + ) + assert git(repo, "status", "--porcelain=v1") == before + (repo / "AGENTS.md").write_text("Instructions\n") + (repo / "CLAUDE.md").write_text("Claude\n") + output = preflight.run(["prepare", "--repo", str(repo), "--host", "claude"]) + assert ( + f"working_tree\tdirty\ninstruction\t{repo / 'AGENTS.md'}\ninstruction\t{repo / 'CLAUDE.md'}\n" + in output + ) + assert "route\troutine\tclaude\tanthropic\tclaude-sonnet-5\tlow\n" in output + + +def test_policy_provenance_and_explicit_precedence(repo: Path) -> None: + args = ["route", "--repo", str(repo), "--host", "codex", "--profile", "routine"] + output = preflight.run(args) + assert ( + output + == "format\tdarrow-native-goal-route-v2\nprofile\troutine\nselected_route\tcodex\topenai\tgpt-5.6-terra\tmedium\nroute_source\tpolicy\npolicy_route_source\tbundled\n" + ) + path = policy(repo) + assert "policy_route_source\trepository\n" in preflight.run(args) + assert "selected_route\tcodex\topenai\tgpt-5.6-sol\thigh\n" in preflight.run(args) + path.write_text("not json") + assert ( + preflight.run([*args, "--route", "codex|openai|future-model.1|ultra"]) + == "format\tdarrow-native-goal-route-v2\nprofile\troutine\nselected_route\tcodex\topenai\tfuture-model.1\tultra\nroute_source\tuser\n" + ) + + +@pytest.mark.parametrize( + "args,error", + [ + ([], "usage:"), + (["step"], "unknown command"), + (["prepare"], "requires"), + (["route"], "requires"), + (["prepare", "--repo"], "missing value"), + (["prepare", "--repo", ""], "missing value"), + (["prepare", "--bad"], "unknown prepare option"), + (["prepare", "--repo", ".", "--host", "other"], "unsupported host"), + ], +) +def test_cli_refusals( + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], + args: list[str], + error: str, +) -> None: + status, out, err = invoke(monkeypatch, capsys, *args) + assert (status, out) == (2, "") + assert error in err + + +@pytest.mark.parametrize("option", ["-h", "--help", "help"]) +def test_help( + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str], option: str +) -> None: + assert invoke(monkeypatch, capsys, option) == (0, preflight.USAGE, "") + + +@pytest.mark.parametrize( + "route,error", + [ + ("a|b", "must be harness"), + ("codex|openai||low", "must be harness"), + ("codex|openai|safe|low|extra", "must be harness"), + ("claude|anthropic|model|low", "current host"), + ("codex|anthropic|safe|low", "provider"), + ("codex|openai|none|low", "concrete safe model"), + ("codex|openai|.unsafe|low", "concrete safe model"), + ("codex|openai|model|impossible", "unsupported effort"), + ("codex|openai|gpt-5.6-luna|medium", "not eligible"), + ], +) +def test_explicit_route_refusals(repo: Path, route: str, error: str) -> None: + with pytest.raises(RefusalError, match=error): + preflight.run( + [ + "route", + "--repo", + str(repo), + "--host", + "codex", + "--profile", + "routine", + "--route", + route, + ] + ) + + +@pytest.mark.parametrize( + "profile,error", [("invalid/name", "unsupported profile"), ("unknown", "no route")] +) +def test_profile_refusal(repo: Path, profile: str, error: str) -> None: + with pytest.raises(RefusalError, match=error): + preflight.run( + ["route", "--repo", str(repo), "--host", "codex", "--profile", profile] + ) + + +@pytest.mark.parametrize( + "changes,error", + [ + ({"profile": "unknown"}, "not in bundled"), + ({"model": "gpt-5.6-luna"}, "not eligible"), + ({"provider": "anthropic"}, "provider does not match"), + ], +) +def test_policy_refusal_before_evidence( + repo: Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], + changes: dict[str, object], + error: str, +) -> None: + policy(repo, **changes) + status, out, err = invoke( + monkeypatch, capsys, "prepare", "--repo", str(repo), "--host", "codex" + ) + assert (status, out) == (2, "") + assert error in err + + +def test_configuration_file_boundaries(repo: Path, tmp_path: Path) -> None: + path = policy(repo) + path.write_text("{bad") + with pytest.raises(RefusalError, match="repository route configuration is invalid"): + preflight.prepare(repo, "codex", PLUGIN) + path.unlink() + path.mkdir() + with pytest.raises(RefusalError, match="unsafe"): + preflight.prepare(repo, "codex", PLUGIN) + path.rmdir() + # Native Windows CI may not grant symlink creation; the symlink predicate is + # also covered with a deterministic boundary test below. + if os.name != "nt": + path.symlink_to(tmp_path / "missing") + with pytest.raises(RefusalError, match="unsafe"): + preflight.prepare(repo, "codex", PLUGIN) + + +def test_symlink_policy_refusal(repo: Path, monkeypatch: pytest.MonkeyPatch) -> None: + policy(repo) + monkeypatch.setattr(Path, "is_symlink", lambda _path: True) + with pytest.raises(RefusalError, match="unsafe"): + preflight.prepare(repo, "codex", PLUGIN) + + +def test_git_binding_with_linked_worktree( + repo: Path, tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + linked = tmp_path / "linked repository" + git(repo, "worktree", "add", "-qb", "linked", str(linked)) + nested = linked / "nested" + nested.mkdir() + policy(linked) + for key in common.GIT_SELECTORS: + monkeypatch.setenv(key, str(tmp_path / "foreign")) + monkeypatch.setenv("GIT_CONFIG_KEY_0", "core.bare") + monkeypatch.setenv("GIT_CONFIG_VALUE_0", "true") + assert common.repository(str(nested)) == linked.resolve() + assert f"repo\t{linked.resolve()}\n" in preflight.run( + ["prepare", "--repo", str(nested), "--host", "codex"] + ) + + +def test_repository_refusals(repo: Path, tmp_path: Path) -> None: + with pytest.raises(RefusalError, match="directory does not exist"): + common.repository(str(tmp_path / "missing")) + for path in (tmp_path, repo / ".git"): + with pytest.raises(RefusalError, match="not a git working tree"): + common.repository(str(path)) + + +def test_failed_observations(repo: Path, monkeypatch: pytest.MonkeyPatch) -> None: + def fail(*args: object, **kwargs: object) -> bytes: + raise OSError("Git unavailable") + + monkeypatch.setattr(common, "git", fail) + with pytest.raises(RefusalError, match="not a git working tree"): + common.repository(str(repo)) + with pytest.raises(RefusalError, match="cannot read repository revision"): + preflight.prepare(repo, "codex", PLUGIN) + + +def test_missing_resources(repo: Path, tmp_path: Path) -> None: + plugin = tmp_path / "plugin" + shutil.copytree(PLUGIN / "config", plugin / "config") + with pytest.raises(RefusalError, match="workflow directory is missing"): + preflight.prepare(repo, "codex", plugin) + workflows = plugin / "skills/adaptive-delivery/references/workflows" + workflows.mkdir(parents=True) + with pytest.raises(RefusalError, match="workflow document is unreadable"): + preflight.prepare(repo, "codex", plugin) + (plugin / "config/routes.json").write_text( + '{"routes":' + + json.dumps( + [ + dict( + zip( + FIELDS, + ( + "claude", + "routine", + "claude", + "anthropic", + "claude-sonnet-5", + "low", + "none", + "none", + ), + strict=True, + ) + ) + ] + ) + + "}" + ) + with pytest.raises(RefusalError, match="no routes for host"): + preflight.prepare(repo, "codex", plugin) + + +@pytest.mark.parametrize( + "model,effort", + [ + ("claude-sonnet-5", "low"), + ("claude-sonnet-5", "medium"), + ("claude-opus-5", "high"), + ], +) +def test_claude_route( + model: str, + effort: str, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +) -> None: + monkeypatch.setenv("CLAUDE_CODE_SUBAGENT_MODEL", model) + monkeypatch.setenv("CLAUDE_CODE_EFFORT_LEVEL", effort) + monkeypatch.setattr( + sys, + "argv", + [ + "claude-agent-route", + "--provider", + "anthropic", + "--model", + model, + "--effort", + effort, + ], + ) + assert cli.claude_route() == 0 + output = capsys.readouterr().out + name = f"adaptive-delivery-{model.removeprefix('claude-')}-{effort}" + assert ( + output + == f"format\tdarrow-claude-agent-route-v1\nselected_route\tclaude\tanthropic\t{model}\t{effort}\nsubagent_type\tdarrow-adaptive-delivery:{name}\nagent_file\t{PLUGIN / 'agents' / (name + '.md')}\n" + ) + + +@pytest.mark.parametrize( + "key", + [ + *claude.SELECTORS, + "ANTHROPIC_BASE_URL", + "CLAUDE_CODE_SUBAGENT_MODEL", + "CLAUDE_CODE_EFFORT_LEVEL", + ], +) +def test_claude_overrides(key: str, monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setenv(key, "other") + with pytest.raises(RefusalError): + claude.run( + ["--provider", "anthropic", "--model", "claude-sonnet-5", "--effort", "low"] + ) + + +@pytest.mark.parametrize( + "args,error", + [ + ([], "required"), + (["--other"], "unknown option"), + (["--model"], "missing value"), + ( + ["--provider", "other", "--model", "claude-sonnet-5", "--effort", "low"], + "unsupported provider", + ), + ], +) +def test_claude_arguments(args: list[str], error: str) -> None: + with pytest.raises(RefusalError, match=error): + claude.run(args) + assert claude.run(["--help"]) == claude.USAGE + + +@pytest.mark.parametrize( + "model,effort,error", + [ + ("sonnet", "low", "concrete Claude"), + ("claude-", "low", "unsafe model"), + ("claude-../bad", "low", "unsafe model"), + ("claude-sonnet-5", "ultra", "unsupported effort"), + ("claude-opus-5", "low", "no bundled runner"), + ], +) +def test_claude_unsupported_routes(model: str, effort: str, error: str) -> None: + with pytest.raises(RefusalError, match=error): + claude.resolve(model, effort, PLUGIN, {}) + + +@pytest.mark.parametrize("field", ["name", "model", "effort"]) +def test_claude_runner_mismatch(tmp_path: Path, field: str) -> None: + path = tmp_path / "agents/adaptive-delivery-sonnet-5-low.md" + path.parent.mkdir() + original = (PLUGIN / "agents" / path.name).read_text() + path.write_text( + "\n".join( + line for line in original.splitlines() if not line.startswith(field + ":") + ) + ) + with pytest.raises(RefusalError, match=f"runner {field} does not match"): + claude.resolve("claude-sonnet-5", "low", tmp_path, {}) + + +def test_console_executable(repo: Path) -> None: + result = subprocess.run( + [ + str( + Path(sys.executable).parent + / ( + "adaptive-delivery-preflight.exe" + if os.name == "nt" + else "adaptive-delivery-preflight" + ) + ), + "prepare", + "--repo", + str(repo), + "--host", + "codex", + ], + capture_output=True, + text=True, + check=False, + ) + assert result.returncode == 0, result.stderr + assert "darrow-native-goal-prepared-v2" in result.stdout + + +@pytest.mark.parametrize( + "document", + [ + "", + "{", + "[]", + '{"extra":1}', + '{"routes":[],"routes":[]}', + '{"routes":{}}', + '{"routes":[{}]}', + '{"routes":[{"extra":"x"}]}', + '{"routes":[{"host":"codex","host":"codex"}]}', + '{"reviewers":NaN}', + '{"reviewers":Infinity}', + '{"reviewers":01}', + '{"reviewers":true} trailing', + ], +) +def test_invalid_documents(document: str) -> None: + with pytest.raises(RefusalError): + parse_document(document) + + +def test_route_document_shapes(repo: Path) -> None: + assert ( + parse_document( + '{"reviewers":{"ignored":[true,false,null,1.2e3,"\\u263a"],"ignored":2}}', + True, + ) + == [] + ) + assert parse_document('{"routes":[]}', True) == [] + for document in ("{}", '{"routes":[]}'): + with pytest.raises(RefusalError): + parse_document(document) + path = policy(repo) + row = json.loads(path.read_text())["routes"][0] + with pytest.raises(RefusalError, match="duplicate route"): + parse_document(json.dumps({"routes": [row, row]})) + for value in (None, 3, "", "unsafe\tvalue"): + row["model"] = value + with pytest.raises(RefusalError, match="unsafe or empty model"): + parse_document(json.dumps({"routes": [row]})) diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/tests/test_proof.py b/plugins/orchestration/darrow-adaptive-delivery/backend/tests/test_proof.py new file mode 100644 index 00000000..aa552bb4 --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/tests/test_proof.py @@ -0,0 +1,230 @@ +from __future__ import annotations + +import subprocess +from pathlib import Path + +import pytest + +from darrow_adaptive_delivery.fixtures import proof + + +def write(path: Path, text: str = "provider fixture\n") -> Path: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(text, encoding="utf-8", newline="\n") + return path + + +def provider(repo: Path, name: str = "review-plugin") -> Path: + backend = repo / ".git" / name / "backend" + for filename in ("result.py", "scope.py", "report.py"): + write(backend / "src/darrow_review" / filename) + return backend + + +@pytest.fixture +def calls(monkeypatch: pytest.MonkeyPatch) -> list[tuple[str, ...]]: + result: list[tuple[str, ...]] = [] + + def invoke(_backend: Path, _repo: Path, *args: str) -> str: + result.append(args) + if args[0] == "review-report": + return "Canonical human report\n" + return "target\tWORKTREE@current\n" + + monkeypatch.setattr(proof, "invoke", invoke) + return result + + +def comprehensive(repo: Path) -> Path: + return write( + repo / ".git/darrow-review.original/result.tsv", + "format\tdarrow-review-result-v1\nverdict\tpass\nnext_action\treturn control to enclosing goal\ntarget\tWORKTREE@current\n", + ) + + +def verification(repo: Path) -> Path: + return write( + repo / ".git/darrow-review.repair/verification.tsv", + "format\tdarrow-review-verification-v1\noutcome\tclear\noriginal_target\tWORKTREE@current\ncurrent_target\tWORKTREE@current\n", + ) + + +def test_clear_proof_delegates_and_binds_current_candidate( + repo: Path, calls: list[tuple[str, ...]] +) -> None: + provider(repo) + record = comprehensive(repo) + assert ( + proof.validate(repo, "complete", str(record)) + == f"valid clear review: {record}\n" + ) + assert (repo / ".git/goal-complete").read_text() == "complete\n" + assert calls[0] == ("review-result", "validate", str(record)) + assert calls[1] == ( + "review-scope", + "prepare", + "--repo", + str(repo), + "--base", + "HEAD", + "--target", + "WORKTREE", + ) + assert proof.validate(repo, "artifact").startswith("valid clear review") + assert proof.validate(repo, "current").startswith("valid clear review") + repaired = verification(repo) + assert proof.validate(repo, "complete", str(repaired)).startswith( + "valid clear review" + ) + assert ("review-result", "validate-original", str(record), str(repaired)) in calls + + +@pytest.mark.parametrize("followup", [False, True]) +def test_human_reports_are_canonical( + repo: Path, calls: list[tuple[str, ...]], followup: bool +) -> None: + backend = provider(repo) + original = comprehensive(repo) + record = verification(repo) if followup else original + report = write( + record.with_name("verification.md" if followup else "review.md"), + "Canonical human report\n", + ) + assert proof.validate(repo, "complete", str(report)).endswith(str(record) + "\n") + report.write_text("Forged human report\n") + with pytest.raises(proof.InvalidProofError, match="differs"): + proof.validate(repo, "complete", str(report)) + (backend / "src/darrow_review/report.py").unlink() + with pytest.raises(proof.InvalidProofError, match="renderer is missing"): + proof.validate(repo, "complete", str(report)) + + +def test_provider_discovery_and_conflicts(repo: Path) -> None: + with pytest.raises(proof.InvalidProofError, match="tool is missing"): + proof.provider(repo / ".git") + first = provider(repo, "a") + second = provider(repo, "b") + assert proof.provider(repo / ".git") == first + write(second / "src/darrow_review/scope.py", "different\n") + with pytest.raises(proof.InvalidProofError, match="conflicting"): + proof.provider(repo / ".git") + + +@pytest.mark.parametrize( + "relative,error", + [ + ("missing", "unreadable"), + ("outside.tsv", "not a canonical"), + (".git/darrow-review.test/other.tsv", "not a canonical"), + ], +) +def test_proof_path_refusals(repo: Path, relative: str, error: str) -> None: + path = repo / relative + if relative != "missing": + write(path) + with pytest.raises(proof.InvalidProofError, match=error): + proof.validate(repo, "complete", str(path)) + assert not (repo / ".git/goal-complete").exists() + + +def test_bad_modes_and_missing_saved_evidence(repo: Path) -> None: + with pytest.raises(proof.InvalidProofError, match="expected complete"): + proof.validate(repo, "bad") + with pytest.raises(proof.InvalidProofError): + proof.validate(repo, "current") + + +@pytest.mark.parametrize( + "replacement,error", + [ + (("verdict\tpass", "verdict\tfail"), "not clear"), + ( + ("next_action\treturn control to enclosing goal", "next_action\tcontinue"), + "did not return", + ), + (("target\tWORKTREE@current", "target\tWORKTREE@old"), "stale review"), + (("format\tdarrow-review-result-v1", "format\tunknown"), "unsupported format"), + ], +) +def test_comprehensive_refusals( + repo: Path, calls: list[tuple[str, ...]], replacement: tuple[str, str], error: str +) -> None: + provider(repo) + path = comprehensive(repo) + path.write_text(path.read_text().replace(*replacement)) + with pytest.raises(proof.InvalidProofError, match=error): + proof.validate(repo, "complete", str(path)) + assert not (repo / ".git/goal-complete").exists() + + +def test_artifact_names(repo: Path, calls: list[tuple[str, ...]]) -> None: + backend = provider(repo) + original = comprehensive(repo) + wrong = original.with_name("verification.tsv") + original.rename(wrong) + with pytest.raises(proof.InvalidProofError, match="wrong comprehensive"): + proof.validate(repo, "complete", str(wrong)) + repaired = verification(repo) + wrong = repaired.with_name("result.tsv") + repaired.rename(wrong) + with pytest.raises(proof.InvalidProofError, match="wrong verification"): + proof.validate(repo, "complete", str(wrong)) + (backend / "src/darrow_review/scope.py").unlink() + with pytest.raises(proof.InvalidProofError, match="scope tool is missing"): + proof.current(backend, repo, "WORKTREE@current") + + +def test_verification_requires_clear_and_original( + repo: Path, calls: list[tuple[str, ...]] +) -> None: + provider(repo) + record = verification(repo) + with pytest.raises(proof.InvalidProofError, match=r"original.*missing"): + proof.validate(repo, "complete", str(record)) + original = comprehensive(repo) + write(repo / ".git/darrow-review.duplicate/result.tsv", original.read_text()) + with pytest.raises(proof.InvalidProofError, match=r"original.*ambiguous"): + proof.validate(repo, "complete", str(record)) + record.write_text(record.read_text().replace("outcome\tclear", "outcome\tcontinue")) + with pytest.raises(proof.InvalidProofError, match="verification is not clear"): + proof.validate(repo, "complete", str(record)) + + +@pytest.mark.parametrize( + "status,stdout,stderr", + [(0, "valid\n", ""), (2, "", "invalid\n"), (2, "invalid\n", "")], +) +def test_provider_subprocess_arguments( + repo: Path, monkeypatch: pytest.MonkeyPatch, status: int, stdout: str, stderr: str +) -> None: + backend = provider(repo) + + def run(args: list[str], **kwargs: object) -> subprocess.CompletedProcess[str]: + assert args == [ + "uv", + "run", + "--quiet", + "--frozen", + "--no-dev", + "--project", + str(backend), + "review-result", + "validate", + "artifact with spaces", + ] + assert kwargs["cwd"] == repo + return subprocess.CompletedProcess(args, status, stdout, stderr) + + monkeypatch.setattr(subprocess, "run", run) + if status: + with pytest.raises(proof.InvalidProofError, match="invalid"): + proof.invoke( + backend, repo, "review-result", "validate", "artifact with spaces" + ) + else: + assert ( + proof.invoke( + backend, repo, "review-result", "validate", "artifact with spaces" + ) + == "valid\n" + ) diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/tests/test_properties.py b/plugins/orchestration/darrow-adaptive-delivery/backend/tests/test_properties.py new file mode 100644 index 00000000..c3b335df --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/tests/test_properties.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +import json + +from hypothesis import given, settings +from hypothesis import strategies as st + +from darrow_adaptive_delivery.fixtures.state import checksum +from darrow_adaptive_delivery.routes import FIELDS, parse_document + + +@settings(max_examples=50, derandomize=True) +@given(st.from_regex(r"[A-Za-z0-9][A-Za-z0-9._-]{0,30}", fullmatch=True)) +def test_route_json_round_trip(model: str) -> None: + values = ("codex", "routine", "codex", "openai", model, "high", "none", "none") + row = dict(zip(FIELDS, values, strict=True)) + forward = json.dumps({"routes": [row]}, ensure_ascii=True) + reverse = json.dumps( + { + "reviewers": [False, {"future": 1}], + "routes": [dict(reversed(list(row.items())))], + } + ) + assert parse_document(forward) == parse_document(reverse) + assert parse_document(forward)[0].tuple == ("codex", "openai", model, "high") + + +@settings(max_examples=50, derandomize=True) +@given(st.binary(max_size=512)) +def test_checksum_matches_posix_polynomial(data: bytes) -> None: + # Independent bit-string polynomial division oracle, including encoded length. + suffix = len(data).to_bytes((len(data).bit_length() + 7) // 8, "little") + message = int.from_bytes(data + suffix, "big") << 32 + polynomial = (1 << 32) | 0x04C11DB7 + while message.bit_length() >= polynomial.bit_length(): + message ^= polynomial << (message.bit_length() - polynomial.bit_length()) + assert checksum(data) == f"{message ^ 0xFFFFFFFF} {len(data)}" diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/tests/test_route_matrix.py b/plugins/orchestration/darrow-adaptive-delivery/backend/tests/test_route_matrix.py new file mode 100644 index 00000000..dd1f4f09 --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/tests/test_route_matrix.py @@ -0,0 +1,216 @@ +"""Policy/explicit parity and repository isolation from the retired shell suite.""" + +from __future__ import annotations + +import json +import sys +from pathlib import Path + +import pytest + +from darrow_adaptive_delivery import cli, common, preflight +from darrow_adaptive_delivery.common import git, git_text +from darrow_adaptive_delivery.routes import FIELDS + + +def write_policy(repo: Path, host: str, route: str, profile: str = "routine") -> None: + path = repo / ".darrow/config.json" + path.parent.mkdir(exist_ok=True) + values = [host, profile, *route.split("|"), "none", "none"] + path.write_text(json.dumps({"routes": [dict(zip(FIELDS, values, strict=True))]})) + + +def commands(repo: Path, host: str, route: str) -> list[list[str]]: + shared = ["--repo", str(repo), "--host", host] + selected = ["route", *shared, "--profile", "routine"] + return [["prepare", *shared], selected, [*selected, "--route", route]] + + +@pytest.mark.parametrize( + "host,route", + [ + ("codex", "codex|anthropic|gpt-5.6-terra|medium"), + ("claude", "claude|openai|claude-sonnet-5|low"), + ("codex", "claude|anthropic|claude-sonnet-5|low"), + ("claude", "codex|openai|gpt-5.6-terra|medium"), + ("codex", "codex|openai|none|medium"), + ("claude", "claude|anthropic|none|low"), + ("codex", "codex|openai|gpt-5.6-terra|impossible"), + ("claude", "claude|anthropic|claude-sonnet-5|none"), + ("codex", "codex|openai|unsafe model|medium"), + ("codex", "codex|openai|.unsafe|medium"), + ("codex", "codex|openai||medium"), + ("codex", "codex|openai|gpt-5.6-luna|medium"), + ], +) +def test_policy_and_explicit_refuse_without_evidence( + repo: Path, + host: str, + route: str, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +) -> None: + write_policy(repo, host, route) + for args in commands(repo, host, route): + monkeypatch.setattr(sys, "argv", ["adaptive-delivery-preflight", *args]) + assert cli.preflight() == 2 + captured = capsys.readouterr() + assert captured.out == "" + assert captured.err.startswith("adaptive-delivery-preflight:") + + +@pytest.mark.parametrize( + "host,provider", [("codex", "openai"), ("claude", "anthropic")] +) +@pytest.mark.parametrize("effort", ["low", "medium", "high", "xhigh", "max", "ultra"]) +def test_off_catalog_policy_and_explicit_routes( + repo: Path, host: str, provider: str, effort: str +) -> None: + route = f"{host}|{provider}|future-model.1|{effort}" + write_policy(repo, host, route) + prepared, policy, explicit = [ + preflight.run(args) for args in commands(repo, host, route) + ] + selected = route.replace("|", "\t") + "\n" + assert "route\troutine\t" + selected in prepared + assert "selected_route\t" + selected in policy + assert "policy_route_source\trepository\n" in policy + assert "selected_route\t" + selected in explicit + assert "route_source\tuser\n" in explicit + assert "policy_route_source" not in explicit + + +def test_override_preserves_other_bundled_profiles(repo: Path) -> None: + write_policy(repo, "codex", "codex|openai|gpt-5.6-sol|high", "scaled") + out = preflight.run(["prepare", "--repo", str(repo), "--host", "codex"]) + for profile, model, effort in ( + ("routine", "gpt-5.6-terra", "medium"), + ("routine-plus", "gpt-5.6-terra", "high"), + ("scaled", "gpt-5.6-sol", "high"), + ("repo-wide", "gpt-5.6-sol", "high"), + ("judgment", "gpt-6-astra", "high"), + ): + assert f"route\t{profile}\tcodex\topenai\t{model}\t{effort}\n" in out + assert "route_policy_source\tscaled\trepository\n" in out + assert "route_policy_source\troutine\tbundled\n" in out + assert "preexisting_change" not in out + + +def selectors(foreign: Path, requested: Path) -> list[dict[str, str]]: + metadata = foreign / ".git" + config = foreign / "invalid-config" + config.write_text("[invalid\n") + return [ + {"GIT_DIR": str(metadata), "GIT_WORK_TREE": str(foreign)}, + {"GIT_DIR": str(metadata)}, + {"GIT_WORK_TREE": str(foreign)}, + {"GIT_COMMON_DIR": str(metadata)}, + {"GIT_INDEX_FILE": str(metadata / "index")}, + { + "GIT_OBJECT_DIRECTORY": str(metadata / "objects"), + "GIT_ALTERNATE_OBJECT_DIRECTORIES": str(foreign / "missing"), + }, + {"GIT_CEILING_DIRECTORIES": str(requested)}, + { + "GIT_CONFIG_COUNT": "1", + "GIT_CONFIG_KEY_0": "core.worktree", + "GIT_CONFIG_VALUE_0": str(foreign), + }, + {"GIT_CONFIG_PARAMETERS": "'core.bare=true'"}, + {"GIT_CONFIG_GLOBAL": str(config)}, + {"GIT_CONFIG_SYSTEM": str(config)}, + ] + + +@pytest.mark.parametrize("linked", [False, True]) +def test_ambient_selectors_cannot_supply_foreign_evidence( + repo: Path, + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + linked: bool, +) -> None: + foreign = tmp_path / "foreign repo" + git(repo, "clone", "--quiet", str(repo), str(foreign)) + git( + foreign, + "-c", + "user.name=Other", + "-c", + "user.email=other@example.invalid", + "commit", + "--allow-empty", + "-qm", + "Other", + ) + requested = repo + if linked: + requested = tmp_path / "linked repo" + git(repo, "worktree", "add", "-qb", "linked", str(requested)) + requested = requested.resolve() + (requested / "nested").mkdir() + (requested / "AGENTS.md").write_text("Requested repository instructions\n") + write_policy(requested, "codex", "codex|openai|requested-model|medium") + write_policy(foreign, "codex", "codex|openai|foreign-model|high") + revision = git_text(requested, "rev-parse", "HEAD") + for environment in selectors(foreign, requested): + with monkeypatch.context() as context: + for key, value in environment.items(): + context.setenv(key, value) + prepared, selected, _ = [ + preflight.run(args) + for args in commands( + requested / "nested", "codex", "codex|openai|requested-model|medium" + ) + ] + assert ( + f"repo\t{requested}\nbase_revision\t{revision}\nworking_tree\tdirty\n" + in prepared + ) + assert f"instruction\t{requested / 'AGENTS.md'}\n" in prepared + assert "route\troutine\tcodex\topenai\trequested-model\tmedium\n" in prepared + assert "selected_route\tcodex\topenai\trequested-model\tmedium\n" in selected + + +@pytest.mark.parametrize("failure", ["outside", "status"]) +def test_failed_git_observation_emits_no_evidence( + repo: Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], + failure: str, +) -> None: + original = common.git + + def observe(path: Path, *args: str) -> bytes: + if failure == "outside" and "--is-inside-work-tree" in args: + return b"false\n" + if failure == "status" and "status" in args: + raise OSError("status failed") + return original(path, *args) + + monkeypatch.setattr(common, "git", observe) + monkeypatch.setattr( + sys, "argv", ["preflight", "prepare", "--repo", str(repo), "--host", "codex"] + ) + assert cli.preflight() == 2 + captured = capsys.readouterr() + assert captured.out == "" + assert ( + "not a git working tree" + if failure == "outside" + else "cannot read working tree state" + ) in captured.err + + +def test_bare_repository_refusal(repo: Path, tmp_path: Path) -> None: + bare = tmp_path / "bare.git" + git(repo, "init", "--bare", "--quiet", str(bare)) + for args in commands(bare, "codex", "codex|openai|safe|medium")[:2]: + with pytest.raises(common.RefusalError, match="not a git working tree"): + preflight.run(args) + + +@pytest.mark.parametrize("command", ["step", "materialize-objective", "launch"]) +def test_retired_commands_refused(command: str) -> None: + with pytest.raises(common.RefusalError, match="unknown command"): + preflight.run([command]) + assert command not in preflight.USAGE diff --git a/plugins/orchestration/darrow-adaptive-delivery/backend/uv.lock b/plugins/orchestration/darrow-adaptive-delivery/backend/uv.lock new file mode 100644 index 00000000..2aefa67e --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/backend/uv.lock @@ -0,0 +1,510 @@ +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-adaptive-delivery" +version = "0.22.3" +source = { editable = "." } + +[package.dev-dependencies] +dev = [ + { name = "coverage", extra = ["toml"] }, + { name = "editables" }, + { name = "hatchling" }, + { name = "hypothesis" }, + { name = "mypy" }, + { name = "pytest" }, + { name = "ruff" }, +] + +[package.metadata] + +[package.metadata.requires-dev] +dev = [ + { name = "coverage", extras = ["toml"], specifier = ">=7.10,<8" }, + { name = "editables", specifier = ">=0.3,<1" }, + { name = "hatchling", specifier = "==1.32.3" }, + { 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 = "editables" +version = "0.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/f8/02a4e9a0cb961b0feeb431ac96b231c157ecdcbbeacafe9e2fdb4b1dde39/editables-0.6.tar.gz", hash = "sha256:1163834902381c4613787951c5914800fdf155ae08848a373b8ea5006780977c", size = 16763, upload-time = "2026-04-14T10:39:04.677Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/0e/d095533037fc9468f947b19f2da53f869804632b508808de41e86e801797/editables-0.6-py3-none-any.whl", hash = "sha256:d70e4698078a1d033e7786d9c64e5be070d058a67c21417024d38a58ac20aa43", size = 5438, upload-time = "2026-04-14T10:39:03.663Z" }, +] + +[[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 = "hatchling" +version = "1.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "pathspec" }, + { name = "pluggy" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "tomlkit" }, + { name = "trove-classifiers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/62/7f/b20bd29978bec1f2ddd3bbc1cdc5829bb2dd5c1476facd8a84bfe9934c5e/hatchling-1.32.3.tar.gz", hash = "sha256:acdd4436d84d829c6e7ae6df723241d8f0bca9c5bad5a6148bd41f2ea6b8a792", size = 58154, upload-time = "2026-09-17T20:05:11.675Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/89/268c7e4b2bcf2071047366c4c3b4b0e89f640e13ea7bfdaf1df6c40e5140/hatchling-1.32.3-py3-none-any.whl", hash = "sha256:d05b350af5d9aa5bf7b34b434a45a742b99e67d0b48ea67d240cfeab98e13945", size = 78895, upload-time = "2026-09-17T20:05:10.38Z" }, +] + +[[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 = "tomlkit" +version = "0.15.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/96/e07752635b98536177fa1f37671c8f3cdde2e724c6bcf6034b2cfb571565/tomlkit-0.15.1.tar.gz", hash = "sha256:e25bbf38843005246210a12982776f27f99cb9be67160e14434d0c0d21ee1e97", size = 180129, upload-time = "2026-07-17T01:48:04.562Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/bc/8c13eb66537dce1d2bd3a57132902f38d0e7f5bb46fa9f4daed9fe9d76ee/tomlkit-0.15.1-py3-none-any.whl", hash = "sha256:177a05aece5a8ca5266fd3c448abb47b8d352f09d477d3ca8332db4d89b24304", size = 49449, upload-time = "2026-07-17T01:48:05.728Z" }, +] + +[[package]] +name = "trove-classifiers" +version = "2026.6.1.19" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/e3/7ca82ee24c82d344584abd5b8637b3bd056f2900226e8d82fc22f1184b92/trove_classifiers-2026.6.1.19.tar.gz", hash = "sha256:c5132b4b61a829d11cfbd2d72e97f20a45ed6edb95e45c5efdeb5e00836b2745", size = 17059, upload-time = "2026-06-01T19:41:34.649Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/a4/81502f486f01db95bc8320646a8a12511f5e556cb63d5e224d91816605c4/trove_classifiers-2026.6.1.19-py3-none-any.whl", hash = "sha256:ab4c4ec93cc4a4e7815fa759906e05e6bb3f2fbd92ea0f897288c6a43efd15b3", size = 14211, upload-time = "2026-06-01T19:41:33.434Z" }, +] + +[[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/orchestration/darrow-adaptive-delivery/bin/adaptive-delivery-preflight b/plugins/orchestration/darrow-adaptive-delivery/bin/adaptive-delivery-preflight deleted file mode 100755 index e3436bdd..00000000 --- a/plugins/orchestration/darrow-adaptive-delivery/bin/adaptive-delivery-preflight +++ /dev/null @@ -1,369 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# --repo owns discovery and every later Git observation. Inherited invocation -# selectors must not substitute another repository, index, or object store. -unset GIT_DIR GIT_WORK_TREE GIT_COMMON_DIR GIT_INDEX_FILE -unset GIT_OBJECT_DIRECTORY GIT_ALTERNATE_OBJECT_DIRECTORIES -unset GIT_CEILING_DIRECTORIES GIT_DISCOVERY_ACROSS_FILESYSTEM -unset GIT_CONFIG GIT_CONFIG_PARAMETERS GIT_CONFIG_COUNT -unset GIT_CONFIG_GLOBAL GIT_CONFIG_SYSTEM -unset GIT_SHALLOW_FILE GIT_GRAFT_FILE GIT_NAMESPACE - -script_dir=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) -routes_file="$script_dir/../config/routes.json" -routes_parser="$script_dir/routes-json.awk" -workflows_dir="$script_dir/../skills/adaptive-delivery/references/workflows" -tab=$(printf '\t') - -usage() { - cat <<'EOF' -usage: - adaptive-delivery-preflight prepare --repo --host - adaptive-delivery-preflight route --repo --host --profile [--route ] -EOF -} - -die() { - printf 'adaptive-delivery-preflight: %s\n' "$*" >&2 - exit 2 -} - -require_value() { - test "$#" -ge 2 && test -n "$2" || die "missing value for $1" -} - -validate_host() { - case "$1" in - codex|claude) ;; - *) die "unsupported host: $1" ;; - esac -} - -validate_profile() { - case "$1" in - ''|*[!a-z0-9-]*) die "unsupported profile: $1" ;; - esac -} - -split_route_tuple() { - tuple_label=$1 - tuple_value=$2 - tuple_old_ifs=$IFS - IFS='|' - read -r tuple_harness tuple_provider tuple_model tuple_effort tuple_extra </dev/null) || - die "not a git working tree: $repo_path" - test "$inside_work_tree" = true || - die "not a git working tree: $repo_path" - git -C "$repo_path" rev-parse --show-toplevel -} - -bundled_route_records() { - test -r "$routes_file" || die "route configuration is unreadable: $routes_file" - test -r "$routes_parser" || die "route configuration parser is unreadable: $routes_parser" - awk -f "$routes_parser" "$routes_file" -} - -validate_route_record_row() { - route_row_source=$1 - route_row_host=$2 - route_row_profile=$3 - route_row_harness=$4 - route_row_provider=$5 - route_row_model=$6 - route_row_effort=$7 - route_row_fallback_model=$8 - route_row_fallback_effort=$9 - shift 9 - route_row_extra=${1:-} - test -n "$route_row_host" && test -n "$route_row_profile" && - test -n "$route_row_harness" && test -n "$route_row_provider" && - test -n "$route_row_model" && test -n "$route_row_effort" && - test -n "$route_row_fallback_model" && - test -n "$route_row_fallback_effort" && test -z "$route_row_extra" || - die "invalid $route_row_source route configuration row for host=$route_row_host profile=$route_row_profile" - validate_route "$route_row_source route for profile=$route_row_profile" \ - "$route_row_host" "$route_row_harness" "$route_row_provider" \ - "$route_row_model" "$route_row_effort" -} - -route_records() { - route_repo=$1 - bundled_records=$(bundled_route_records) - repository_config="$route_repo/.darrow/config.json" - - while IFS="$tab" read -r host profile harness provider model effort fallback_model fallback_effort extra; do - test -n "$host" || continue - validate_route_record_row bundled "$host" "$profile" "$harness" \ - "$provider" "$model" "$effort" "$fallback_model" "$fallback_effort" \ - "${extra:-}" - done <<<"$bundled_records" - - if test ! -e "$repository_config" && test ! -L "$repository_config"; then - while IFS="$tab" read -r host profile harness provider model effort fallback_model fallback_effort; do - test -n "$host" || continue - printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\tbundled\n' \ - "$host" "$profile" "$harness" "$provider" "$model" "$effort" \ - "$fallback_model" "$fallback_effort" - done <<<"$bundled_records" - return - fi - - safe_regular_file "$repository_config" || - die "repository route configuration is unreadable or unsafe: $repository_config" - repository_records=$(awk -v allow_missing_routes=1 -f "$routes_parser" "$repository_config") || - die "repository route configuration is invalid: $repository_config" - - while IFS="$tab" read -r host profile harness provider model effort fallback_model fallback_effort extra; do - test -n "$host" || continue - validate_route_record_row repository "$host" "$profile" "$harness" \ - "$provider" "$model" "$effort" "$fallback_model" "$fallback_effort" \ - "${extra:-}" - bundled_match=0 - while IFS="$tab" read -r bundled_host bundled_profile _; do - if test "$host" = "$bundled_host" && test "$profile" = "$bundled_profile"; then - bundled_match=1 - break - fi - done <<<"$bundled_records" - test "$bundled_match" -eq 1 || - die "repository route is not in bundled host/profile catalog: host=$host profile=$profile" - done <<<"$repository_records" - - while IFS="$tab" read -r host profile harness provider model effort fallback_model fallback_effort; do - replacement= - while IFS="$tab" read -r override_host override_profile override_harness override_provider override_model override_effort override_fallback_model override_fallback_effort; do - if test "$host" = "$override_host" && test "$profile" = "$override_profile"; then - replacement="$override_harness$tab$override_provider$tab$override_model$tab$override_effort$tab$override_fallback_model$tab$override_fallback_effort" - break - fi - done <<<"$repository_records" - if test -n "$replacement"; then - printf '%s\t%s\t%s\trepository\n' "$host" "$profile" "$replacement" - else - printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\tbundled\n' \ - "$host" "$profile" "$harness" "$provider" "$model" "$effort" \ - "$fallback_model" "$fallback_effort" - fi - done <<<"$bundled_records" -} - -route_record() { - route_repo=$1 - route_host=$2 - route_profile=$3 - records=$(route_records "$route_repo") - found=0 - while IFS="$tab" read -r host profile harness provider model effort fallback_model fallback_effort policy_source; do - if test "$host" = "$route_host" && test "$profile" = "$route_profile"; then - printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \ - "$harness" "$provider" "$model" "$effort" "$fallback_model" \ - "$fallback_effort" "$policy_source" - found=1 - break - fi - done <<<"$records" - test "$found" -eq 1 || - die "no route for host=$route_host profile=$route_profile" -} - -validate_route() { - local label=$1 expected_host=$2 route_harness=$3 route_provider=$4 - local route_model=$5 route_effort=$6 - test "$route_harness" = "$expected_host" || - die "$label must use current host: $expected_host" - case "$expected_host|$route_provider" in - codex\|openai|claude\|anthropic) ;; - *) die "$label provider does not match host: $expected_host" ;; - esac - case "$route_model" in - ''|none|[!A-Za-z0-9]*|*[!A-Za-z0-9._-]*) die "$label has no concrete safe model" ;; - esac - case "$route_effort" in - low|medium|high|xhigh|max|ultra) ;; - *) die "$label has unsupported effort: $route_effort" ;; - esac -} - -validate_owner_route() { - owner_host=$1 - owner_model=$2 - case "$owner_host|$owner_model" in - codex\|gpt-5.6-luna) - die "model is not eligible for adaptive-delivery ownership: $owner_model" - ;; - esac -} - -prepare() { - repo= - host= - while test "$#" -gt 0; do - case "$1" in - --repo) - require_value "$@" - repo=$2 - shift 2 - ;; - --host) - require_value "$@" - host=$2 - shift 2 - ;; - *) die "unknown prepare option: $1" ;; - esac - done - test -n "$repo" && test -n "$host" || - die "prepare requires --repo and --host" - validate_host "$host" - repo=$(repository_root "$repo") - test -d "$workflows_dir" || - die "workflow directory is missing: $workflows_dir" - - revision=$(git -C "$repo" rev-parse HEAD) || die "cannot read repository revision: $repo" - tree_status=$(git -C "$repo" status --porcelain=v1 --untracked-files=all) || - die "cannot read working tree state: $repo" - if test -n "$tree_status"; then - tree_state=dirty - else - tree_state=clean - fi - records=$(route_records "$repo") - - printf 'format\tdarrow-native-goal-prepared-v2\n' - printf 'repo\t%s\n' "$repo" - printf 'base_revision\t%s\n' "$revision" - printf 'working_tree\t%s\n' "$tree_state" - - for instruction_name in AGENTS.md CLAUDE.md; do - instruction_path="$repo/$instruction_name" - if test -e "$instruction_path"; then - test -r "$instruction_path" || - die "repository instruction is unreadable: $instruction_path" - printf 'instruction\t%s\n' "$instruction_path" - fi - done - - route_count=0 - while IFS="$tab" read -r route_host profile harness provider model effort fallback_model fallback_effort policy_source extra; do - test -n "$profile" && test -n "$harness" && test -n "$provider" && - test -n "$model" && test -n "$effort" && test -n "$fallback_model" && - test -n "$fallback_effort" && test -n "$policy_source" && - test -z "${extra:-}" || - die "invalid route configuration row for profile: $profile" - test "$route_host" = "$host" || continue - validate_owner_route "$route_host" "$model" - printf 'route\t%s\t%s\t%s\t%s\t%s\n' \ - "$profile" "$harness" "$provider" "$model" "$effort" - printf 'route_policy_source\t%s\t%s\n' "$profile" "$policy_source" - route_count=$((route_count + 1)) - done <<<"$records" - test "$route_count" -gt 0 || die "no routes for host=$host" - - for workflow in fix-bug implement-feature change-feature refactor migration mechanical; do - workflow_file="$workflows_dir/$workflow.md" - test -r "$workflow_file" || - die "workflow document is unreadable: $workflow_file" - workflow_path=$(CDPATH='' cd -- "$workflows_dir" && pwd)/$workflow.md - printf 'workflow\t%s\t%s\n' "$workflow" "$workflow_path" - done -} - -route() { - repo= - host= - profile= - explicit_route= - while test "$#" -gt 0; do - case "$1" in - --repo) - require_value "$@" - repo=$2 - shift 2 - ;; - --host) - require_value "$@" - host=$2 - shift 2 - ;; - --profile) - require_value "$@" - profile=$2 - shift 2 - ;; - --route) - require_value "$@" - explicit_route=$2 - shift 2 - ;; - *) die "unknown route option: $1" ;; - esac - done - test -n "$repo" && test -n "$host" && test -n "$profile" || - die "route requires --repo, --host, and --profile" - validate_host "$host" - validate_profile "$profile" - repo=$(repository_root "$repo") - if test -n "$explicit_route"; then - split_route_tuple "--route" "$explicit_route" - harness=$tuple_harness - provider=$tuple_provider - model=$tuple_model - effort=$tuple_effort - validate_route 'explicit route' "$host" "$harness" "$provider" "$model" "$effort" - source=user - else - record=$(route_record "$repo" "$host" "$profile") - old_ifs=$IFS - IFS="$tab" - read -r harness provider model effort fallback_model fallback_effort policy_source <&2 - exit 2 -} -command=$1 -shift -case "$command" in - prepare) prepare "$@" ;; - route) route "$@" ;; - -h|--help|help) usage ;; - *) die "unknown command: $command" ;; -esac diff --git a/plugins/orchestration/darrow-adaptive-delivery/bin/adaptive-delivery-preflight.test.sh b/plugins/orchestration/darrow-adaptive-delivery/bin/adaptive-delivery-preflight.test.sh deleted file mode 100755 index 646e9066..00000000 --- a/plugins/orchestration/darrow-adaptive-delivery/bin/adaptive-delivery-preflight.test.sh +++ /dev/null @@ -1,303 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -script_dir=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) -preflight="$script_dir/adaptive-delivery-preflight" -temp_parent=${TMPDIR:-/tmp} -tmp_root=$(mktemp -d "${temp_parent%/}/darrow-adaptive-delivery-test.XXXXXX") -tmp_root=$(CDPATH='' cd -- "$tmp_root" && pwd -P) - -cleanup() { - chmod -R u+rwX "$tmp_root" 2>/dev/null || true - rm -rf "$tmp_root" -} -trap cleanup EXIT - -# A PATH-selected Bash must not replace the matrix interpreter, even in a -# negative test whose expected failure could otherwise hide the mistake. -mkdir -p "$tmp_root/conflicting-bin" -export CONFLICTING_BASH_MARKER="$tmp_root/wrong-interpreter" -cat >"$tmp_root/conflicting-bin/bash" <<'EOF' -#!/bin/sh -: >"$CONFLICTING_BASH_MARKER" -exit 97 -EOF -chmod +x "$tmp_root/conflicting-bin/bash" -export PATH="$tmp_root/conflicting-bin:$PATH" - -fail() { - printf 'not ok - %s\n' "$*" >&2 - exit 1 -} - -contains() { - case "$1" in - *"$2"*) ;; - *) fail "expected output to contain: $2" ;; - esac -} - -rejects() { - if "$@" >/dev/null 2>&1; then - fail "command unexpectedly succeeded: $*" - fi -} - -refuses() { - expected_error=$1 - shift - refusal_status=0 - "$@" >"$tmp_root/refusal.out" 2>"$tmp_root/refusal.err" || refusal_status=$? - test "$refusal_status" -eq 2 || fail "expected refusal exit 2, got $refusal_status: $*" - test ! -s "$tmp_root/refusal.out" || fail "refusal emitted prepared or selected evidence" - contains "$(<"$tmp_root/refusal.err")" "$expected_error" -} - -write_policy() { - printf '{"routes":[{"host":"%s","profile":"routine","harness":"%s","provider":"%s","model":"%s","effort":"%s","fallbackModel":"none","fallbackEffort":"none"}]}\n' \ - "$1" "$2" "$3" "$4" "$5" >"$repo/.darrow/config.json" -} - -repo="$tmp_root/repo" -mkdir -p "$repo" -git -C "$repo" init -q -git -C "$repo" config user.name Test -git -C "$repo" config user.email test@example.invalid -printf 'base\n' >"$repo/value.txt" -printf 'Root guidance.\n' >"$repo/AGENTS.md" -git -C "$repo" add value.txt AGENTS.md -git -C "$repo" commit -qm initial -printf 'changed\n' >"$repo/value.txt" - -out=$("$BASH" "$preflight" prepare --repo "$repo" --host codex) -repo_abs=$(git -C "$repo" rev-parse --show-toplevel) -contains "$out" $'format\tdarrow-native-goal-prepared-v2' -contains "$out" $'repo\t'"$repo_abs" -contains "$out" $'working_tree\tdirty' -case "$out" in - *$'preexisting_change\t'*) fail "prepare leaked relative porcelain paths" ;; -esac -contains "$out" $'instruction\t'"$repo_abs/AGENTS.md" -contains "$out" $'route\troutine\tcodex\topenai\tgpt-5.6-terra\tmedium' -contains "$out" $'route\troutine-plus\tcodex\topenai\tgpt-5.6-terra\thigh' -contains "$out" $'route\tscaled\tcodex\topenai\tgpt-5.6-sol\tmedium' -contains "$out" $'route\trepo-wide\tcodex\topenai\tgpt-5.6-sol\thigh' -contains "$out" $'route\tjudgment\tcodex\topenai\tgpt-6-astra\thigh' -contains "$out" $'workflow\tfix-bug\t' -contains "$out" $'workflow\timplement-feature\t' -contains "$out" $'workflow\tmigration\t' -contains "$out" $'workflow\tmechanical\t' -case "$out" in - *'ledger'*|*'materialize'*|*'goal-objective'*) - fail "prepared evidence exposed removed lifecycle mechanics" - ;; -esac - -out=$("$BASH" "$preflight" prepare --repo "$repo" --host claude) -contains "$out" $'route\troutine\tclaude\tanthropic\tclaude-sonnet-5\tlow' -contains "$out" $'route\tjudgment\tclaude\tanthropic\tclaude-opus-5\thigh' - -out=$("$BASH" "$preflight" route --repo "$repo" --host codex --profile routine) -contains "$out" $'format\tdarrow-native-goal-route-v2' -contains "$out" $'profile\troutine' -contains "$out" $'selected_route\tcodex\topenai\tgpt-5.6-terra\tmedium' -contains "$out" $'route_source\tpolicy' -contains "$out" $'policy_route_source\tbundled' - -mkdir -p "$repo/.darrow" -cat >"$repo/.darrow/config.json" <<'EOF' -{"routes":[{"host":"codex","profile":"scaled","harness":"codex","provider":"openai","model":"gpt-5.6-sol","effort":"high","fallbackModel":"none","fallbackEffort":"none"}]} -EOF -out=$("$BASH" "$preflight" route --repo "$repo" --host codex --profile scaled) -contains "$out" $'selected_route\tcodex\topenai\tgpt-5.6-sol\thigh' -contains "$out" $'policy_route_source\trepository' -out=$("$BASH" "$preflight" route --repo "$repo" --host codex --profile routine) -contains "$out" $'selected_route\tcodex\topenai\tgpt-5.6-terra\tmedium' -contains "$out" $'policy_route_source\tbundled' - -out=$("$BASH" "$preflight" route --repo "$repo" --host codex --profile scaled \ - --route 'codex|openai|gpt-future-off-catalog|ultra') -contains "$out" $'selected_route\tcodex\topenai\tgpt-future-off-catalog\tultra' -contains "$out" $'route_source\tuser' -case "$out" in - *$'policy_route_source\t'*) - fail "explicit user route retained policy provenance" - ;; -esac - -rejects "$BASH" "$preflight" route --repo "$repo" --host codex --profile scaled \ - --route 'claude|anthropic|claude-sonnet-5|low' -rejects "$BASH" "$preflight" route --repo "$repo" --host codex --profile scaled \ - --route 'codex|anthropic|gpt-5.6-luna|high' -rejects "$BASH" "$preflight" route --repo "$repo" --host codex --profile scaled \ - --route 'codex|openai|none|none' -rejects "$BASH" "$preflight" route --repo "$repo" --host codex --profile scaled \ - --route 'codex|openai|gpt-5.6-luna|medium' -rejects "$BASH" "$preflight" route --repo "$repo" --host codex --profile scaled \ - --route 'codex|openai|gpt-5.6-luna|impossible' - -cat >"$repo/.darrow/config.json" <<'EOF' -{"routes":[{"host":"codex","profile":"routine","harness":"codex","provider":"openai","model":"gpt-5.6-luna","effort":"medium","fallbackModel":"none","fallbackEffort":"none"}]} -EOF -rejects "$BASH" "$preflight" prepare --repo "$repo" --host codex -rejects "$BASH" "$preflight" route --repo "$repo" --host codex --profile routine - -# Policy and explicit inputs must reject the same invalid semantic tuples. -while IFS='|' read -r route_host route_harness route_provider route_model route_effort; do - write_policy "$route_host" "$route_harness" "$route_provider" "$route_model" "$route_effort" - refuses 'adaptive-delivery-preflight:' "$BASH" "$preflight" route \ - --repo "$repo" --host "$route_host" --profile routine \ - --route "$route_harness|$route_provider|$route_model|$route_effort" - refuses 'adaptive-delivery-preflight:' "$BASH" "$preflight" route \ - --repo "$repo" --host "$route_host" --profile routine - refuses 'adaptive-delivery-preflight:' "$BASH" "$preflight" prepare \ - --repo "$repo" --host "$route_host" -done <<'EOF' -codex|codex|anthropic|gpt-5.6-terra|medium -claude|claude|openai|claude-sonnet-5|low -codex|claude|anthropic|claude-sonnet-5|low -claude|codex|openai|gpt-5.6-terra|medium -codex|codex|openai|none|medium -claude|claude|anthropic|none|low -codex|codex|openai|gpt-5.6-terra|impossible -claude|claude|anthropic|claude-sonnet-5|none -codex|codex|openai|unsafe model|medium -codex|codex|openai|.unsafe|medium -codex|codex|openai||medium -EOF - -# Preserve every supported effort and valid off-catalog models for both hosts. -for route_host in codex claude; do - case "$route_host" in - codex) route_provider=openai ;; - claude) route_provider=anthropic ;; - esac - for route_effort in low medium high xhigh max ultra; do - write_policy "$route_host" "$route_host" "$route_provider" future-model.1 "$route_effort" - expected_tuple="$route_host"$'\t'"$route_provider"$'\t'"future-model.1"$'\t'"$route_effort" - out=$("$BASH" "$preflight" prepare --repo "$repo" --host "$route_host") - contains "$out" $'route\troutine\t'"$expected_tuple" - out=$("$BASH" "$preflight" route --repo "$repo" --host "$route_host" --profile routine) - contains "$out" $'selected_route\t'"$expected_tuple" - contains "$out" $'policy_route_source\trepository' - out=$("$BASH" "$preflight" route --repo "$repo" --host "$route_host" --profile routine \ - --route "$route_host|$route_provider|future-model.1|$route_effort") - contains "$out" $'selected_route\t'"$expected_tuple" - contains "$out" $'route_source\tuser' - done -done - -printf '{malformed\n' >"$repo/.darrow/config.json" -rejects "$BASH" "$preflight" prepare --repo "$repo" --host codex -rm -f "$repo/.darrow/config.json" -ln -s "$script_dir/../config/routes.json" "$repo/.darrow/config.json" -rejects "$BASH" "$preflight" route --repo "$repo" --host codex --profile routine -rm -f "$repo/.darrow/config.json" - -linked_repo="$tmp_root/linked-repo" -git -C "$repo" worktree add -q -b linked-goal-test "$linked_repo" -mkdir -p "$linked_repo/.darrow" "$linked_repo/nested" -cat >"$linked_repo/.darrow/config.json" <<'EOF' -{"routes":[{"host":"codex","profile":"routine","harness":"codex","provider":"openai","model":"gpt-5.6-sol","effort":"medium","fallbackModel":"none","fallbackEffort":"none"}]} -EOF -out=$("$BASH" "$preflight" route --repo "$linked_repo/nested" --host codex \ - --profile routine) -contains "$out" $'selected_route\tcodex\topenai\tgpt-5.6-sol\tmedium' -contains "$out" $'policy_route_source\trepository' - -# A second committed repository must never supply A's identity or evidence. -other_repo="$tmp_root/other repo" -mkdir -p "$other_repo" "$repo/nested" -git -C "$other_repo" init -q -git -C "$other_repo" config user.name Test -git -C "$other_repo" config user.email test@example.invalid -printf 'other\n' >"$other_repo/other.txt" -git -C "$other_repo" add other.txt -git -C "$other_repo" commit -qm other -printf '[invalid\n' >"$tmp_root/foreign-git-config" - -assert_requested_repo() { - out=$(env "$@" "$BASH" "$preflight" prepare --repo "$requested_repo/nested" --host codex) - contains "$out" $'repo\t'"$requested_repo" - contains "$out" $'base_revision\t'"$requested_revision" - contains "$out" $'working_tree\tdirty' - contains "$out" $'instruction\t'"$requested_repo/AGENTS.md" - contains "$out" $'route\troutine\tcodex\topenai\t'"$requested_model"$'\tmedium' - out=$(env "$@" "$BASH" "$preflight" route --repo "$requested_repo/nested" --host codex --profile routine) - contains "$out" $'selected_route\tcodex\topenai\t'"$requested_model"$'\tmedium' -} - -for requested_repo in "$repo_abs" "$linked_repo"; do - requested_revision=$(git -C "$requested_repo" rev-parse HEAD) - case "$requested_repo" in - "$repo_abs") requested_model=gpt-5.6-terra ;; - *) requested_model=gpt-5.6-sol ;; - esac - assert_requested_repo "GIT_DIR=$other_repo/.git" "GIT_WORK_TREE=$other_repo" - assert_requested_repo "GIT_DIR=$other_repo/.git" - assert_requested_repo "GIT_WORK_TREE=$other_repo" - assert_requested_repo "GIT_COMMON_DIR=$other_repo/.git" - assert_requested_repo "GIT_INDEX_FILE=$other_repo/.git/index" - assert_requested_repo "GIT_OBJECT_DIRECTORY=$other_repo/.git/objects" \ - "GIT_ALTERNATE_OBJECT_DIRECTORIES=$tmp_root/missing-objects" - assert_requested_repo "GIT_CEILING_DIRECTORIES=$requested_repo" - assert_requested_repo GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=core.worktree "GIT_CONFIG_VALUE_0=$other_repo" - assert_requested_repo "GIT_CONFIG_PARAMETERS='core.bare=true'" - assert_requested_repo "GIT_CONFIG_GLOBAL=$tmp_root/foreign-git-config" - assert_requested_repo "GIT_CONFIG_SYSTEM=$tmp_root/foreign-git-config" -done - -bare_repo="$tmp_root/bare.git" -git init --bare -q "$bare_repo" -for invalid_repo in "$bare_repo" "$repo/.git" "$tmp_root"; do - refuses 'not a git working tree:' env "GIT_DIR=$other_repo/.git" "GIT_WORK_TREE=$other_repo" \ - "$BASH" "$preflight" prepare --repo "$invalid_repo" --host codex - refuses 'not a git working tree:' "$BASH" "$preflight" route \ - --repo "$invalid_repo" --host codex --profile routine -done - -# A successful Git exit with false output is still outside a working tree; -# a failed status must not be turned into clean prepared evidence. -mkdir -p "$tmp_root/git-bin" -export PREFLIGHT_TEST_GIT -PREFLIGHT_TEST_GIT=$(command -v git) -cat >"$tmp_root/git-bin/git" <<'EOF' -#!/bin/sh -case "$*" in - *'rev-parse --is-inside-work-tree') - if [ "$PREFLIGHT_TEST_FAILURE" = outside ]; then - printf 'false\n' - exit 0 - fi - ;; - *'status --porcelain=v1'*) - if [ "$PREFLIGHT_TEST_FAILURE" = status ]; then - exit 1 - fi - ;; -esac -exec "$PREFLIGHT_TEST_GIT" "$@" -EOF -chmod +x "$tmp_root/git-bin/git" -refuses 'not a git working tree:' env "PATH=$tmp_root/git-bin:$PATH" PREFLIGHT_TEST_FAILURE=outside \ - "$BASH" "$preflight" prepare --repo "$repo" --host codex -refuses 'cannot read working tree state:' env "PATH=$tmp_root/git-bin:$PATH" PREFLIGHT_TEST_FAILURE=status \ - "$BASH" "$preflight" prepare --repo "$repo" --host codex - -git -C "$repo" worktree remove --force "$linked_repo" - -rejects "$BASH" "$preflight" step start --repo "$repo" --host codex -rejects "$BASH" "$preflight" materialize-objective --repo "$repo" --goal-file x -rejects "$BASH" "$preflight" launch --host codex - -help=$("$BASH" "$preflight" --help) -contains "$help" 'adaptive-delivery-preflight prepare' -contains "$help" 'adaptive-delivery-preflight route' -case "$help" in - *'step '*|*'ledger'*|*'materialize'*|*'launch '*) - fail "help exposed removed lifecycle commands" - ;; -esac - -test ! -e "$CONFLICTING_BASH_MARKER" || fail "test invoked PATH-selected Bash" -printf 'ok - adaptive delivery preflight and route helper\n' diff --git a/plugins/orchestration/darrow-adaptive-delivery/bin/claude-agent-route b/plugins/orchestration/darrow-adaptive-delivery/bin/claude-agent-route deleted file mode 100755 index 69ef8536..00000000 --- a/plugins/orchestration/darrow-adaptive-delivery/bin/claude-agent-route +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -usage() { - cat <<'EOF' -usage: claude-agent-route --provider --model --effort -EOF -} - -die() { - printf 'claude-agent-route: %s\n' "$*" >&2 - exit 2 -} - -require_value() { - test "$#" -ge 2 && test -n "$2" || die "missing value for $1" -} - -provider= -model= -effort= -while test "$#" -gt 0; do - case "$1" in - --provider) - require_value "$@" - provider=$2 - shift 2 - ;; - --model) - require_value "$@" - model=$2 - shift 2 - ;; - --effort) - require_value "$@" - effort=$2 - shift 2 - ;; - -h|--help) - usage - exit 0 - ;; - *) die "unknown option: $1" ;; - esac -done - -test -n "$provider" && test -n "$model" && test -n "$effort" || - die "--provider, --model, and --effort are required" -test "$provider" = anthropic || die "unsupported provider: $provider" - -for selector in \ - CLAUDE_CODE_USE_BEDROCK \ - CLAUDE_CODE_USE_MANTLE \ - CLAUDE_CODE_USE_VERTEX \ - CLAUDE_CODE_USE_FOUNDRY \ - CLAUDE_CODE_USE_ANTHROPIC_AWS; do - selector_value=$(printenv "$selector" 2>/dev/null || true) - test -z "$selector_value" || - die "Claude provider is not observably direct Anthropic: $selector is set" -done -base_url=${ANTHROPIC_BASE_URL:-} -case "$base_url" in - ''|https://api.anthropic.com|https://api.anthropic.com/) ;; - *) die "Claude provider is not observably direct Anthropic: ANTHROPIC_BASE_URL is custom" ;; -esac - -case "$model" in - claude-*) model_suffix=${model#claude-} ;; - *) die "model must be a concrete Claude model ID: $model" ;; -esac -case "$model_suffix" in - ''|*[!a-zA-Z0-9.-]*) die "unsafe model ID: $model" ;; -esac -case "$effort" in - low|medium|high|xhigh|max) ;; - *) die "unsupported effort: $effort" ;; -esac - -script_dir=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) -plugin_dir=$(CDPATH='' cd -- "$script_dir/.." && pwd) -agent_name="adaptive-delivery-$model_suffix-$effort" -agent_file="$plugin_dir/agents/$agent_name.md" -test -r "$agent_file" || die "no bundled runner for $model/$effort" -grep -F -x -- "name: $agent_name" "$agent_file" >/dev/null || - die "runner name does not match route: $agent_file" -grep -F -x -- "model: $model" "$agent_file" >/dev/null || - die "runner model does not match route: $agent_file" -grep -F -x -- "effort: $effort" "$agent_file" >/dev/null || - die "runner effort does not match route: $agent_file" - -model_override=${CLAUDE_CODE_SUBAGENT_MODEL:-} -case "$model_override" in - ''|"$model") ;; - *) die "CLAUDE_CODE_SUBAGENT_MODEL overrides selected model: $model_override" ;; -esac -effort_override=${CLAUDE_CODE_EFFORT_LEVEL:-} -case "$effort_override" in - ''|"$effort") ;; - *) die "CLAUDE_CODE_EFFORT_LEVEL overrides selected effort: $effort_override" ;; -esac - -printf 'format\tdarrow-claude-agent-route-v1\n' -printf 'selected_route\tclaude\t%s\t%s\t%s\n' "$provider" "$model" "$effort" -printf 'subagent_type\tdarrow-adaptive-delivery:%s\n' "$agent_name" -printf 'agent_file\t%s\n' "$agent_file" diff --git a/plugins/orchestration/darrow-adaptive-delivery/bin/claude-launch.test.sh b/plugins/orchestration/darrow-adaptive-delivery/bin/claude-launch.test.sh deleted file mode 100755 index f7bcfd05..00000000 --- a/plugins/orchestration/darrow-adaptive-delivery/bin/claude-launch.test.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -script_dir=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) -plugin_dir=$(CDPATH='' cd -- "$script_dir/.." && pwd) -resolver="$script_dir/claude-agent-route" -skill="$plugin_dir/skills/adaptive-delivery/SKILL.md" -guide="$plugin_dir/skills/adaptive-delivery/references/claude-launch.md" - -fail() { - printf 'not ok - %s\n' "$*" >&2 - exit 1 -} - -contains() { - case "$1" in - *"$2"*) ;; - *) fail "expected output to contain: $2" ;; - esac -} - -out=$(bash "$resolver" --provider anthropic --model claude-sonnet-5 --effort low) -contains "$out" $'subagent_type\tdarrow-adaptive-delivery:adaptive-delivery-sonnet-5-low' -out=$(bash "$resolver" --provider anthropic --model claude-sonnet-5 --effort medium) -contains "$out" $'subagent_type\tdarrow-adaptive-delivery:adaptive-delivery-sonnet-5-medium' -out=$(bash "$resolver" --provider anthropic --model claude-opus-5 --effort high) -contains "$out" $'subagent_type\tdarrow-adaptive-delivery:adaptive-delivery-opus-5-high' - -grep -F -- '"name": "darrow-adaptive-delivery"' \ - "$plugin_dir/.claude-plugin/plugin.json" \ - "$plugin_dir/.codex-plugin/plugin.json" >/dev/null || - fail "plugin manifests retained the old package identity" - -if bash "$resolver" --provider anthropic --model claude-opus-5 --effort low \ - >/dev/null 2>&1; then - fail "unsupported Claude route resolved" -fi -if CLAUDE_CODE_EFFORT_LEVEL=high bash "$resolver" --provider anthropic \ - --model claude-sonnet-5 --effort low >/dev/null 2>&1; then - fail "conflicting Claude effort override was accepted" -fi -if CLAUDE_CODE_USE_VERTEX=1 bash "$resolver" --provider anthropic \ - --model claude-sonnet-5 --effort low >/dev/null 2>&1; then - fail "non-Anthropic provider selector was accepted" -fi - -grep -F -- '- phase: adaptive-delivery-owner' "$skill" >/dev/null || - fail "skill omitted the owner marker" -grep -F -- 'Invoke the resolved Agent exactly once' "$guide" >/dev/null || - fail "Claude guide omitted the one-Agent boundary" -grep -F -- 'use Claude' "$guide" >/dev/null || - fail "Claude guide omitted same-owner feedback transport" -if grep -F -- 'claude-owner-route' "$skill" "$guide" \ - "$plugin_dir"/agents/adaptive-delivery-*.md >/dev/null; then - fail "Claude launch surface retained parent-side route observation" -fi - -if grep -E 'adaptive-delivery-preflight step|Protocol ledger|claude-route-gate|materialize-objective|darrow-native-goal-report|- phase: human-feedback-(request|response)' \ - "$skill" "$guide" "$plugin_dir"/agents/adaptive-delivery-*.md >/dev/null; then - fail "Claude launch surface retained removed lifecycle protocol" -fi - -printf 'ok - Claude separate-owner launch contract\n' diff --git a/plugins/orchestration/darrow-adaptive-delivery/bin/codex-launch.test.sh b/plugins/orchestration/darrow-adaptive-delivery/bin/codex-launch.test.sh deleted file mode 100755 index 53f4a418..00000000 --- a/plugins/orchestration/darrow-adaptive-delivery/bin/codex-launch.test.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash -# shellcheck disable=SC2016 -# The single-quoted backticks below are literal Markdown contract text. -set -euo pipefail - -script_dir=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) -plugin_dir=$(CDPATH='' cd -- "$script_dir/.." && pwd) -skill="$plugin_dir/skills/adaptive-delivery/SKILL.md" -guide="$plugin_dir/skills/adaptive-delivery/references/codex-launch.md" - -fail() { - printf 'not ok - %s\n' "$*" >&2 - exit 1 -} - -require_line() { - grep -F -- "$2" "$1" >/dev/null || fail "missing contract text: $2" -} - -require_line "$skill" 'launch exactly one route-selected subagent owner' -require_line "$skill" '- phase: adaptive-delivery-owner' -require_line "$skill" 'A direct shell, Git, forge,' -require_line "$guide" '`fork_turns` set to `none`' -require_line "$guide" '`model` set to the selected concrete Codex model' -require_line "$guide" '`reasoning_effort` set to the selected effort' -# Assert the host controls, not one prose rendering of idle/active feedback. -# Live continuation cases cover the same-owner behavior at the public seam. -require_line "$guide" '`followup_task`' -require_line "$guide" '`send_message`' - -# Mentioning a host control in a prohibition is not a lifecycle invocation. -# Live transcript checks cover create_goal/update_goal usage, including mirrors. -if grep -E 'adaptive-delivery-preflight step|Protocol ledger|materialize-objective|darrow-native-goal-report' \ - "$skill" "$guide" >/dev/null; then - fail "Codex launch surface retained removed lifecycle protocol" -fi - -printf 'ok - Codex separate-owner launch contract\n' diff --git a/plugins/orchestration/darrow-adaptive-delivery/bin/routes-json.awk b/plugins/orchestration/darrow-adaptive-delivery/bin/routes-json.awk deleted file mode 100644 index 72d25fb5..00000000 --- a/plugins/orchestration/darrow-adaptive-delivery/bin/routes-json.awk +++ /dev/null @@ -1,299 +0,0 @@ -function fail(message) { - print "adaptive-delivery-preflight: invalid route configuration: " message > "/dev/stderr" - exit 2 -} - -function skip_space( character) { - while (position <= length(document)) { - character = substr(document, position, 1) - if (character == " " || character == "\t" || character == "\r" || character == "\n") - position++ - else - break - } -} - -function expect(character, actual) { - skip_space() - actual = substr(document, position, 1) - if (actual != character) - fail("expected " character " at byte " position) - position++ -} - -function decoded_unicode(hex, digits, value, index_value, digit) { - digits = "0123456789abcdef" - value = 0 - for (index_value = 1; index_value <= 4; index_value++) { - digit = index(digits, tolower(substr(hex, index_value, 1))) - 1 - value = value * 16 + digit - } - if (value >= 32 && value <= 126) - return sprintf("%c", value) - return "\\u" hex -} - -function string_value( result, character, escape, hex) { - skip_space() - if (substr(document, position, 1) != "\"") - fail("expected string at byte " position) - position++ - result = "" - while (position <= length(document)) { - character = substr(document, position++, 1) - if (character == "\"") - return result - if (character == "\\") { - escape = substr(document, position++, 1) - if (escape == "\"" || escape == "\\" || escape == "/") - result = result escape - else if (escape == "b" || escape == "f" || escape == "n" || - escape == "r" || escape == "t") - result = result "\\" escape - else if (escape == "u") { - hex = substr(document, position, 4) - if (length(hex) != 4 || hex !~ /^[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]$/) - fail("invalid unicode escape at byte " (position - 2)) - result = result decoded_unicode(hex) - position += 4 - } else - fail("invalid string escape at byte " (position - 2)) - } else { - if (character ~ /[[:cntrl:]]/) - fail("control character in string at byte " (position - 1)) - result = result character - } - } - fail("unterminated string") -} - -function safe_value(name, value) { - if (value !~ /^[A-Za-z0-9][A-Za-z0-9._-]*$/) - fail("unsafe or empty " name " value") -} - -function skip_number( character) { - if (substr(document, position, 1) == "-") - position++ - - character = substr(document, position, 1) - if (character == "0") { - position++ - if (substr(document, position, 1) ~ /[0-9]/) - fail("invalid JSON number at byte " position) - } else if (character ~ /[1-9]/) { - while (substr(document, position, 1) ~ /[0-9]/) - position++ - } else { - fail("invalid JSON number at byte " position) - } - - if (substr(document, position, 1) == ".") { - position++ - if (substr(document, position, 1) !~ /[0-9]/) - fail("invalid JSON number at byte " position) - while (substr(document, position, 1) ~ /[0-9]/) - position++ - } - - character = substr(document, position, 1) - if (character == "e" || character == "E") { - position++ - character = substr(document, position, 1) - if (character == "+" || character == "-") - position++ - if (substr(document, position, 1) !~ /[0-9]/) - fail("invalid JSON number at byte " position) - while (substr(document, position, 1) ~ /[0-9]/) - position++ - } -} - -function skip_literal(literal) { - if (substr(document, position, length(literal)) != literal) - fail("invalid JSON value at byte " position) - position += length(literal) -} - -function skip_array( separator) { - expect("[") - skip_space() - if (substr(document, position, 1) == "]") { - position++ - return - } - while (1) { - skip_value() - skip_space() - separator = substr(document, position++, 1) - if (separator == "]") - return - if (separator != ",") - fail("expected , or ] at byte " (position - 1)) - } -} - -function skip_object( separator) { - expect("{") - skip_space() - if (substr(document, position, 1) == "}") { - position++ - return - } - while (1) { - string_value() - expect(":") - skip_value() - skip_space() - separator = substr(document, position++, 1) - if (separator == "}") - return - if (separator != ",") - fail("expected , or } at byte " (position - 1)) - } -} - -function skip_value( character) { - skip_space() - character = substr(document, position, 1) - if (character == "\"") - string_value() - else if (character == "{") - skip_object() - else if (character == "[") - skip_array() - else if (character == "t") - skip_literal("true") - else if (character == "f") - skip_literal("false") - else if (character == "n") - skip_literal("null") - else if (character ~ /[-0-9]/) - skip_number() - else - fail("invalid JSON value at byte " position) -} - -function route_object( key, value, separator, field_count, route_key, field) { - for (field in route) - delete route[field] - expect("{") - skip_space() - if (substr(document, position, 1) == "}") - fail("route object must not be empty") - - while (1) { - key = string_value() - if (key != "host" && key != "profile" && key != "harness" && - key != "provider" && key != "model" && key != "effort" && - key != "fallbackModel" && key != "fallbackEffort") - fail("unknown route field: " key) - if (key in route) - fail("duplicate route field: " key) - expect(":") - value = string_value() - route[key] = value - field_count++ - - skip_space() - separator = substr(document, position++, 1) - if (separator == "}") - break - if (separator != ",") - fail("expected , or } at byte " (position - 1)) - } - - if (field_count != 8) - fail("each route must contain exactly eight fields") - safe_value("host", route["host"]) - safe_value("profile", route["profile"]) - safe_value("harness", route["harness"]) - safe_value("provider", route["provider"]) - safe_value("model", route["model"]) - safe_value("effort", route["effort"]) - safe_value("fallbackModel", route["fallbackModel"]) - safe_value("fallbackEffort", route["fallbackEffort"]) - - route_key = route["host"] SUBSEP route["profile"] - if (route_key in seen_route) - fail("duplicate route for host=" route["host"] " profile=" route["profile"]) - seen_route[route_key] = 1 - - records[++record_count] = route["host"] "\t" route["profile"] "\t" \ - route["harness"] "\t" route["provider"] "\t" route["model"] "\t" \ - route["effort"] "\t" route["fallbackModel"] "\t" route["fallbackEffort"] -} - -function routes_array( separator) { - expect("[") - skip_space() - if (substr(document, position, 1) == "]") { - if (!allow_missing_routes) - fail("bundled routes array must not be empty") - position++ - return - } - - while (1) { - route_object() - skip_space() - separator = substr(document, position++, 1) - if (separator == "]") - break - if (separator != ",") - fail("expected , or ] at byte " (position - 1)) - } -} - -function root_object( key, separator) { - expect("{") - skip_space() - if (substr(document, position, 1) == "}") { - position++ - skip_space() - if (position <= length(document)) - fail("unexpected content at byte " position) - if (!allow_missing_routes) - fail("root object must contain routes") - return - } - while (1) { - key = string_value() - if (key != "routes" && key != "reviewers") - fail("unknown root field: " key) - if (key in seen_root) - fail("duplicate root field: " key) - seen_root[key] = 1 - expect(":") - if (key == "routes") { - routes_array() - found_routes = 1 - } else { - skip_value() - } - skip_space() - separator = substr(document, position++, 1) - if (separator == "}") - break - if (separator != ",") - fail("expected , or } at byte " (position - 1)) - } - skip_space() - if (position <= length(document)) - fail("unexpected content at byte " position) - if (!found_routes && !allow_missing_routes) - fail("root object must contain routes") -} - -{ - document = document $0 "\n" -} - -END { - if (document == "") - fail("file is empty") - position = 1 - root_object() - for (record_index = 1; record_index <= record_count; record_index++) - print records[record_index] -} diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/SKILL.md b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/SKILL.md index 51a49bc6..88d3820c 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/SKILL.md +++ b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/SKILL.md @@ -42,13 +42,15 @@ reason: explicit-orchestration-entrypoint-required Resolve the repository, then bind the bundled helper without searching: -- Claude: use `${CLAUDE_PLUGIN_ROOT}/bin`; Claude substitutes the active +- Claude: use `${CLAUDE_PLUGIN_ROOT}/backend`; Claude substitutes the active plugin's absolute root in skill content. - Codex: for the activated file - `/skills/adaptive-delivery/SKILL.md`, use `/bin`. - Starting at the directory containing `SKILL.md`, this is `../../bin`. + `/skills/adaptive-delivery/SKILL.md`, use `/backend`. + Starting at the directory containing `SKILL.md`, this is `../../backend`. -Require the resulting `adaptive-delivery-preflight` path to be an executable regular file. Do not +Require readable `pyproject.toml` and `uv.lock` in that exact backend, plus UV, +Python 3.10–3.13, and Git. Use the frozen runtime-only entrypoints below on +Linux, macOS, or native Windows. Do not scan the repository, plugin caches, home directory, `PATH`, or machine for an alternative. If the host does not expose the active plugin path or the exact helper is unavailable, return `Status: launch_required` and make no mutation. @@ -57,8 +59,12 @@ Describe the unavailable launch boundary clearly, for example with Then run: +The examples use Bash line continuations. On native Windows, enter each UV +command on one PowerShell line, omitting the trailing backslashes and passing +the same arguments. Helper execution does not require Bash on either host. + ```sh -/bin/bash /adaptive-delivery-preflight prepare \ +uv run --quiet --frozen --no-dev --project "" adaptive-delivery-preflight prepare \ --repo --host ``` @@ -242,7 +248,7 @@ must be rejected; Luna remains available for explicit leaf work outside this orchestration. Otherwise resolve the policy route: ```sh -/bin/bash /adaptive-delivery-preflight route \ +uv run --quiet --frozen --no-dev --project "" adaptive-delivery-preflight route \ --repo --host --profile \ [--route 'harness|provider|model|effort'] ``` @@ -461,7 +467,8 @@ The separate route-selected subagent is the sole Darrow work owner. The accepted Codex launch carries its concrete route. On Claude, the resolver validates the scoped agent's model and effort frontmatter and rejects higher-priority environment overrides; launch without a per-call model -override. Do not launch in the current thread, create another goal inside the +override and explicitly include `run_in_background: false` in the Agent input, +even when foreground execution is the host default. Do not launch in the current thread, create another goal inside the owner, inspect child work, start a nested host process, retry with another route, or replace an accepted owner. diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/claude-readiness-nonready-stops.yaml b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/claude-readiness-nonready-stops.yaml index 2093ae09..e202139a 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/claude-readiness-nonready-stops.yaml +++ b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/claude-readiness-nonready-stops.yaml @@ -32,7 +32,7 @@ fixture: set -eu grep -Fx 'Claude non-ready request completed.' NOTES.md >/dev/null setup: >- - bash "{{case_dir}}/fixtures/install-implementation-readiness.sh" "$PWD" + uv run --quiet --frozen --no-dev --project "{{case_dir}}/../../../backend" adaptive-delivery-fixture install readiness "$PWD" "{{case_dir}}/fixtures" claude checks: - name: Claude non-ready verdict stops before mutation diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/implementation-readiness.test.sh b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/implementation-readiness.test.sh deleted file mode 100755 index 732ad9a2..00000000 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/implementation-readiness.test.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -script_dir=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) -installer="$script_dir/install-implementation-readiness.sh" -test_root=$(mktemp -d "${TMPDIR:-/tmp}/darrow-readiness-fixture-test.XXXXXX") -trap 'rm -rf "$test_root"' EXIT HUP INT TERM - -make_repo() { - make_repo_path=$1 - mkdir -p "$make_repo_path" - git -C "$make_repo_path" init -q - printf '%s\n' "$2" >"$make_repo_path/.readiness-verdict" -} - -codex_repo="$test_root/codex" -make_repo "$codex_repo" blocked -bash "$installer" "$codex_repo" "$script_dir" codex -test -x "$codex_repo/.agents/bin/implementation-readiness-fixture" -codex_result=$(bash "$codex_repo/.agents/bin/implementation-readiness-fixture" "$codex_repo") -grep -F "**Verdict:** \`blocked\`" <<<"$codex_result" >/dev/null -grep -F "**Type:** \`dependency\`" <<<"$codex_result" >/dev/null -grep -F "**Type:** \`unblock\`" <<<"$codex_result" >/dev/null -test "$(wc -l <"$codex_repo/.git/fixture-state/implementation-readiness-invocations" | tr -d ' ')" -eq 1 - -iterative_repo="$test_root/iterative" -make_repo "$iterative_repo" needs-decision-then-ready -bash "$installer" "$iterative_repo" "$script_dir" codex -first_result=$(bash "$iterative_repo/.agents/bin/implementation-readiness-fixture" "$iterative_repo") -second_result=$(bash "$iterative_repo/.agents/bin/implementation-readiness-fixture" "$iterative_repo") -grep -F "**Verdict:** \`needs-decision\`" <<<"$first_result" >/dev/null -grep -F "**Verdict:** \`ready\`" <<<"$second_result" >/dev/null -test "$(wc -l <"$iterative_repo/.git/fixture-state/implementation-readiness-invocations" | tr -d ' ')" -eq 2 - -claude_repo="$test_root/claude" -make_repo "$claude_repo" needs-discovery -bash "$installer" "$claude_repo" "$script_dir" claude -test -f "$claude_repo/.claude/skills/assess-implementation-readiness/SKILL.md" -test ! -e "$claude_repo/.claude/bin/implementation-readiness-fixture" -test ! -e "$claude_repo/.git/fixture-state/implementation-readiness-invocations" -grep -F 'Use the native Read tool, never Bash' \ - "$claude_repo/.claude/skills/assess-implementation-readiness/SKILL.md" >/dev/null -grep -F "**Verdict:** \`needs-discovery\`" \ - "$claude_repo/.git/fixture-state/implementation-readiness-result" >/dev/null -grep -F "**Type:** \`missing-information\`" \ - "$claude_repo/.git/fixture-state/implementation-readiness-result" >/dev/null -grep -F "**Type:** \`discovery\`" \ - "$claude_repo/.git/fixture-state/implementation-readiness-result" >/dev/null - -printf '%s\n' 'implementation-readiness fixture tests passed' diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/implementation-readiness/SKILL.fixture.md b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/implementation-readiness/SKILL.fixture.md index dba31f6c..1f9134fc 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/implementation-readiness/SKILL.fixture.md +++ b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/implementation-readiness/SKILL.fixture.md @@ -8,7 +8,7 @@ description: Assess whether one authoritative ticket, specification, plan, or re Resolve this file's directory and the repository root, then run exactly: ```sh -bash "$skill_dir/../../bin/implementation-readiness-fixture" "$repo" +uv run --quiet --frozen --no-dev --project "$skill_dir/../../backend" adaptive-delivery-fixture readiness "$repo" ``` Treat its human-readable response as this capability's complete result. Return diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/implementation-readiness/implementation-readiness-fixture b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/implementation-readiness/implementation-readiness-fixture deleted file mode 100644 index a3cd593e..00000000 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/implementation-readiness/implementation-readiness-fixture +++ /dev/null @@ -1,156 +0,0 @@ -#!/usr/bin/env bash -set -eu - -repo=$1 -mode=${2:-record} -case "$mode" in record|render-only) ;; *) exit 2 ;; esac -verdict_file="$repo/.readiness-verdict" -test -r "$verdict_file" || { - printf '%s\n' 'implementation-readiness-fixture: missing .readiness-verdict' >&2 - exit 2 -} -IFS= read -r configured_verdict <"$verdict_file" -if test "$mode" = record; then - git -C "$repo" status --porcelain --untracked-files=all \ - >"$repo/.git/fixture-state/implementation-readiness-pre-status" - printf '%s\n' "$configured_verdict" >>"$repo/.git/fixture-state/implementation-readiness-invocations" -fi - -verdict=$configured_verdict -if test "$configured_verdict" = needs-decision-then-ready; then - invocation_count=1 - if test "$mode" = record; then - invocation_count=$(wc -l <"$repo/.git/fixture-state/implementation-readiness-invocations" | tr -d ' ') - fi - if test "$invocation_count" -ge 2; then - verdict=ready - else - verdict=needs-decision - fi -fi - -case "$verdict" in - ready) - cat <<'EOF' -## Implementation readiness - -**Verdict:** `ready` - -### Basis - -- **Source:** authoritative fixture specification - - **Authority:** `authoritative` - - **Status:** `available` - - **Summary:** The requested outcome and exact verification command are defined. - -### Quality bar - -- **Criterion:** The requested fixture behavior is observable. - - **Oracle:** The fixture's check script exits zero for the requested content. - - **Verification:** Run the repository-provided check script after implementation. - -### Findings - -None. - -### Required next action - -- **Type:** `none` -- **Description:** Begin the bounded implementation under the existing goal authority. -EOF - ;; - needs-decision) - cat <<'EOF' -## Implementation readiness - -**Verdict:** `needs-decision` - -### Basis - -- **Source:** authoritative fixture specification - - **Authority:** `authoritative` - - **Status:** `available` - - **Summary:** The request deliberately leaves one product choice unresolved. - -### Quality bar - -None. - -### Findings - -- **Type:** `unresolved-decision` - - **Summary:** The authoritative request does not select the required behavior. - - **Evidence:** - - The fixture marks the request as requiring a decision before implementation. - -### Required next action - -- **Type:** `decision` -- **Description:** Select the required behavior before implementation starts. -EOF - ;; - needs-discovery) - cat <<'EOF' -## Implementation readiness - -**Verdict:** `needs-discovery` - -### Basis - -- **Source:** authoritative fixture specification - - **Authority:** `authoritative` - - **Status:** `available` - - **Summary:** A required behavior boundary has not yet been discovered. - -### Quality bar - -None. - -### Findings - -- **Type:** `missing-information` - - **Summary:** The request depends on an unverified behavior boundary. - - **Evidence:** - - The fixture marks the request as requiring discovery before implementation. - -### Required next action - -- **Type:** `discovery` -- **Description:** Discover and settle the required behavior boundary before implementation starts. -EOF - ;; - blocked) - cat <<'EOF' -## Implementation readiness - -**Verdict:** `blocked` - -### Basis - -- **Source:** authoritative fixture specification - - **Authority:** `authoritative` - - **Status:** `available` - - **Summary:** A required external prerequisite is unavailable. - -### Quality bar - -None. - -### Findings - -- **Type:** `dependency` - - **Summary:** Implementation cannot begin while the prerequisite is unavailable. - - **Evidence:** - - The fixture marks the request as externally blocked. - -### Required next action - -- **Type:** `unblock` -- **Description:** Restore the required prerequisite before implementation starts. -EOF - ;; - *) - printf 'implementation-readiness-fixture: unsupported verdict: %s\n' "$verdict" >&2 - exit 2 - ;; -esac diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/independent-review.test.sh b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/independent-review.test.sh deleted file mode 100644 index 1cbe89bb..00000000 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/independent-review.test.sh +++ /dev/null @@ -1,200 +0,0 @@ -#!/usr/bin/env bash -set -eu - -fixture_dir=$(cd "$(dirname "$0")" 2>/dev/null && pwd -P) || exit 1 -installer=$fixture_dir/install-independent-review.sh -source_skill=$fixture_dir/independent-review/SKILL.fixture.md -review_contract=independent-review-skill-contract-v1 -discoverable_fixture_skills=$(find "$fixture_dir" -name SKILL.md -print) -if [ -n "$discoverable_fixture_skills" ]; then - printf '%s\n' "eval fixture skills are host-discoverable: $discoverable_fixture_skills" >&2 - exit 1 -fi -test -f "$source_skill" -temporary_root=$(mktemp -d "${TMPDIR:-/tmp}/darrow-review-fixture-test.XXXXXX") || - exit 1 -trap 'rm -rf "$temporary_root"' EXIT HUP INT TERM - -repo=$temporary_root/repo -mkdir -p "$repo" -repo=$(cd "$repo" 2>/dev/null && pwd -P) || exit 1 -git -C "$repo" init -q -git -C "$repo" config user.name "Darrow Eval" -git -C "$repo" config user.email "eval@example.invalid" -printf '%s\n' '# Instructions' >"$repo/AGENTS.md" -printf '%s\n' 'before' >"$repo/candidate.txt" -git -C "$repo" add AGENTS.md candidate.txt -git -C "$repo" commit -q -m "chore: init" - -if "$BASH" "$installer" "$repo" "$fixture_dir" invalid >/dev/null 2>&1; then - printf '%s\n' 'invalid host was accepted' >&2 - exit 1 -fi - -"$BASH" "$installer" "$repo" "$fixture_dir" codex -test -x "$repo/.agents/bin/independent-review-fixture" -test -f "$repo/.agents/skills/independent-code-review/SKILL.md" -cmp "$source_skill" "$repo/.agents/skills/independent-code-review/SKILL.md" -test ! -e "$repo/.claude" - -printf '%s\n' 'after' >"$repo/candidate.txt" -printf '%s\n' 'candidate.txt' >"$repo/.git/fixture-review-event-path" -printf '%s\n' '0' >"$repo/.git/fixture-review-delay-seconds" -result=$("$BASH" "$repo/.agents/bin/independent-review-fixture" "$repo" comprehensive "$review_contract") -printf '%s\n' "$result" | - grep -Fx 'Independent review outcome: no blocking findings' >/dev/null -test "$(wc -l <"$repo/.git/fixture-state/independent-review-invocations" | tr -d ' ')" -eq 1 -test "$(awk -F '\t' 'NF == 3 && $2 == "clear" { print "yes" }' "$repo/.git/fixture-state/independent-review-invocations")" = yes -test "$(cut -f3 "$repo/.git/fixture-state/independent-review-invocations")" = comprehensive -test "$(cut -f1 "$repo/.git/fixture-state/independent-review-events")" = review_start -test "$(cut -f2 "$repo/.git/fixture-state/independent-review-events")" = "$(cut -f1 "$repo/.git/fixture-state/independent-review-invocations")" -test "$(awk -F '\t' '$1 == "review" && $2 == "clear" && $3 != "" { print "yes" }' "$repo/.git/fixture-state/review-events")" = yes -test "$(awk -F '\t' '$1 == "review_start" { start=$2 } $1 == "review_end" && $2 == start { print "yes"; exit }' "$repo/.git/fixture-state/review-events")" = yes -first_target=$(printf '%s\n' "$result" | sed -n 's/^Reviewed target: //p') -test "$("$BASH" "$repo/.agents/bin/independent-review-fixture" "$repo" fingerprint)" = "$first_target" - -review_starts=$(grep -c '^review_start' "$repo/.git/fixture-state/review-events") -printf '%s\n' '2' >"$repo/.git/fixture-review-delay-seconds" -concurrent_result=$temporary_root/concurrent-result -"$BASH" "$repo/.agents/bin/independent-review-fixture" "$repo" verify "$review_contract" >"$concurrent_result" & -review_pid=$! -attempts=0 -while [ "$(grep -c '^review_start' "$repo/.git/fixture-state/review-events")" -le "$review_starts" ]; do - attempts=$((attempts + 1)) - if [ "$attempts" -gt 3 ]; then - printf '%s\n' 'review fixture did not enter its wait boundary' >&2 - exit 1 - fi - sleep 1 -done -printf '%s\n' '# Changed instructions while review was pending' >"$repo/AGENTS.md" -wait "$review_pid" -grep -Fx 'Fix verification: inconclusive.' "$concurrent_result" >/dev/null -test "$(awk -F '\t' '$1 == "review_start" { start=$2 } $1 == "review_end" && $2 != start { print "yes" }' "$repo/.git/fixture-state/review-events")" = yes -printf '%s\n' '0' >"$repo/.git/fixture-review-delay-seconds" -printf '%s\n' '# Instructions' >"$repo/AGENTS.md" - -printf '%s\n' 'untracked one' >"$repo/new-file.txt" -result=$("$BASH" "$repo/.agents/bin/independent-review-fixture" "$repo" verify "$review_contract") -printf '%s\n' "$result" | - grep -Fx "Changed file: $repo/new-file.txt" >/dev/null -second_target=$(printf '%s\n' "$result" | sed -n 's/^Reviewed target: //p') -test "$first_target" != "$second_target" -printf '%s\n' 'untracked two' >"$repo/new-file.txt" -result=$("$BASH" "$repo/.agents/bin/independent-review-fixture" "$repo" verify "$review_contract") -third_target=$(printf '%s\n' "$result" | sed -n 's/^Reviewed target: //p') -test "$second_target" != "$third_target" - -touch "$repo/.git/fixture-review-fail" -printf '%s\n' 'Repair guidance (advisory): use a named default; alternatives are valid' \ - 'Resolution evidence: required behavior is restored' >"$repo/.git/fixture-review-repair-guidance" -printf '%s\n' 'fail target' >"$repo/candidate.txt" -result=$("$BASH" "$repo/.agents/bin/independent-review-fixture" "$repo" verify "$review_contract") -printf '%s\n' "$result" | - grep -Fx 'Fix verification: continue.' >/dev/null -printf '%s\n' "$result" | grep -F 'Blocking finding: ' >/dev/null -grep -F 'Repair guidance (advisory): use a named default; alternatives are valid' <<<"$result" >/dev/null -grep -F 'Resolution evidence: required behavior is restored' <<<"$result" >/dev/null -rm "$repo/.git/fixture-review-repair-guidance" -rm "$repo/.git/fixture-review-fail" - -touch "$repo/.git/fixture-review-inconclusive" -printf '%s\n' 'inconclusive target' >"$repo/candidate.txt" -result=$("$BASH" "$repo/.agents/bin/independent-review-fixture" "$repo" verify "$review_contract") -printf '%s\n' "$result" | - grep -Fx 'Fix verification: inconclusive.' >/dev/null -printf '%s\n' "$result" | grep -F 'Evidence gap: ' >/dev/null -rm "$repo/.git/fixture-review-inconclusive" - -touch "$repo/.git/fixture-review-blocked" -printf '%s\n' 'blocked target' >"$repo/candidate.txt" -result=$("$BASH" "$repo/.agents/bin/independent-review-fixture" "$repo" verify "$review_contract") -printf '%s\n' "$result" | - grep -Fx 'Fix verification: unavailable.' >/dev/null -printf '%s\n' "$result" | grep -F 'Evidence gap: ' >/dev/null -rm "$repo/.git/fixture-review-blocked" - -rm -f "$repo/.git/fixture-state/independent-review-invocations" -printf '%s\t%s\t%s\n' \ - blocking 'sequence blocker' 'sequence advisory' \ - clear '' '' >"$repo/.git/fixture-review-sequence" -result=$("$BASH" "$repo/.agents/bin/independent-review-fixture" "$repo" comprehensive "$review_contract") -printf '%s\n' "$result" | - grep -Fx 'Independent review outcome: blocking findings' >/dev/null -printf '%s\n' "$result" | - grep -Fx 'Blocking finding: sequence blocker' >/dev/null -printf '%s\n' "$result" | - grep -Fx 'Advisory finding: sequence advisory' >/dev/null -printf '%s\n' 'repaired' >"$repo/candidate.txt" -result=$("$BASH" "$repo/.agents/bin/independent-review-fixture" "$repo" verify "$review_contract") -printf '%s\n' "$result" | - grep -Fx 'Fix verification: clear.' >/dev/null -test "$(wc -l <"$repo/.git/fixture-state/independent-review-invocations" | tr -d ' ')" -eq 2 -if "$BASH" "$repo/.agents/bin/independent-review-fixture" "$repo" verify "$review_contract" >/dev/null 2>&1; then - printf '%s\n' 'exhausted review sequence was accepted' >&2 - exit 1 -fi - -for final_outcome in clear continue no_progress; do - rm -f "$repo/.git/fixture-state/independent-review-invocations" - printf '%s\t%s\t%s\n' \ - blocking 'original blocker' '' \ - continue 'Original cause narrowed to null input' '' \ - "$final_outcome" 'Remaining original blocker' '' >"$repo/.git/fixture-review-sequence" - printf '%s\n' 'initial candidate' >"$repo/candidate.txt" - "$BASH" "$repo/.agents/bin/independent-review-fixture" "$repo" comprehensive "$review_contract" >/dev/null - printf '%s\n' 'first repair' >"$repo/candidate.txt" - result=$("$BASH" "$repo/.agents/bin/independent-review-fixture" "$repo" verify "$review_contract") - grep -Fx 'Fix verification: continue.' <<<"$result" >/dev/null - grep -F 'Original cause narrowed to null input' <<<"$result" >/dev/null - printf '%s\n' 'second repair' >"$repo/candidate.txt" - result=$("$BASH" "$repo/.agents/bin/independent-review-fixture" "$repo" verify "$review_contract") - grep -Fx "Fix verification: $final_outcome." <<<"$result" >/dev/null - test "$(cut -f3 "$repo/.git/fixture-state/independent-review-invocations" | tr '\n' ' ')" = 'comprehensive verify verify ' - test "$(cut -f1 "$repo/.git/fixture-state/independent-review-invocations" | sort -u | wc -l | tr -d ' ')" -eq 3 - current_target=$("$BASH" "$repo/.agents/bin/independent-review-fixture" "$repo" fingerprint) - grep -Fx "Reviewed target: $current_target" <<<"$result" >/dev/null - if "$BASH" "$repo/.agents/bin/independent-review-fixture" "$repo" comprehensive "$review_contract" >/dev/null 2>&1; then - printf '%s\n' 'comprehensive review reset the closed finding set' >&2 - exit 1 - fi -done - -rm -f "$repo/.git/fixture-state/independent-review-invocations" -printf '%s\t%s\t%s\n' \ - blocking 'unavailable sequence blocker' '' \ - unavailable '' '' >"$repo/.git/fixture-review-sequence" -result=$("$BASH" "$repo/.agents/bin/independent-review-fixture" "$repo" comprehensive "$review_contract") -printf '%s\n' "$result" | - grep -Fx 'Independent review outcome: blocking findings' >/dev/null -printf '%s\n' 'unavailable repair' >"$repo/candidate.txt" -result=$("$BASH" "$repo/.agents/bin/independent-review-fixture" "$repo" verify "$review_contract") -printf '%s\n' "$result" | - grep -Fx 'Fix verification: unavailable.' >/dev/null -printf '%s\n' "$result" | grep -F 'Evidence gap: ' >/dev/null -test "$(cut -f3 "$repo/.git/fixture-state/independent-review-invocations" | tr '\n' ' ')" = 'comprehensive verify ' - -rm -f "$repo/.git/fixture-state/independent-review-invocations" -printf '%s\t%s\t%s\n' invalid 'bad outcome' '' >"$repo/.git/fixture-review-sequence" -if "$BASH" "$repo/.agents/bin/independent-review-fixture" "$repo" comprehensive "$review_contract" >/dev/null 2>&1; then - printf '%s\n' 'invalid review sequence outcome was accepted' >&2 - exit 1 -fi - -claude_repo=$temporary_root/claude-repo -mkdir -p "$claude_repo" -"$BASH" "$installer" "$claude_repo" "$fixture_dir" claude -test -x "$claude_repo/.claude/bin/independent-review-fixture" -test -f "$claude_repo/.claude/skills/independent-code-review/SKILL.md" -cmp "$source_skill" "$claude_repo/.claude/skills/independent-code-review/SKILL.md" -test ! -e "$claude_repo/.agents" - -both_repo=$temporary_root/both-repo -mkdir -p "$both_repo" -"$BASH" "$installer" "$both_repo" "$fixture_dir" both -test -x "$both_repo/.agents/bin/independent-review-fixture" -test -x "$both_repo/.claude/bin/independent-review-fixture" -cmp "$source_skill" "$both_repo/.agents/skills/independent-code-review/SKILL.md" -cmp "$source_skill" "$both_repo/.claude/skills/independent-code-review/SKILL.md" - -printf '%s\n' 'all independent-review fixture tests passed' diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/independent-review/SKILL.fixture.md b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/independent-review/SKILL.fixture.md index 093a920d..b28046c4 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/independent-review/SKILL.fixture.md +++ b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/independent-review/SKILL.fixture.md @@ -19,14 +19,14 @@ Resolve this file's directory and the repository root. For the first comprehensive review run exactly: ```sh -bash "$skill_dir/../../bin/independent-review-fixture" "$repo" comprehensive independent-review-skill-contract-v1 +uv run --quiet --frozen --no-dev --project "$skill_dir/../../backend" adaptive-delivery-fixture review "$repo" comprehensive independent-review-skill-contract-v1 ``` After an enclosing goal repairs that comprehensive review's closed finding set, fix-verify only those attempts and direct repair-caused regressions by running: ```sh -bash "$skill_dir/../../bin/independent-review-fixture" "$repo" verify independent-review-skill-contract-v1 +uv run --quiet --frozen --no-dev --project "$skill_dir/../../backend" adaptive-delivery-fixture review "$repo" verify independent-review-skill-contract-v1 ``` Treat the ordinary prose response as this capability's complete, read-only diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/independent-review/independent-review-fixture b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/independent-review/independent-review-fixture deleted file mode 100644 index 364277ab..00000000 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/independent-review/independent-review-fixture +++ /dev/null @@ -1,203 +0,0 @@ -#!/usr/bin/env bash -# Deterministic intent-matched review capability used only by adaptive-delivery evals. -set -u - -repo=${1:-} -mode=${2:-} -contract=${3:-} -[ -n "$repo" ] || { printf '%s\n' 'repository path is required' >&2; exit 2; } -case $mode in - comprehensive | verify | fingerprint) ;; - *) printf '%s\n' 'usage: independent-review-fixture REPOSITORY comprehensive|verify|fingerprint [contract]' >&2; exit 2 ;; -esac -root=$(git -C "$repo" rev-parse --show-toplevel 2>/dev/null) || exit 2 -root=$(cd "$root" && pwd -P) || exit 2 -if [ "$mode" != fingerprint ] && - [ "$(printf '%s' "$contract" | git -C "$root" hash-object --stdin)" != b1d8706dabad37d43795dcba094c36681b029df5 ]; then - printf '%s\n' 'review capability contract is required' >&2 - exit 2 -fi -git_dir=$(git -C "$root" rev-parse --absolute-git-dir 2>/dev/null) || exit 2 -packet_file=$(mktemp "$git_dir/independent-review-fixture.XXXXXX") || exit 2 -changed_file=$(mktemp "$git_dir/independent-review-changed.XXXXXX") || exit 2 -untracked_file=$(mktemp "$git_dir/independent-review-untracked.XXXXXX") || exit 2 -end_packet_file=$(mktemp "$git_dir/independent-review-end-fixture.XXXXXX") || exit 2 -end_changed_file=$(mktemp "$git_dir/independent-review-end-changed.XXXXXX") || exit 2 -end_untracked_file=$(mktemp "$git_dir/independent-review-end-untracked.XXXXXX") || exit 2 -trap 'rm -f "$packet_file" "$changed_file" "$untracked_file" "$end_packet_file" "$end_changed_file" "$end_untracked_file"' EXIT - -capture_worktree() { - capture_packet=$1 - capture_changed=$2 - capture_untracked=$3 - : >"$capture_packet" || return 1 - : >"$capture_changed" || return 1 - : >"$capture_untracked" || return 1 - git -C "$root" diff --binary --full-index HEAD -- >"$capture_packet" || return 1 - git -C "$root" diff --name-only -z HEAD -- >"$capture_changed" || return 1 - git -C "$root" ls-files --others --exclude-standard -z >"$capture_untracked" || - return 1 - while IFS= read -r -d '' path; do - oid=$(git -C "$root" hash-object -- "$path" 2>/dev/null) || return 1 - printf 'untracked_path\0%s\0untracked_oid\0%s\0' "$path" "$oid" >>"$capture_packet" - cat "$root/$path" >>"$capture_packet" || return 1 - printf '\0' >>"$capture_packet" - printf '%s\0' "$path" >>"$capture_changed" - done <"$capture_untracked" - capture_base=$(git -C "$root" rev-parse HEAD 2>/dev/null) || return 1 - capture_checksum=$(git -C "$root" hash-object "$capture_packet" 2>/dev/null) || - return 1 - printf 'WORKTREE@%s+%s\n' "$capture_base" "$capture_checksum" -} - -target=$(capture_worktree "$packet_file" "$changed_file" "$untracked_file") || - exit 2 -if [ "$mode" = fingerprint ]; then - printf '%s\n' "$target" - exit 0 -fi -if [ "$mode" = comprehensive ] && [ -f "$git_dir/fixture-state/independent-review-invocations" ]; then - printf '%s\n' 'comprehensive review already established the closed finding set' >&2 - exit 2 -fi -if [ "$mode" = verify ] && [ ! -f "$git_dir/fixture-state/independent-review-invocations" ]; then - printf '%s\n' 'fix verification requires the original comprehensive review' >&2 - exit 2 -fi -printf 'review_start\t%s\n' "$target" >>"$git_dir/fixture-state/independent-review-events" -review_subject_oid= -if [ -f "$git_dir/fixture-review-event-path" ]; then - IFS= read -r review_subject <"$git_dir/fixture-review-event-path" || exit 2 - [ -n "$review_subject" ] && [ -f "$root/$review_subject" ] || exit 2 - review_subject_oid=$(git -C "$root" hash-object -- "$review_subject") || exit 2 - printf 'review_start\t%s\t%s\n' "$target" "$review_subject_oid" >>"$git_dir/fixture-state/review-events" - if [ -f "$git_dir/fixture-review-delay-seconds" ]; then - IFS= read -r review_delay <"$git_dir/fixture-review-delay-seconds" || exit 2 - case $review_delay in - '' | *[!0-9]*) exit 2 ;; - esac - sleep "$review_delay" || exit 2 - fi -fi - -outcome=clear -failure_evidence='Fixture policy rejects this candidate' -advisory_evidence= -unrelated_evidence= -if [ -f "$git_dir/fixture-review-sequence" ]; then - invocation=1 - if [ -f "$git_dir/fixture-state/independent-review-invocations" ]; then - invocation_count=$(wc -l <"$git_dir/fixture-state/independent-review-invocations" | tr -d ' ') - case $invocation_count in - '' | *[!0-9]*) exit 2 ;; - esac - invocation=$((invocation_count + 1)) - fi - sequence_line=$(sed -n "${invocation}p" "$git_dir/fixture-review-sequence") - [ -n "$sequence_line" ] || exit 2 - tab=$(printf '\t') - old_ifs=$IFS - IFS=$tab - read -r outcome failure_evidence advisory_evidence unrelated_evidence </dev/null 2>&1; then - outcome=blocking - failure_evidence="Fixture policy rejects changed content containing: $reject_pattern" - fi -fi - -if [ "$mode" = comprehensive ]; then - case $outcome in - clear | blocking | inconclusive | unavailable) ;; - *) printf '%s\n' 'comprehensive fixture outcome is invalid' >&2; exit 2 ;; - esac -else - case $outcome in - clear | continue | no_progress | blocked | inconclusive | unavailable) ;; - blocking) outcome='continue' ;; - *) printf '%s\n' 'verification fixture outcome is invalid' >&2; exit 2 ;; - esac -fi - -if [ -n "$review_subject_oid" ]; then - review_subject_end_oid=$(git -C "$root" hash-object -- "$review_subject") || - exit 2 - end_target=$(capture_worktree "$end_packet_file" "$end_changed_file" "$end_untracked_file") || - exit 2 - printf 'review_end\t%s\t%s\n' "$end_target" "$review_subject_end_oid" >>"$git_dir/fixture-state/review-events" - if [ "$end_target" != "$target" ]; then - outcome=inconclusive - failure_evidence='Reviewed worktree changed before the review response returned' - fi -fi - -if [ "$mode" = verify ] && - [ "$outcome" != inconclusive ] && [ "$outcome" != unavailable ] && - [ "$outcome" != blocked ]; then - read_tab=$(printf '\t') - while IFS=$read_tab read -r recorded_target _; do - if [ "$recorded_target" = "$target" ]; then - outcome=no_progress - failure_evidence='Target fingerprint repeats an earlier convergence state' - break - fi - done <"$git_dir/fixture-state/independent-review-invocations" -fi - -printf '%s\t%s\t%s\n' "$target" "$outcome" "$mode" >>"$git_dir/fixture-state/independent-review-invocations" -if [ -n "$review_subject_oid" ]; then - printf 'review\t%s\t%s\n' "$outcome" "$review_subject_oid" >>"$git_dir/fixture-state/review-events" -fi -if [ "$mode" = verify ]; then - printf 'Fix verification: %s.\n' "$outcome" -elif [ "$outcome" = clear ]; then - printf '%s\n' 'Independent review outcome: no blocking findings' -elif [ "$outcome" = blocking ]; then - printf '%s\n' 'Independent review outcome: blocking findings' -elif [ "$outcome" = inconclusive ]; then - printf '%s\n' 'Independent review outcome: inconclusive' -else - printf '%s\n' 'Independent review outcome: unavailable' -fi -printf 'Reviewed target: %s\n' "$target" -while IFS= read -r -d '' path; do - [ -n "$path" ] && printf 'Changed file: %s/%s\n' "$root" "$path" -done <"$changed_file" -if [ "$outcome" = clear ]; then - printf '%s\n' 'Summary: The current change has no blocking findings against the supplied goal and repository instructions.' -elif [ "$outcome" = blocking ] || [ "$outcome" = continue ] || [ "$outcome" = no_progress ]; then - printf 'Blocking finding: %s\n' "$failure_evidence" - if [ -e "$git_dir/fixture-review-repair-guidance" ]; then - [ -f "$git_dir/fixture-review-repair-guidance" ] && - [ -r "$git_dir/fixture-review-repair-guidance" ] || exit 2 - cat "$git_dir/fixture-review-repair-guidance" || exit 2 - fi -elif [ "$outcome" = blocked ]; then - printf 'Evidence gap: %s\n' "$failure_evidence" -elif [ "$outcome" = inconclusive ]; then - printf '%s\n' 'Evidence gap: The review could not reach a reliable conclusion from the available evidence.' -else - printf '%s\n' 'Evidence gap: Required independent review evidence is unavailable.' -fi -if [ -n "$advisory_evidence" ]; then - printf 'Advisory finding: %s\n' "$advisory_evidence" -fi -if [ -n "$unrelated_evidence" ]; then - printf 'Unrelated observation excluded: %s\n' "$unrelated_evidence" -fi diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/install-implementation-readiness.sh b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/install-implementation-readiness.sh deleted file mode 100644 index 056fa38c..00000000 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/install-implementation-readiness.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env bash -set -eu - -repo=$1 -fixture_dir=$2 -host=${3:-} -case "$host" in - codex) host_roots=.agents ;; - claude) host_roots=.claude ;; - both) host_roots='.agents .claude' ;; - *) - printf '%s\n' 'usage: install-implementation-readiness.sh REPO FIXTURE_DIR codex|claude|both' >&2 - exit 2 - ;; -esac -mkdir -p "$repo/.git/fixture-state" -for host_root in $host_roots; do - printf '/%s/\n' "$host_root" >>"$repo/.git/info/exclude" - mkdir -p "$repo/$host_root/skills/assess-implementation-readiness" \ - "$repo/$host_root/bin" - case "$host_root" in - .claude) - cp "$fixture_dir/implementation-readiness/SKILL.claude.fixture.md" \ - "$repo/$host_root/skills/assess-implementation-readiness/SKILL.md" - bash "$fixture_dir/implementation-readiness/implementation-readiness-fixture" \ - "$repo" render-only >"$repo/.git/fixture-state/implementation-readiness-result" - ;; - *) - cp "$fixture_dir/implementation-readiness/SKILL.fixture.md" \ - "$repo/$host_root/skills/assess-implementation-readiness/SKILL.md" - cp "$fixture_dir/implementation-readiness/implementation-readiness-fixture" \ - "$repo/$host_root/bin/implementation-readiness-fixture" - chmod +x "$repo/$host_root/bin/implementation-readiness-fixture" - ;; - esac -done diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/install-independent-review.sh b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/install-independent-review.sh deleted file mode 100644 index 48991344..00000000 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/install-independent-review.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash -set -eu - -repo=$1 -fixture_dir=$2 -host=${3:-} -case "$host" in - codex) host_roots=.agents ;; - claude) host_roots=.claude ;; - both) host_roots='.agents .claude' ;; - *) - printf '%s\n' 'usage: install-independent-review.sh REPO FIXTURE_DIR codex|claude|both' >&2 - exit 2 - ;; -esac -mkdir -p "$repo/.git/fixture-state" -for host_root in $host_roots; do - mkdir -p "$repo/$host_root/skills/independent-code-review" "$repo/$host_root/bin" - cp "$fixture_dir/independent-review/SKILL.fixture.md" "$repo/$host_root/skills/independent-code-review/SKILL.md" - cp "$fixture_dir/independent-review/independent-review-fixture" "$repo/$host_root/bin/independent-review-fixture" - chmod +x "$repo/$host_root/bin/independent-review-fixture" -done diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/install-verification.sh b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/install-verification.sh deleted file mode 100644 index 32cbb3bd..00000000 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/install-verification.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash -set -eu -repo=$1 -fixture_dir=$2 -"$BASH" "$fixture_dir/install-independent-review.sh" "$repo" "$fixture_dir" both -printf '%s\n' '.agents/' '.claude/' >>"$repo/.git/info/exclude" -for host_root in .agents .claude; do - mkdir -p "$repo/$host_root/skills/assess-candidate" - cp "$fixture_dir/verification/SKILL.fixture.md" "$repo/$host_root/skills/assess-candidate/SKILL.md" - cp "$fixture_dir/verification/verification-fixture" "$repo/$host_root/bin/verification-fixture" - chmod +x "$repo/$host_root/bin/verification-fixture" -done diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/owner-readiness.fixture.md b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/owner-readiness.fixture.md index f7e50032..9ca7b786 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/owner-readiness.fixture.md +++ b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/owner-readiness.fixture.md @@ -6,6 +6,6 @@ description: Assess readiness of an authoritative request, including reassessmen # Assess implementation readiness Resolve this file's directory as `skill_dir` and the repository root as `repo`. -Run `bash "$skill_dir/../../bin/implementation-readiness-fixture" "$repo"`. +Run `uv run --quiet --frozen --no-dev --project "$skill_dir/../../backend" adaptive-delivery-fixture readiness "$repo"`. Return its complete assessment to the invoking agent. Do not implement the request or transfer execution to another agent inside this capability. diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/require-clear-review.sh b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/require-clear-review.sh deleted file mode 100644 index 4f716aeb..00000000 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/require-clear-review.sh +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env bash -# Eval-only completion gate for canonical human and machine review artifacts. -set -euo pipefail -fail() { printf 'review proof: %s\n' "$*" >&2; exit 1; } -git_dir=$(git rev-parse --absolute-git-dir) -git_dir=$(cd "$git_dir" && pwd -P) -proof_file=$git_dir/fixture-state/high-risk-review-proof -mode=${1:-} -case "$mode" in - complete) record=${2:-} ;; - artifact|current) record=$(cat "$proof_file") ;; - *) fail 'expected complete ARTIFACT, artifact, or current' ;; -esac -[ -f "$record" ] && [ -r "$record" ] || fail "unreadable artifact: $record" -record=$(cd "$(dirname "$record")" && printf '%s/%s\n' "$(pwd -P)" "${record##*/}") -case "$record" in - "$git_dir"/darrow-review.*/result.tsv|"$git_dir"/darrow-review.*/verification.tsv) ;; - "$git_dir"/darrow-review.*/review.md|"$git_dir"/darrow-review.*/verification.md) ;; - *) fail "not a canonical fixture review artifact: $record" ;; -esac -# Only the installed dependency beneath this isolated fixture is eligible. -tools=$(find "$git_dir" -type f -path '*/backend/src/darrow_review/result.py') -result_tool= -while IFS= read -r candidate; do - [ -f "$candidate" ] || fail 'installed review-result tool is missing' - if [ -z "$result_tool" ]; then - result_tool=$candidate - else - # The local marketplace source and the host-installed cache are copies of - # the same dependency. Accept duplicates only when both tools are identical. - if ! cmp -s "$result_tool" "$candidate" || - ! cmp -s "${result_tool%/*}/scope.py" "${candidate%/*}/scope.py"; then - fail 'conflicting fixture review-tool copies' - fi - fi -done <<<"$tools" -backend=${result_tool%/src/darrow_review/result.py} -scope_tool=${result_tool%/*}/scope.py -case "$record" in - */review.md|*/verification.md) - report=$record - report_tool=${result_tool%/*}/report.py - [ -f "$report_tool" ] && [ -r "$report_tool" ] || fail 'installed review renderer is missing' - case "$report" in - */review.md) record=${report%/*}/result.tsv; render=render ;; - *) record=${report%/*}/verification.tsv; render='render-verification' ;; - esac - rendered=$(mktemp "$git_dir/review-proof.XXXXXX") - trap 'rm -f "$rendered"' EXIT - uv run --quiet --frozen --no-dev --project "$backend" review-report "$render" "$record" >"$rendered" - cmp -s "$rendered" "$report" || fail 'human report differs from its canonical result' - ;; -esac -field() { awk -F '\t' -v key="$1" '$1 == key { print $2 }' "$2"; } -format=$(field format "$record") -case "$format" in - darrow-review-result-v1) - [ "${record##*/}" = result.tsv ] || fail 'wrong comprehensive artifact name' - uv run --quiet --frozen --no-dev --project "$backend" review-result validate "$record" >/dev/null - [ "$(field verdict "$record")" = pass ] || fail 'review is not clear' - [ "$(field next_action "$record")" = 'return control to enclosing goal' ] || - fail 'review did not return control' - reviewed_target=$(field target "$record") - ;; - darrow-review-verification-v1) - [ "${record##*/}" = verification.tsv ] || fail 'wrong verification artifact name' - uv run --quiet --frozen --no-dev --project "$backend" review-result validate-verification "$record" >/dev/null - [ "$(field outcome "$record")" = clear ] || fail 'verification is not clear' - original_target=$(field original_target "$record") - original= - while IFS= read -r candidate; do - if [ "$(field target "$candidate")" = "$original_target" ]; then - [ -z "$original" ] || fail 'ambiguous original comprehensive result' - original=$candidate - fi - done < <(find "$git_dir" -type f -path '*/darrow-review.*/result.tsv') - [ -n "$original" ] || fail 'original comprehensive result is missing' - # The provider owns the complete original schema, including repair guidance. - uv run --quiet --frozen --no-dev --project "$backend" review-result validate-original "$original" "$record" >/dev/null || - fail 'verification changed the original finding set' - reviewed_target=$(field current_target "$record") - ;; - *) fail "unsupported format: $format" ;; -esac -if [ "$mode" != artifact ]; then - [ -f "$scope_tool" ] || fail 'installed review-scope tool is missing' - scope=$(uv run --quiet --frozen --no-dev --project "$backend" review-scope prepare --repo "$PWD" --base HEAD --target WORKTREE) - current_target=$(awk -F '\t' '$1 == "target" { print $2 }' <<<"$scope") - [ -n "$reviewed_target" ] && [ "$reviewed_target" = "$current_target" ] || - fail "stale review target: $reviewed_target; current: $current_target" -fi -if [ "$mode" = complete ]; then - mkdir -p "$git_dir/fixture-state" - printf '%s\n' "$record" >"$proof_file" - printf '%s\n' complete >"$git_dir/goal-complete" -fi -printf 'valid clear review: %s\n' "$record" diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/verification.test.sh b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/verification.test.sh deleted file mode 100644 index 5f40f3ca..00000000 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/verification.test.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env bash -set -eu -fixture_dir=$(cd "$(dirname "$0")" && pwd -P) -temp_parent=${TMPDIR:-/tmp} -case $temp_parent in - /) temp_template=/darrow-verification-fixture.XXXXXX ;; - */) temp_template=${temp_parent}darrow-verification-fixture.XXXXXX ;; - *) temp_template=$temp_parent/darrow-verification-fixture.XXXXXX ;; -esac -work_dir=$(mktemp -d "$temp_template") -work_dir=$(cd "$work_dir" && pwd -P) -trap 'rm -rf "$work_dir"' EXIT -repo=$work_dir/repo -mkdir -p "$repo/src" -git -C "$repo" init -q -git -C "$repo" config user.name Fixture -git -C "$repo" config user.email fixture@example.invalid -printf '%s\n' 'export const TIMEOUT_MS = 1000;' 'export const RETRY_COUNT = 3;' 'export const CACHE_SIZE = 5;' >"$repo/src/config.js" -git -C "$repo" add src/config.js -git -C "$repo" commit -qm 'chore: baseline' -printf '%s\n' '.agents/' '.claude/' >>"$repo/.git/info/exclude" -"$BASH" "$fixture_dir/install-verification.sh" "$repo" "$fixture_dir" -tool=$repo/.agents/bin/verification-fixture -state=$repo/.git/fixture-state -printf '%s\n' 'RETRY_COUNT = 0' >"$repo/.git/fixture-review-reject-pattern" -printf '%s\n' 'src/config.js' >"$repo/.git/fixture-review-event-path" -printf '%s\n' 'export const TIMEOUT_MS = 2500;' 'export const RETRY_COUNT = 0;' 'export const CACHE_SIZE = 0;' >"$repo/src/config.js" -if "$BASH" "$tool" "$repo" initial >"$work_dir/missing" 2>&1; then exit 1; fi -test ! -e "$state/verification-invocations" -cksum <"$repo/src/config.js" >"$state/check-target" -"$BASH" "$tool" "$repo" initial >"$work_dir/initial" -grep -Fx 'Conclusion: progress' "$work_dir/initial" >/dev/null -grep -F 'Q1, QA/cache, blocking' "$work_dir/initial" >/dev/null -grep -F 'Independent review outcome: blocking findings' "$work_dir/initial" >/dev/null -"$BASH" "$tool" "$repo" follow-up >"$work_dir/unchanged" -grep -Fx 'Conclusion: no-progress' "$work_dir/unchanged" >/dev/null -touch "$repo/.git/fixture-review-blocked" -"$BASH" "$tool" "$repo" follow-up >"$work_dir/unavailable-review" -grep -Fx 'Conclusion: blocked' "$work_dir/unavailable-review" >/dev/null -rm "$repo/.git/fixture-review-blocked" -touch "$repo/.git/fixture-review-inconclusive" -"$BASH" "$tool" "$repo" follow-up >"$work_dir/inconclusive-review" -grep -Fx 'Conclusion: blocked' "$work_dir/inconclusive-review" >/dev/null -rm "$repo/.git/fixture-review-inconclusive" -printf '%s\n' 'export const TIMEOUT_MS = 2500;' 'export const RETRY_COUNT = 3;' 'export const CACHE_SIZE = 5;' >"$repo/src/config.js" -if "$BASH" "$tool" "$repo" follow-up >"$work_dir/stale-check" 2>&1; then exit 1; fi -test "$(wc -l <"$state/verification-invocations" | tr -d ' ')" = 4 -cksum <"$repo/src/config.js" >"$state/check-target" -"$BASH" "$tool" "$repo" follow-up >"$work_dir/clear" -grep -Fx 'Conclusion: clear' "$work_dir/clear" >/dev/null -test "$(tail -n 1 "$state/verification-invocations" | cut -f2)" = "$(cksum <"$repo/src/config.js")" -if "$BASH" "$tool" "$repo" initial >"$work_dir/restart" 2>&1; then exit 1; fi - -# A mutation during the later assessment produces blocked current-content evidence. -expected_events=$(( $(wc -l <"$state/verification-events" | tr -d ' ') + 2 )) -"$BASH" "$tool" "$repo" follow-up >"$work_dir/stale-result" & -assessment_pid=$! -polls=0 -while [ "$(wc -l <"$state/verification-events" | tr -d ' ')" -lt "$expected_events" ]; do - kill -0 "$assessment_pid" 2>/dev/null || { wait "$assessment_pid"; exit 1; } - polls=$((polls + 1)) - test "$polls" -lt 100 || { kill "$assessment_pid"; wait "$assessment_pid"; exit 1; } - sleep 0.1 -done -printf '%s\n' 'export const TIMEOUT_MS = 2500;' 'export const RETRY_COUNT = 3;' 'export const CACHE_SIZE = 0;' >"$repo/src/config.js" -wait "$assessment_pid" -grep -Fx 'Conclusion: blocked' "$work_dir/stale-result" >/dev/null -printf '%s\n' 'verification fixture: combined results, current checks, closed follow-up and stale evidence passed' diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/verification/SKILL.fixture.md b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/verification/SKILL.fixture.md index 8fcb0ba9..00d14aee 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/verification/SKILL.fixture.md +++ b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/verification/SKILL.fixture.md @@ -16,7 +16,7 @@ execute that deterministic provider in this bounded assessment context, then collect the selected QA-like result. Run exactly: ```sh -bash "$skill_dir/../../bin/verification-fixture" "$repo" initial +uv run --quiet --frozen --no-dev --project "$skill_dir/../../backend" adaptive-delivery-fixture verification "$repo" initial ``` For follow-up, require the complete original/prior results, finding identities, @@ -24,7 +24,7 @@ provider provenance, blocking/advisory disposition, target and repair history, and direct-regression lineage. In a fresh assessment context run: ```sh -bash "$skill_dir/../../bin/verification-fixture" "$repo" follow-up +uv run --quiet --frozen --no-dev --project "$skill_dir/../../backend" adaptive-delivery-fixture verification "$repo" follow-up ``` Return the complete candidate-bound report, including both selected results, diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/verification/verification-fixture b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/verification/verification-fixture deleted file mode 100644 index 5ead511d..00000000 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/fixtures/verification/verification-fixture +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env bash -# Bounded synthetic assessment, not an owner or repair controller. -set -eu -repo=${1:?repository required} -mode=${2:?initial or follow-up required} -case $mode in initial) review_mode=comprehensive ;; follow-up) review_mode=verify ;; *) exit 2 ;; esac -repo=$(cd "$repo" && pwd -P) -bin_dir=$(cd "$(dirname "$0")" && pwd -P) -state=$repo/.git/fixture-state -subject=$repo/src/config.js -test -r "$subject" -target=$(cksum <"$subject") -test -r "$state/check-target" -test "$(cat "$state/check-target")" = "$target" || { printf '%s\n' 'blocked: successful current checks required'; exit 1; } -if [ "$mode" = initial ]; then - test ! -e "$state/verification-invocations" -else - test -s "$state/verification-invocations" -fi -printf 'assessment-start\t%s\t%s\n' "$mode" "$target" >>"$state/verification-events" -review_report=$(mktemp "$state/review-result.XXXXXX") -"$BASH" "$bin_dir/independent-review-fixture" "$repo" "$review_mode" independent-review-skill-contract-v1 >"$review_report" -printf 'review-returned\t%s\t%s\n' "$mode" "$(cksum <"$subject")" >>"$state/verification-events" -# Deliberately later than review: an owner must not act on the early result. -sleep 2 -qa_target=$(cksum <"$subject") -qa=blocking -if grep -Fx 'export const CACHE_SIZE = 5;' "$subject" >/dev/null; then qa=clear; fi -printf 'qa-returned\t%s\t%s\n' "$mode" "$qa_target" >>"$state/verification-events" -conclusion=progress -if [ "$target" != "$qa_target" ] || - grep -E '^Independent review outcome: (inconclusive|unavailable)$|^Fix verification: (blocked|inconclusive|unavailable)\.$' "$review_report" >/dev/null; then - conclusion=blocked -elif grep -E '^Independent review outcome: no blocking findings$|^Fix verification: clear\.$' "$review_report" >/dev/null && [ "$qa" = clear ]; then - conclusion=clear -elif grep -E '^Fix verification: no_progress\.$' "$review_report" >/dev/null; then - conclusion=no-progress -fi -printf '%s\t%s\t%s\n' "$mode" "$target" "$conclusion" >>"$state/verification-invocations" -printf 'Repository: %s\nCandidate: src/config.js cksum %s, HEAD to WORKTREE\nMode: %s\n' "$repo" "$target" "$mode" -printf '%s\n' 'Selected: independent code review and QA-like cache assessment' 'Required current check: bash check.sh passed for this candidate.' -printf '%s\n' 'Complete independent review result:' -cat "$review_report" -printf 'Complete QA-like result: %s; candidate cksum %s.\n' "$qa" "$qa_target" -if [ "$qa" = blocking ]; then - printf '%s\n' 'Q1, QA/cache, blocking: CACHE_SIZE must equal 5; observed:' - grep -F 'CACHE_SIZE' "$subject" -else - printf '%s\n' 'Q1, QA/cache: CACHE_SIZE = 5 observed; resolved in follow-up, no direct regression.' -fi -printf '%s\n' 'Acceptance C1: TIMEOUT_MS = 2500 supported by current check.' 'Acceptance C2: RETRY_COUNT = 3 assessed in the complete review result (R1).' 'Acceptance C3: CACHE_SIZE = 5 assessed by QA (Q1).' -printf 'Conclusion: %s\n' "$conclusion" -printf '%s\n' 'Finding provenance: R1 review/retries; Q1 QA/cache; both blocking when unresolved. No advisories or direct regressions observed.' -printf 'Target and assessment history: %s/verification-invocations\nComplete review result: %s\n' "$state" "$review_report" -printf 'assessment-returned\t%s\t%s\n' "$mode" "$(cksum <"$subject")" >>"$state/verification-events" diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/high-risk-routine.yaml b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/high-risk-routine.yaml index 79d73a62..174b8f9a 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/high-risk-routine.yaml +++ b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/high-risk-routine.yaml @@ -53,10 +53,11 @@ fixture: #!/usr/bin/env bash set -eu git_dir=$(git rev-parse --git-dir) - bash "$git_dir/fixture-bin/review-proof" complete "${1:?Supply the returned canonical review artifact path}" + uv run --quiet --frozen --no-dev --project "$git_dir/fixture-backend" adaptive-delivery-fixture proof complete "${1:?Supply the returned canonical review artifact path}" setup: | - mkdir -p .git/fixture-bin - cp "{{case_dir}}/fixtures/require-clear-review.sh" .git/fixture-bin/review-proof + mkdir -p .git/fixture-backend + cp "{{case_dir}}/../../../backend/pyproject.toml" "{{case_dir}}/../../../backend/uv.lock" .git/fixture-backend/ + cp -R "{{case_dir}}/../../../backend/src" .git/fixture-backend/ checks: - name: cookie behavior is changed and preserved run: >- @@ -76,9 +77,9 @@ checks: awk -F '\t' 'NR == 1 && $1 == "check" && $2 == 0 { ok=1 } END { exit ok ? 0 : 1 }' .git/fixture-state/high-risk-review-events - name: independent review artifact is canonical and clear - run: bash .git/fixture-bin/review-proof artifact + run: uv run --quiet --frozen --no-dev --project .git/fixture-backend adaptive-delivery-fixture proof artifact - name: independent review covers the exact final worktree - run: bash .git/fixture-bin/review-proof current + run: uv run --quiet --frozen --no-dev --project .git/fixture-backend adaptive-delivery-fixture proof current output_checks: - name: no lifecycle protocol is reported not_regex: "darrow-native-goal-report|Protocol ledger|route_verified" diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/intent-change-vs-migration.yaml b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/intent-change-vs-migration.yaml index 1b0f8074..27f23f8b 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/intent-change-vs-migration.yaml +++ b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/intent-change-vs-migration.yaml @@ -44,10 +44,10 @@ fixture: #!/usr/bin/env bash set -eu node config.test.js - review_target=$(.agents/bin/independent-review-fixture "$PWD" fingerprint) + review_target=$(uv run --quiet --frozen --no-dev --project .agents/backend adaptive-delivery-fixture review "$PWD" fingerprint) printf 'check\t%s\n' "$review_target" >>.git/fixture-state/independent-review-events setup: >- - bash "{{case_dir}}/fixtures/install-independent-review.sh" "$PWD" + uv run --quiet --frozen --no-dev --project "{{case_dir}}/../../../backend" adaptive-delivery-fixture install review "$PWD" "{{case_dir}}/fixtures" codex checks: - name: sequenced persisted-format transition works @@ -71,7 +71,7 @@ checks: - name: independent review covers the migrated final target run: >- test "$(cut -f1 .git/fixture-state/independent-review-invocations)" = - "$(.agents/bin/independent-review-fixture "$PWD" fingerprint)" + "$(uv run --quiet --frozen --no-dev --project .agents/backend adaptive-delivery-fixture review "$PWD" fingerprint)" - name: independent review starts after a matching current check run: >- awk -F '\t' '$1 == "check" { checked[$2]=1 } diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/post-launch-reassessment.yaml b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/post-launch-reassessment.yaml index 9fc790fe..6b70fd03 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/post-launch-reassessment.yaml +++ b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/post-launch-reassessment.yaml @@ -29,7 +29,7 @@ fixture: #!/usr/bin/env bash node --input-type=module -e "import {normalize} from './src/normalize.js'; import assert from 'node:assert/strict'; assert.equal(normalize(' a '), 'a');" setup: | - bash "{{case_dir}}/fixtures/install-implementation-readiness.sh" "$PWD" \ + uv run --quiet --frozen --no-dev --project "{{case_dir}}/../../../backend" adaptive-delivery-fixture install readiness "$PWD" \ "{{case_dir}}/fixtures" codex cp "{{case_dir}}/fixtures/owner-readiness.fixture.md" .agents/skills/assess-implementation-readiness/SKILL.md bin: diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/python-migration.md b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/python-migration.md new file mode 100644 index 00000000..0208b220 --- /dev/null +++ b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/python-migration.md @@ -0,0 +1,52 @@ +# Python migration validation + +This migration preserves the skill's activation and orchestration contract. +It changes deterministic preflight, route, and fixture mechanics and the frozen +commands that invoke them. The original Bash regression suites were run before +the conversion; their required scenarios now run in the Python package suite. + +| Representative request | Required behavior | Existing evidence seam | +| ---------------------------------------------------------------------- | ------------------------------------------------- | ----------------------------------------------------------------------- | +| Use adaptive-delivery to implement this bounded request | Prepare and launch one native owner | `bounded-native-goal.yaml` | +| An invoked task recipe delegates its authorized request | Preserve delegated authority | `authority-task-recipe-parent.yaml` | +| Use adaptive-delivery on a specification with a missing product choice | Return the full non-ready assessment, launch none | `readiness-nonready-stops.yaml`, `claude-readiness-nonready-stops.yaml` | +| Implement this ordinary engineering change | Do not activate orchestration | `ordinary-engineering-nonactivation.yaml` | +| A review returns before another selected assessment | Wait for combined evidence before repairing | `verification-combined-repair.yaml` | + +The request matrix reuses the existing judgment oracles. Deterministic baseline +tests cover exact record formats and ordering, route policy precedence and +refusal, repository binding, readiness reports, closed review history, and +assessment timing. Python tests add generated JSON and POSIX checksum oracles, +native path and subprocess boundaries, and separately gated line/branch coverage. + +The six Bash test drivers are retired. Their retained assertions map to these +files under `backend/tests`: + +| Retired driver | Python coverage | +| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| `adaptive-delivery-preflight.test.sh` | `test_preflight.py`, `test_route_matrix.py`: records, policy/explicit parity, all efforts, Git isolation, refusals | +| `codex-launch.test.sh`, `claude-launch.test.sh` | `test_launch_contract.py`, `test_preflight.py`: native controls, manifest identities, resolver contracts | +| `implementation-readiness.test.sh` | `test_fixtures.py`, `test_installed_fixtures.py`: exact templates, host isolation, reports, passive prerender, iterative installed execution | +| `independent-review.test.sh` | `test_fixtures.py`, `test_installed_fixtures.py`: fingerprints, closed outcomes/history, installed event records, non-subject mutation during review | +| `verification.test.sh` | `test_fixtures.py`, `test_installed_fixtures.py`: current checks, combined outcomes, refused resets, mutation between review and QA | + +Installed-provider tests invoke frozen UV console entrypoints with argument +vectors. Race tests wait for recorded assessment events before changing the +candidate, then check the returned result and target history. The removed +conflicting-Bash-interpreter check applied only to the retired test runner. + +The package follows the contained UV blueprint with no runtime dependencies. +Its deliberate migration difference is standard-library JSON syntax diagnostics +(line/column instead of the removed AWK parser's byte wording). Semantic route +validation, output records, and exit statuses retain their contracts. Fixture +installation copies a runtime-only package beside the host's fixture skills; +no compatibility script or discoverable fixture skill ships in the source tree. + +Run `bun run check:python` for repository Python quality. Run +`uv run --quiet --frozen --no-dev --project backend python backend/tests/fresh_install.py` +from the plugin root for fresh copied-artifact validation. CI runs native Linux, +macOS, and Windows package/fresh-artifact checks, including the Python regression +suite. No separate Bash regression step remains. +Native live trials use `--owner-evaluation passive`; dry validation proves fixture +preparation only. Report actual host trials separately from deterministic and +installation evidence; installing the helper does not prove host acceptance. diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/readiness-artifact-selected.yaml b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/readiness-artifact-selected.yaml index d2c7290d..f6d9503d 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/readiness-artifact-selected.yaml +++ b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/readiness-artifact-selected.yaml @@ -27,7 +27,7 @@ fixture: printf '%s\n' '# Notes' 'Artifact request completed.' >.git/expected-notes cmp -s .git/expected-notes NOTES.md setup: >- - bash "{{case_dir}}/fixtures/install-implementation-readiness.sh" "$PWD" + uv run --quiet --frozen --no-dev --project "{{case_dir}}/../../../backend" adaptive-delivery-fixture install readiness "$PWD" "{{case_dir}}/fixtures" codex checks: - name: artifact request reaches its exact outcome diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/readiness-iterative-resolution.yaml b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/readiness-iterative-resolution.yaml index 2a199b75..bd374938 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/readiness-iterative-resolution.yaml +++ b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/readiness-iterative-resolution.yaml @@ -29,7 +29,7 @@ fixture: set -eu grep -Fx 'mode = safe' policy.txt >/dev/null setup: >- - bash "{{case_dir}}/fixtures/install-implementation-readiness.sh" "$PWD" + uv run --quiet --frozen --no-dev --project "{{case_dir}}/../../../backend" adaptive-delivery-fixture install readiness "$PWD" "{{case_dir}}/fixtures" codex checks: - name: resolved request is implemented diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/readiness-prior-assessed-omitted.yaml b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/readiness-prior-assessed-omitted.yaml index 62f22304..547fb8d2 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/readiness-prior-assessed-omitted.yaml +++ b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/readiness-prior-assessed-omitted.yaml @@ -30,7 +30,7 @@ fixture: printf '%s\n' '# Notes' 'Previously assessed.' >.git/expected-notes cmp -s .git/expected-notes NOTES.md setup: >- - bash "{{case_dir}}/fixtures/install-implementation-readiness.sh" "$PWD" + uv run --quiet --frozen --no-dev --project "{{case_dir}}/../../../backend" adaptive-delivery-fixture install readiness "$PWD" "{{case_dir}}/fixtures" codex checks: - name: previously assessed plan is implemented diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/readiness-scope-changed.yaml b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/readiness-scope-changed.yaml index 79462fd0..c92037f8 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/readiness-scope-changed.yaml +++ b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/readiness-scope-changed.yaml @@ -31,7 +31,7 @@ fixture: printf '%s\n' '# Notes' 'existing = preserved' 'First revised line.' 'Second revised line.' >.git/expected-notes cmp -s .git/expected-notes NOTES.md setup: >- - bash "{{case_dir}}/fixtures/install-implementation-readiness.sh" "$PWD" + uv run --quiet --frozen --no-dev --project "{{case_dir}}/../../../backend" adaptive-delivery-fixture install readiness "$PWD" "{{case_dir}}/fixtures" codex checks: - name: changed-scope plan is implemented diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/review-high-selected-claude.yaml b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/review-high-selected-claude.yaml index 171ff018..43e5aa09 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/review-high-selected-claude.yaml +++ b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/review-high-selected-claude.yaml @@ -43,10 +43,10 @@ fixture: #!/usr/bin/env bash set -eu node auth-config.test.js - review_target=$(.claude/bin/independent-review-fixture "$PWD" fingerprint) + review_target=$(uv run --quiet --frozen --no-dev --project .claude/backend adaptive-delivery-fixture review "$PWD" fingerprint) printf 'check\t%s\n' "$review_target" >>.git/fixture-state/independent-review-events setup: >- - bash "{{case_dir}}/fixtures/install-independent-review.sh" "$PWD" + uv run --quiet --frozen --no-dev --project "{{case_dir}}/../../../backend" adaptive-delivery-fixture install review "$PWD" "{{case_dir}}/fixtures" claude checks: - name: cookie behavior is changed and preserved @@ -66,12 +66,12 @@ checks: run: >- grep -F 'independent-review-skill-contract-v1' .claude/skills/independent-code-review/SKILL.md >/dev/null && - ! grep -F 'independent-review-skill-contract-v1' - .claude/bin/independent-review-fixture >/dev/null + ! grep -RF 'independent-review-skill-contract-v1' + .claude/backend >/dev/null - name: independent review clears the final changed target run: >- test "$(cut -f1 .git/fixture-state/independent-review-invocations)" = - "$(.claude/bin/independent-review-fixture "$PWD" fingerprint)" && + "$(uv run --quiet --frozen --no-dev --project .claude/backend adaptive-delivery-fixture review "$PWD" fingerprint)" && awk -F '\t' '$1 == "check" { checked[$2]=1 } $1 == "review_start" && checked[$2] { ok=1 } END { exit ok ? 0 : 1 }' .git/fixture-state/independent-review-events && diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/review-repair-rereview.yaml b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/review-repair-rereview.yaml index 0207462e..32cd59bc 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/review-repair-rereview.yaml +++ b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/review-repair-rereview.yaml @@ -49,7 +49,7 @@ fixture: export const TIMEOUT_MS = 2500; export const RETRY_COUNT = 0; setup: >- - bash "{{case_dir}}/fixtures/install-independent-review.sh" "$PWD" + uv run --quiet --frozen --no-dev --project "{{case_dir}}/../../../backend" adaptive-delivery-fixture install review "$PWD" "{{case_dir}}/fixtures" codex && printf '%s\n' 'RETRY_COUNT = 0' > .git/fixture-review-reject-pattern && printf '%s\n' 'src/config.js' > .git/fixture-review-event-path && printf '%s\n' '2' > @@ -103,7 +103,7 @@ checks: - name: final worktree still matches the clear reviewed target run: >- test "$(tail -n 1 .git/fixture-state/independent-review-invocations | cut -f1)" = - "$(bash .agents/bin/independent-review-fixture "$PWD" fingerprint)" + "$(uv run --quiet --frozen --no-dev --project .agents/backend adaptive-delivery-fixture review "$PWD" fingerprint)" - name: repair changes the reviewed target fingerprint run: >- cut -f1 .git/fixture-state/independent-review-invocations | sort -u >.git/fixture-state/review-targets && diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/review-routine-omitted.yaml b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/review-routine-omitted.yaml index a0311792..498d6002 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/review-routine-omitted.yaml +++ b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/review-routine-omitted.yaml @@ -23,7 +23,7 @@ fixture: printf '%s\n' '# Notes' 'Retries are bounded.' >.git/expected-notes cmp -s .git/expected-notes NOTES.md setup: >- - bash "{{case_dir}}/fixtures/install-independent-review.sh" "$PWD" + uv run --quiet --frozen --no-dev --project "{{case_dir}}/../../../backend" adaptive-delivery-fixture install review "$PWD" "{{case_dir}}/fixtures" codex checks: - name: routine change reaches its exact outcome diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/verification-combined-repair.yaml b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/verification-combined-repair.yaml index 398e0084..9a54cde7 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/verification-combined-repair.yaml +++ b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/verification-combined-repair.yaml @@ -32,7 +32,7 @@ fixture: export const RETRY_COUNT = 0; export const CACHE_SIZE = 0; setup: | - bash "{{case_dir}}/fixtures/install-verification.sh" "$PWD" "{{case_dir}}/fixtures" + uv run --quiet --frozen --no-dev --project "{{case_dir}}/../../../backend" adaptive-delivery-fixture install verification "$PWD" "{{case_dir}}/fixtures" both printf '%s\n' 'RETRY_COUNT = 0' >.git/fixture-review-reject-pattern printf '%s\n' 'src/config.js' >.git/fixture-review-event-path checks: diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/verification-existing-review.yaml b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/verification-existing-review.yaml index 45d17d1c..6e597dcb 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/verification-existing-review.yaml +++ b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/verification-existing-review.yaml @@ -39,8 +39,9 @@ fixture: export const TIMEOUT_MS = 2500; export const RETRY_COUNT = 0; setup: | - mkdir -p .git/fixture-bin - cp "{{case_dir}}/fixtures/require-clear-review.sh" .git/fixture-bin/review-proof + mkdir -p .git/fixture-backend + cp "{{case_dir}}/../../../backend/pyproject.toml" "{{case_dir}}/../../../backend/uv.lock" .git/fixture-backend/ + cp -R "{{case_dir}}/../../../backend/src" .git/fixture-backend/ checks: - name: current candidate satisfies both originating criteria run: | @@ -58,8 +59,8 @@ checks: find .git -maxdepth 2 -name result.tsv -path '*/darrow-review.*/*' -exec awk -F '\t' '$1 == "target" || $1 == "finding" { print FILENAME ":" $0 }' {} \; awk -F '\t' '$1 == "original_target" || $1 == "original_finding" { print FILENAME ":" $0 }' "$record" grep -F 'outcome clear' "$record" >/dev/null - bash .git/fixture-bin/review-proof complete "$record" - bash .git/fixture-bin/review-proof current + uv run --quiet --frozen --no-dev --project .git/fixture-backend adaptive-delivery-fixture proof complete "$record" + uv run --quiet --frozen --no-dev --project .git/fixture-backend adaptive-delivery-fixture proof current semantic_output_checks: - name: same owner completes after actual provider repair verification proposition: >- diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/verification-required-unavailable.yaml b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/verification-required-unavailable.yaml index 99f09e4b..6d0e0dde 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/verification-required-unavailable.yaml +++ b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/verification-required-unavailable.yaml @@ -17,7 +17,7 @@ fixture: #!/usr/bin/env bash grep -Fx 'export const requireMfa = true;' auth-policy.js setup: | - bash "{{case_dir}}/fixtures/install-independent-review.sh" "$PWD" "{{case_dir}}/fixtures" both + uv run --quiet --frozen --no-dev --project "{{case_dir}}/../../../backend" adaptive-delivery-fixture install review "$PWD" "{{case_dir}}/fixtures" both printf '%s\n' '.agents/' '.claude/' >>.git/info/exclude checks: - name: unavailable verification leaves the product unchanged diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/references/claude-launch.md b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/references/claude-launch.md index 3bc1edb5..38c05272 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/references/claude-launch.md +++ b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/references/claude-launch.md @@ -7,8 +7,11 @@ and contract compilation are complete. Run the bundled resolver once with the selected route: +On native Windows, enter this command on one PowerShell line without the Bash +continuation backslashes. + ```sh -/bin/bash /claude-agent-route \ +uv run --quiet --frozen --no-dev --project "" claude-agent-route \ --provider anthropic --model \ --effort ``` @@ -22,12 +25,23 @@ conflicting host environment override is active. A nonzero result is Invoke the resolved Agent exactly once with: -- `run_in_background` set to `false`; +- an explicit `run_in_background: false` field, even when the host defaults to foreground execution; - no per-call model override; - no worktree isolation or resume option on the initial call; and - the complete task whose first line is exactly `- phase: adaptive-delivery-owner`. +Use this input shape, replacing both placeholders with the resolved value and +complete compiled contract: + +```json +{ + "subagent_type": "", + "run_in_background": false, + "prompt": "- phase: adaptive-delivery-owner\n" +} +``` + The resolver has rejected higher-priority environment overrides, and the Agent call supplies no per-call model override. The accepted call therefore uses the scoped agent's model and effort frontmatter and establishes the sole owner. diff --git a/python-packages.txt b/python-packages.txt index c1f4c1b5..566207ac 100644 --- a/python-packages.txt +++ b/python-packages.txt @@ -8,3 +8,4 @@ plugins/capability/darrow-tickets-github/backend plugins/foundation/darrow-decisions/backend plugins/foundation/darrow-information-architecture/backend plugins/capability/darrow-review/backend +plugins/orchestration/darrow-adaptive-delivery/backend