Files
ironclaw/scripts/ci/test_reborn_pr_test_plan.py
Coffee 2042380731 chore: remove IronLoop small-fix role instructions (#7415)
* chore: remove IronLoop small-fix role instructions

* ci: classify IronLoop configuration changes
2026-08-10 08:52:31 +00:00

1971 lines
89 KiB
Python

#!/usr/bin/env python3
"""Contract tests for affected-area Reborn PR test planning."""
from __future__ import annotations
import importlib.util
import re
import sys
import tomllib
import unittest
from pathlib import Path
from unittest import mock
ROOT = Path(__file__).resolve().parents[2]
MODULE_PATH = ROOT / "scripts/ci/reborn_pr_test_plan.py"
SPEC = importlib.util.spec_from_file_location("reborn_pr_test_plan", MODULE_PATH)
assert SPEC is not None and SPEC.loader is not None
planner = importlib.util.module_from_spec(SPEC)
sys.modules[SPEC.name] = planner
SPEC.loader.exec_module(planner)
sys.path.insert(0, str(ROOT / "scripts/ci/lib"))
from crate_tree import crate_directories, owning_crate_directory # noqa: E402
# A literal `include_str!`/`include_bytes!` target. `concat!` forms are not
# matched and do not need to be: this resolves *whether* a crate reaches into
# an asset tree, and every tree in the table has either literal sites or a
# build-script walk that `_build_script_tree_embedders` derives instead
# (`skills/` is embedded via `OUT_DIR`, so its include literal names no tree).
INCLUDE_LITERAL = re.compile(r"include_(?:str|bytes)!\s*\(\s*\"([^\"]+)\"")
DEPENDENCY_TABLES = ("dependencies", "dev-dependencies", "build-dependencies")
def _workspace_crate_directories() -> dict[str, Path]:
"""Package name -> crate directory, from the repo's own crate inventory.
`crate_tree` rather than a `crates/**/Cargo.toml` glob so the workspace-
excluded `wasm-src/` guests stay out: they declare their own `[workspace]`,
no lane of this workspace compiles them, and counting them would make a
guest's include of its own sibling file look like a cross-tree reach-in.
"""
directories: dict[str, Path] = {}
for relative in crate_directories(ROOT):
directory = ROOT / relative
manifest = tomllib.loads(
(directory / "Cargo.toml").read_text(encoding="utf-8")
)
name = manifest.get("package", {}).get("name")
if name:
directories[name] = directory
return directories
def _crates_embedding(prefix: str, crate_dirs: dict[str, Path]) -> set[str]:
"""Crates with an include of a *table-routed* file under `prefix`.
Table-routed means "owned by no crate": the planner resolves package
directories first, so `crates/extensions/packages/telegram/manifest.toml`
is its own crate's file and never reaches `EMBEDDED_ASSET_OWNERS`.
"""
embedders: set[str] = set()
for name, directory in crate_dirs.items():
for source in directory.rglob("*.rs"):
if "target" in source.parts or name in embedders:
continue
text = source.read_text(encoding="utf-8", errors="replace")
for literal in INCLUDE_LITERAL.findall(text):
try:
target = (
(source.parent / literal).resolve().relative_to(ROOT).as_posix()
)
except ValueError:
continue
if target.startswith(prefix) and (
owning_crate_directory(target, ROOT) is None
):
embedders.add(name)
break
return embedders
def _build_script_tree_embedders(prefix: str, crate_dirs: dict[str, Path]) -> set[str]:
"""Crates whose build script consumes the repo-root tree at `prefix`.
The bundled product skills are embedded through a build script rather than
an `include_*!` literal: `build.rs` walks the tree at compile time, prints
`cargo:rerun-if-changed` for every file in it, and writes bundle JSON that
the crate then `include_str!`s from `OUT_DIR` — so the include literal
names `OUT_DIR`, not the tree, and `_crates_embedding` cannot see the
pairing. The build script's own source is the evidence instead: it must
both name the tree it walks (a `join("<tree>")` on the repo root) and
declare the rebuild dependency (`cargo:rerun-if-changed`), which is the
cargo-canonical marker for "a build input outside this crate".
"""
stem = prefix.rstrip("/")
embedders: set[str] = set()
for name, directory in crate_dirs.items():
build_script = directory / "build.rs"
if not build_script.is_file():
continue
text = build_script.read_text(encoding="utf-8", errors="replace")
# Anchored to the repo-root join so a crate joining its own local
# `skills/` subdirectory cannot false-positive as the tree's embedder.
if f'repo_root.join("{stem}")' in text and "cargo:rerun-if-changed" in text:
embedders.add(name)
return embedders
def _depends_on(package: str, target: str, crate_dirs: dict[str, Path]) -> bool:
"""True when `package` reaches `target` through workspace dependencies."""
seen = {package}
pending = [package]
while pending:
directory = crate_dirs.get(pending.pop())
if directory is None:
continue
manifest = tomllib.loads(
(directory / "Cargo.toml").read_text(encoding="utf-8")
)
tables = [manifest.get(table, {}) for table in DEPENDENCY_TABLES]
for platform in manifest.get("target", {}).values():
tables.extend(platform.get(table, {}) for table in DEPENDENCY_TABLES)
for table in tables:
for key, value in table.items():
name = value.get("package", key) if isinstance(value, dict) else key
if name == target:
return True
if name in crate_dirs and name not in seen:
seen.add(name)
pending.append(name)
return False
def metadata() -> dict:
root = str(ROOT / "Cargo.toml")
alpha = str(ROOT / "crates/alpha/Cargo.toml")
beta = str(ROOT / "crates/beta/Cargo.toml")
gamma = str(ROOT / "crates/gamma/Cargo.toml")
return {
"workspace_members": ["root", "alpha", "beta", "gamma"],
"packages": [
{"id": "root", "name": "ironclaw_integration_tests", "manifest_path": root},
{"id": "alpha", "name": "alpha", "manifest_path": alpha},
{"id": "beta", "name": "beta", "manifest_path": beta},
{"id": "gamma", "name": "gamma", "manifest_path": gamma},
],
"resolve": {
"nodes": [
{"id": "root", "deps": [{"pkg": "gamma"}]},
{"id": "alpha", "deps": []},
{"id": "beta", "deps": [{"pkg": "alpha"}]},
{"id": "gamma", "deps": [{"pkg": "beta"}]},
]
},
}
def real_owner_metadata() -> dict:
"""A workspace named after the crates `EMBEDDED_ASSET_OWNERS` routes to.
The rest of this file uses `metadata()`'s `alpha`/`beta`/`gamma`, which
cannot carry the real table: the planner rejects a changed package outside
the canonical set, so the real owners have to exist here to be routed to
at all. Manifest paths are the real ones, so `_workspace_packages` derives
the same package directories it does in CI — which is what makes
`crates/extensions/packages/slack/` resolve to its own crate rather than
to the asset table.
`ironclaw_extension_host` depends on `ironclaw_extension_support` (an
optional dependency plus a dev-dependency, both in its real manifest),
which is why routing a package asset to the support crate also schedules
the host that embeds three of those manifests itself.
"""
def package(name: str, manifest: str, deps: tuple[str, ...] = ()) -> dict:
return {
"id": name,
"name": name,
"manifest_path": str(ROOT / manifest),
"deps": deps,
}
packages = [
package("ironclaw_integration_tests", "Cargo.toml"),
package(
"ironclaw_extension_support",
"crates/extensions/ironclaw_extension_support/Cargo.toml",
),
package(
"ironclaw_extension_host",
"crates/extensions/ironclaw_extension_host/Cargo.toml",
("ironclaw_extension_support",),
),
package(
"ironclaw_slack_extension",
"crates/extensions/packages/slack/Cargo.toml",
),
]
return {
"workspace_members": [entry["id"] for entry in packages],
"packages": [
{key: value for key, value in entry.items() if key != "deps"}
for entry in packages
],
"resolve": {
"nodes": [
{"id": entry["id"], "deps": [{"pkg": dep} for dep in entry["deps"]]}
for entry in packages
]
},
}
class RebornPrTestPlanTests(unittest.TestCase):
def setUp(self) -> None:
self.original_bucket_packages = planner._bucket_packages
planner._bucket_packages = lambda packages: (
[{"name": "selected", "packages": packages}] if packages else []
)
self.canonical = ["alpha", "beta", "gamma"]
def tearDown(self) -> None:
planner._bucket_packages = self.original_bucket_packages
def plan(
self,
event: str,
paths: list[str],
*,
lockfile_manifest_owned: bool = False,
) -> dict:
return planner.build_plan(
event=event,
changed_paths=paths,
metadata=metadata(),
canonical_packages=self.canonical,
lockfile_manifest_owned=lockfile_manifest_owned,
)
def plan_real_owners(self, paths: list[str]) -> dict:
"""Plan a pull request through the real `EMBEDDED_ASSET_OWNERS`."""
metadata = real_owner_metadata()
return planner.build_plan(
event="pull_request",
changed_paths=paths,
metadata=metadata,
canonical_packages=[
package["name"]
for package in metadata["packages"]
if package["name"] != "ironclaw_integration_tests"
],
)
def test_merge_queue_is_always_exhaustive(self) -> None:
plan = self.plan("merge_group", ["crates/alpha/src/lib.rs"])
self.assertEqual(plan["mode"], "full")
self.assertEqual(plan["coverage_mode"], "full")
self.assertEqual(plan["root_partitions"], [0, 1, 2, 3])
self.assertEqual(plan["integration_lanes"], [0, 1, 2, 3, "groups"])
def test_changed_package_includes_transitive_reverse_dependents(self) -> None:
plan = self.plan("pull_request", ["crates/alpha/src/lib.rs"])
self.assertEqual(plan["mode"], "selected")
self.assertEqual(plan["changed_packages"], ["alpha"])
self.assertEqual(plan["affected_packages"], ["alpha", "beta", "gamma"])
self.assertEqual(
plan["crate_buckets"],
[{"name": "selected", "packages": ["alpha", "beta", "gamma"]}],
)
self.assertTrue(plan["run_qa_replay"])
self.assertEqual(plan["coverage_mode"], "none")
def test_package_owned_test_change_does_not_run_reverse_dependents(self) -> None:
plan = self.plan("pull_request", ["crates/alpha/tests/contract.rs"])
self.assertEqual(plan["mode"], "selected")
self.assertEqual(plan["changed_packages"], ["alpha"])
self.assertEqual(plan["affected_packages"], ["alpha"])
self.assertEqual(
plan["crate_buckets"],
[
{
"name": "selected",
"packages": ["alpha"],
"exact_targets": [
{"package": "alpha", "kind": "test", "name": "contract"}
],
}
],
)
def test_nested_package_test_change_keeps_all_owning_package_targets(self) -> None:
plan = self.plan("pull_request", ["crates/alpha/tests/support/mod.rs"])
self.assertEqual(plan["affected_packages"], ["alpha"])
self.assertNotIn("exact_targets", plan["crate_buckets"][0])
def test_high_fanout_package_keeps_consumers_in_bounded_jobs(self) -> None:
wide = metadata()
for index in range(5):
package_id = f"consumer-{index}"
package_name = f"consumer_{index}"
wide["workspace_members"].append(package_id)
wide["packages"].append(
{
"id": package_id,
"name": package_name,
"manifest_path": str(
ROOT / f"crates/{package_name}/Cargo.toml"
),
}
)
wide["resolve"]["nodes"].append(
{"id": package_id, "deps": [{"pkg": "alpha"}]}
)
canonical = ["alpha"] + [f"consumer_{index}" for index in range(5)]
planner._bucket_packages = lambda packages: [
{"name": package, "packages": [package]} for package in packages
]
plan = planner.build_plan(
event="pull_request",
changed_paths=["crates/alpha/src/lib.rs"],
metadata=wide,
canonical_packages=canonical,
)
self.assertEqual(plan["affected_packages"], canonical)
self.assertEqual(len(plan["crate_buckets"]), 3)
self.assertEqual(
sorted(
package
for bucket in plan["crate_buckets"]
for package in bucket["packages"]
),
canonical,
)
self.assertIn("without omitting packages", plan["reasons"][-1])
def test_bounded_jobs_do_not_split_canonical_buckets(self) -> None:
source = [
{"name": "reborn-core", "packages": ["ironclaw", "runner"]},
{"name": "composition-core", "packages": ["composition"]},
{"name": "webui-ingress", "packages": ["attachments", "webui"]},
{"name": "memory-skills", "packages": ["memory", "skills"]},
]
bounded = planner._bound_pr_buckets(source, max_buckets=3)
self.assertEqual(len(bounded), 3)
for bucket in source:
package_set = set(bucket["packages"])
self.assertTrue(
any(package_set <= set(candidate["packages"]) for candidate in bounded)
)
def test_frontend_change_is_owned_by_code_style_with_baseline_qa_replay(self) -> None:
# The frontend prefix is resolved through the crate inventory
# (scripts/ci/lib/crate_tree.py), not a hardcoded literal — deriving
# the test input from the same resolver keeps this correct regardless
# of whether ironclaw_webui sits flat or has already moved into a
# family directory (PROPOSAL §5). Sibling tests below pin the
# resolution mechanism itself (nested + fail-closed) against a mocked
# crate_directory rather than the live tree.
frontend_prefix = planner._webui_frontend_prefix()
plan = self.plan("pull_request", [f"{frontend_prefix}src/app.tsx"])
self.assertEqual(plan["mode"], "none")
self.assertNotIn("run_frontend", plan)
self.assertTrue(plan["run_qa_replay"])
self.assertEqual(plan["crate_buckets"], [])
self.assertEqual(plan["integration_lanes"], [])
def test_frontend_prefix_resolves_through_crate_inventory_when_nested(self) -> None:
"""WS10: a family-moved ironclaw_webui still routes to Code Style.
Mocks `crate_directory` (rather than relying on the live repo's
current crate layout, which the target-architecture restructure is
actively changing) to pin that the frontend-prefix resolution follows
the crate wherever it lives.
"""
planner._webui_frontend_prefix.cache_clear()
try:
with (
mock.patch.object(planner, "_sandbox_docker_prefixes", return_value=()),
mock.patch.object(
planner,
"crate_directory",
return_value="crates/substrates/ironclaw_webui",
) as resolver,
):
plan = self.plan(
"pull_request",
["crates/substrates/ironclaw_webui/frontend/src/app.tsx"],
)
resolver.assert_called_once_with("ironclaw_webui", planner.ROOT)
self.assertEqual(plan["mode"], "none")
self.assertEqual(plan["crate_buckets"], [])
self.assertEqual(plan["integration_lanes"], [])
finally:
planner._webui_frontend_prefix.cache_clear()
def test_frontend_prefix_resolution_failure_fails_closed(self) -> None:
"""An unresolvable ironclaw_webui crate must raise, never fall back
to the literal — a silent fallback is exactly the WS10 failure mode
(a moved crate makes the prefix match nothing and the planner reports
"no Reborn test surface changed" for a real WebUI diff)."""
planner._webui_frontend_prefix.cache_clear()
try:
with (
mock.patch.object(planner, "_sandbox_docker_prefixes", return_value=()),
mock.patch.object(
planner,
"crate_directory",
side_effect=planner.CrateTreeError("boom"),
),
):
with self.assertRaisesRegex(
RuntimeError, "cannot resolve the ironclaw_webui crate"
):
self.plan(
"pull_request",
["crates/ironclaw_webui/frontend/src/app.tsx"],
)
finally:
planner._webui_frontend_prefix.cache_clear()
def test_nested_crate_markdown_remains_package_owned(self) -> None:
plan = self.plan("pull_request", ["crates/alpha/README.md"])
self.assertEqual(plan["changed_packages"], ["alpha"])
self.assertNotEqual(plan["mode"], "none")
def test_recorded_fixture_change_runs_only_qa_replay(self) -> None:
plan = self.plan(
"pull_request",
["tests/fixtures/llm_traces/reborn_qa/example.json"],
)
self.assertEqual(plan["mode"], "selected")
self.assertTrue(plan["run_qa_replay"])
self.assertEqual(plan["crate_buckets"], [])
def test_reborn_e2e_scenario_change_is_owned_by_e2e_workflow(self) -> None:
plan = self.plan(
"pull_request",
["tests/e2e/scenarios/test_reborn_webui_v2_legacy_extensions.py"],
)
# E2E scenarios live in the dedicated reborn-e2e.yml workflow, not
# the crate-bucket / root-partition / integration-lane plan emitted
# here. A scenario-only change must not fail closed as an unmapped
# path, and must not schedule crate buckets or integration lanes.
self.assertEqual(plan["mode"], "none")
self.assertEqual(plan["crate_buckets"], [])
self.assertEqual(plan["integration_lanes"], [])
self.assertEqual(plan["root_partitions"], [])
self.assertTrue(plan["run_qa_replay"])
self.assertTrue(
any("Reborn E2E workflow owns" in reason for reason in plan["reasons"]),
plan["reasons"],
)
def test_shared_e2e_harness_is_owned_by_e2e_workflow(self) -> None:
plan = self.plan("pull_request", ["tests/e2e/reborn_webui_harness.py"])
self.assertEqual(plan["mode"], "none")
self.assertEqual(plan["integration_lanes"], [])
def test_reborn_e2e_and_crate_changes_keep_both_owners(self) -> None:
plan = self.plan(
"pull_request",
[
"tests/e2e/scenarios/test_reborn_webui_v2_legacy_extensions.py",
"crates/alpha/src/lib.rs",
],
)
# The E2E scenario path is skipped (owned by reborn-e2e.yml) while
# the changed crate is still scheduled in the affected crate buckets.
self.assertEqual(plan["mode"], "selected")
self.assertEqual(plan["changed_packages"], ["alpha"])
self.assertTrue(plan["crate_buckets"])
self.assertTrue(plan["run_qa_replay"])
self.assertTrue(
any("Reborn E2E workflow owns" in reason for reason in plan["reasons"]),
plan["reasons"],
)
def test_live_qa_harness_changes_run_only_qa_replay(self) -> None:
for path in (
"scripts/live-canary/README.md",
"scripts/live-canary/notify_slack.py",
"scripts/reborn_webui_v2_live_qa/run_live_qa.py",
# WS10 (2026-08-04): the QA surface-inventory auditor and its
# self-test are the same class of offline QA tooling, and were
# raising `unmapped test or CI path` until they were classified.
"scripts/reborn_qa_matrix/audit_surface_inventory.py",
"scripts/reborn_qa_matrix/test_audit_surface_inventory.py",
# 2026-08-05: `scripts/live_canary/` (UNDERSCORE) is a second real
# directory beside `scripts/live-canary/` (hyphen) above, and
# classifying the hyphen tree never classified this one. It is the
# canary's importable Python package; a crate rename reaches it
# through a `RUST_LOG` string, and the fail-closed arm rejected the
# whole rename PR on that one line.
"scripts/live_canary/common.py",
"scripts/live_canary/auth_runtime.py",
# 2026-08-06: the live Telegram release smoke harness — run by
# hand, referenced by no workflow. PR #7264's stale-command fix in
# its README hit the fail-closed arm.
"scripts/telegram_smoke/README.md",
"scripts/telegram_smoke/run_smoke.py",
):
with self.subTest(path=path):
plan = self.plan("pull_request", [path])
self.assertEqual(plan["mode"], "selected")
self.assertTrue(plan["run_qa_replay"])
self.assertEqual(plan["crate_buckets"], [])
self.assertEqual(plan["root_partitions"], [])
self.assertEqual(plan["integration_lanes"], [])
def test_unrelated_workflow_change_runs_only_baseline_qa_replay(self) -> None:
plan = self.plan("pull_request", [".github/workflows/code_style.yml"])
self.assertEqual(plan["mode"], "none")
self.assertTrue(plan["run_qa_replay"])
def test_reborn_workflow_change_is_owned_by_static_and_merge_queue_gates(self) -> None:
plan = self.plan("pull_request", [".github/workflows/reborn-tests.yml"])
self.assertEqual(plan["mode"], "none")
self.assertEqual(plan["root_partitions"], [])
self.assertEqual(plan["integration_lanes"], [])
self.assertTrue(plan["run_qa_replay"])
self.assertEqual(plan["coverage_mode"], "none")
def test_user_sandbox_worker_change_selects_real_docker_lane(self) -> None:
for path in (
"Dockerfile.sandbox-worker",
"crates/lanes/ironclaw_sandbox/Cargo.toml",
"crates/lanes/ironclaw_sandbox/src/lib.rs",
"crates/app/ironclaw_cli/src/runtime/mod.rs",
"crates/lanes/ironclaw_sandbox/src/sandbox_process.rs",
"crates/lanes/ironclaw_sandbox/tests/support/docker_gate.rs",
"crates/app/ironclaw_composition/src/sandbox.rs",
"crates/app/ironclaw_composition/src/builtin_capability_policy.rs",
"crates/app/ironclaw_composition/src/deployment.rs",
"crates/app/ironclaw_composition/src/factory/production_backend_assembly.rs",
"crates/app/ironclaw_composition/src/factory/runtime_lane_assembly.rs",
"crates/app/ironclaw_composition/src/input.rs",
"crates/app/ironclaw_config/src/profile.rs",
"crates/kernel/ironclaw_host_runtime/src/first_party_tools/mod.rs",
"crates/kernel/ironclaw_host_runtime/src/invocation_services.rs",
"crates/kernel/ironclaw_host_runtime/src/process_port.rs",
"crates/kernel/ironclaw_host_runtime/src/services.rs",
"crates/kernel/ironclaw_host_runtime/src/services/builder.rs",
"crates/kernel/ironclaw_runtime_policy/src/planner.rs",
"crates/kernel/ironclaw_runtime_policy/src/resolver.rs",
"crates/lanes/ironclaw_sandbox/tests/user_sandbox_docker_live.rs",
"tests/integration/reborn_sandbox_shell_turn.rs",
"tests/e2e_trace_runtime_policy_serde.rs",
"tests/fixtures/llm_traces/runtime_policy/hosted_dev_no_shell.json",
"tests/integration/support/builder.rs",
"tests/integration/support/capability_backend.rs",
"tests/integration/support/docker_gate.rs",
"tests/integration/support/harness/mod.rs",
"tests/integration/support/harness/options.rs",
"tests/integration/support/harness/profiles/sandbox_shell.rs",
):
with self.subTest(path=path):
plan = self.plan("pull_request", [path])
self.assertNotEqual(plan["mode"], "none")
self.assertTrue(plan["run_sandbox_docker"])
def test_sandbox_docker_prefix_follows_crate_inventory_when_nested(self) -> None:
"""A family-moved ironclaw_sandbox still selects the Docker lane."""
moved_directory = "crates/lanes-next/ironclaw_sandbox"
moved_metadata = metadata()
next(package for package in moved_metadata["packages"] if package["id"] == "alpha")[
"manifest_path"
] = str(ROOT / moved_directory / "Cargo.toml")
with mock.patch.object(
planner,
"crate_directory",
return_value=moved_directory,
) as resolver:
for relative_path in (
"Cargo.toml",
"src/lib.rs",
"src/sandbox_process/command.rs",
):
with self.subTest(relative_path=relative_path):
plan = planner.build_plan(
event="pull_request",
changed_paths=[f"{moved_directory}/{relative_path}"],
metadata=moved_metadata,
canonical_packages=self.canonical,
)
self.assertTrue(plan["run_sandbox_docker"])
resolver.assert_any_call("ironclaw_sandbox", planner.ROOT)
def test_sandbox_docker_paths_preserve_regular_test_inventory_selection(
self,
) -> None:
integration_path = "tests/integration/reborn_sandbox_shell_turn.rs"
integration_inventory = planner._integration_test_lanes()
self.assertIn(integration_path, integration_inventory)
integration_plan = self.plan("pull_request", [integration_path])
self.assertTrue(integration_plan["run_sandbox_docker"])
self.assertEqual(
integration_plan["integration_lanes"],
[integration_inventory[integration_path]],
)
docker_only_path = "tests/e2e_trace_runtime_policy_serde.rs"
self.assertNotIn(docker_only_path, planner._root_test_partitions())
self.assertNotIn(docker_only_path, integration_inventory)
docker_only_plan = self.plan("pull_request", [docker_only_path])
self.assertTrue(docker_only_plan["run_sandbox_docker"])
self.assertEqual(docker_only_plan["root_partitions"], [])
self.assertEqual(docker_only_plan["integration_lanes"], [])
def test_empty_diff_fails_fast(self) -> None:
with self.assertRaisesRegex(ValueError, "empty pull-request diff"):
self.plan("pull_request", [])
def test_reborn_caller_workflow_is_owned_by_static_and_merge_queue_gates(self) -> None:
plan = self.plan("pull_request", [".github/workflows/nightly-deep-ci.yml"])
self.assertEqual(plan["mode"], "none")
def test_coverage_policy_change_is_statically_validated_on_pr(self) -> None:
plan = self.plan(
"pull_request", ["tests/integration/coverage-floor.toml"]
)
self.assertEqual(plan["mode"], "none")
self.assertEqual(plan["coverage_mode"], "none")
def test_lockfile_with_changed_crate_manifest_uses_affected_packages(self) -> None:
plan = self.plan(
"pull_request",
["Cargo.lock", "crates/alpha/Cargo.toml"],
lockfile_manifest_owned=True,
)
self.assertEqual(plan["mode"], "selected")
self.assertEqual(plan["changed_packages"], ["alpha"])
self.assertEqual(plan["affected_packages"], ["alpha", "beta", "gamma"])
def test_lockfile_without_changed_crate_manifest_defers_breadth_to_queue(self) -> None:
plan = self.plan("pull_request", ["Cargo.lock"])
self.assertEqual(plan["mode"], "none")
def test_unowned_lockfile_change_still_runs_changed_manifest_closure(self) -> None:
plan = self.plan(
"pull_request", ["Cargo.lock", "crates/alpha/Cargo.toml"]
)
self.assertEqual(plan["mode"], "selected")
self.assertEqual(plan["affected_packages"], ["alpha", "beta", "gamma"])
def test_lockfile_ownership_accepts_only_changed_workspace_dependency_edges(self) -> None:
base = {
"version": 4,
"package": [
{"name": "alpha", "version": "0.1.0", "dependencies": ["serde"]},
{
"name": "serde",
"version": "1.0.0",
"source": "registry",
"checksum": "same",
},
],
}
current = {
**base,
"package": [
{
"name": "alpha",
"version": "0.1.0",
"dependencies": ["serde", "tempfile"],
},
base["package"][1],
],
}
self.assertTrue(
planner._lockfile_change_is_manifest_owned(
current=current,
base=base,
changed_paths={"crates/alpha/Cargo.toml"},
metadata=metadata(),
)
)
current["package"][1] = {**base["package"][1], "checksum": "changed"}
self.assertFalse(
planner._lockfile_change_is_manifest_owned(
current=current,
base=base,
changed_paths={"crates/alpha/Cargo.toml"},
metadata=metadata(),
)
)
def test_lockfile_ownership_rejects_package_additions_and_removals(self) -> None:
base = {
"version": 4,
"package": [
{"name": "alpha", "version": "0.1.0", "dependencies": []},
],
}
added = {
"version": 4,
"package": [
{
"name": "alpha",
"version": "0.1.0",
"dependencies": ["tempfile"],
},
{
"name": "tempfile",
"version": "3.0.0",
"source": "registry",
"checksum": "x",
},
],
}
for current, previous in ((added, base), (base, added)):
with self.subTest(current=current):
self.assertFalse(
planner._lockfile_change_is_manifest_owned(
current=current,
base=previous,
changed_paths={"crates/alpha/Cargo.toml"},
metadata=metadata(),
)
)
def test_stress_tool_is_owned_by_dedicated_workflow(self) -> None:
plan = self.plan(
"pull_request", ["tools/ironclaw_stress/src/main.rs"]
)
self.assertEqual(plan["mode"], "none")
self.assertTrue(plan["run_qa_replay"])
self.assertEqual(plan["integration_lanes"], [])
def test_out_of_workspace_harness_is_owned_by_dedicated_workflow(self) -> None:
"""`harness/**` is a standalone cargo project, not a workspace member.
`harness/latency/runner` carries its own `Cargo.lock` and is excluded
from the workspace, so no `Tests (Reborn)` lane builds it. Without a
rule the planner fails closed on it, the same shape as the `.claude/`
and container-input gaps this file already pins.
"""
plan = self.plan("pull_request", ["harness/latency/runner/src/main.rs"])
self.assertEqual(plan["mode"], "none")
self.assertEqual(plan["crate_buckets"], [])
self.assertEqual(plan["integration_lanes"], [])
def test_workspace_excluded_helper_selects_nothing(self) -> None:
"""`tools/ironclaw_silk_decoder/**` is `[workspace]`-rooted and named in
the root `exclude`, and has no workflow of its own either.
Two things are pinned here, and the second is the interesting one.
First, it must be *classified* — WS7 moved it out of `crates/`
(PROPOSAL §12.13 D-O), and an unclassified path fails the planner
closed, skipping every downstream Reborn lane. Second, it must select
**nothing**: under its old `crates/` home the crate-attribution
fall-through selected the ENTIRE workspace for a one-line edit to a
helper nothing builds. Asserting emptiness is what stops a future
"classification" from quietly restoring that over-selection.
"""
for path in (
"tools/ironclaw_silk_decoder/src/main.rs",
"tools/ironclaw_silk_decoder/Cargo.toml",
"tools/ironclaw_silk_decoder/AGENTS.md",
):
with self.subTest(path=path):
plan = self.plan("pull_request", [path])
self.assertEqual(plan["mode"], "none")
self.assertEqual(plan["affected_packages"], [])
self.assertEqual(plan["crate_buckets"], [])
self.assertEqual(plan["integration_lanes"], [])
self.assertEqual(plan["root_partitions"], [])
def test_repo_root_metadata_class_is_owned_by_other_lanes(self) -> None:
"""The repo-root metadata class de-escalates instead of failing closed.
Classified as a *class* rather than one file per red run: a
rename-shaped diff touches root files no feature PR normally touches,
the fail-closed arm rejects the first one, and the next only surfaces
after that one is fixed. Each entry was checked against `crates/**` and
`tests/**` for a reader before being listed; none has one.
Paired assertion, as in the `.claude/` regression: accepted AND
selecting nothing, so a later "classification" that quietly escalates
these to a full matrix fails here too.
"""
for path in (
"clippy.toml",
"deny.toml",
"release-plz.toml",
".gitattributes",
".coderabbit.yaml",
".mcp.json",
".node-version",
".nvmrc",
".sqlfluff",
"Dockerfile.process-sandbox",
"docker-compose.yml",
"railway.toml",
"codecov.yml",
"ironclaw.bash",
"ironclaw.fish",
"ironclaw.zsh",
"ironclaw.png",
"LICENSE-APACHE",
"LICENSE-MIT",
"scripts/check_no_panics.py",
"scripts/dev_metrics.py",
"scripts/pre-commit-safety.sh",
"scripts/test-mutation-audit.sh",
):
with self.subTest(path=path):
plan = self.plan("pull_request", [path])
self.assertEqual(plan["mode"], "none", path)
self.assertEqual(plan["crate_buckets"], [], path)
self.assertEqual(plan["root_partitions"], [], path)
self.assertEqual(plan["integration_lanes"], [], path)
# The plan must say *why*, so a future reader sees the
# decision rather than a silent "nothing to run".
self.assertTrue(
any(
reason.startswith("static CI or workspace-policy checks own")
and path in reason
for reason in plan["reasons"]
),
plan["reasons"],
)
def test_e2e_paths_are_owned_by_dedicated_workflows(self) -> None:
for path in (
"tests/e2e/helpers.py",
"tests/e2e/reborn_webui_harness.py",
"tests/e2e/reborn_coverage_tests.txt",
"tests/e2e/scenarios/test_reborn_webui_v2_projects_api.py",
"tests/e2e/scenarios/test_reborn_webui_v2_smoke.py",
):
with self.subTest(path=path):
plan = self.plan("pull_request", [path])
self.assertEqual(plan["mode"], "none")
self.assertTrue(plan["run_qa_replay"])
self.assertEqual(plan["integration_lanes"], [])
self.assertEqual(plan["coverage_mode"], "none")
self.assertTrue(
any(
reason.startswith("dedicated Reborn E2E workflow owns")
for reason in plan["reasons"]
)
)
def test_changed_coverage_manifest_does_not_launch_integration_lanes(self) -> None:
plan = self.plan(
"pull_request",
["tests/integration/changed-coverage-exemptions.toml"],
)
self.assertEqual(plan["mode"], "none")
self.assertEqual(plan["integration_lanes"], [])
self.assertEqual(plan["coverage_mode"], "none")
def test_noncanonical_package_fails_fast(self) -> None:
with self.assertRaisesRegex(ValueError, "outside the canonical"):
planner.build_plan(
event="pull_request",
changed_paths=["crates/gamma/src/lib.rs"],
metadata=metadata(),
canonical_packages=["alpha", "beta"],
)
def test_generated_integration_suites_are_assigned_to_flat_lanes(self) -> None:
lanes = planner._integration_test_lanes()
self.assertIn("tests/integration/generated_gate_sequences.rs", lanes)
self.assertIn("tests/integration/generated_restart_sequences.rs", lanes)
self.assertIsInstance(
lanes["tests/integration/generated_gate_sequences.rs"], int
)
lane_runner = (
ROOT / "scripts/ci/reborn-coverage-lane-run.sh"
).read_text(encoding="utf-8")
self.assertIn("reborn_(integration_|generated_)", lane_runner)
self.assertIn(
'cargo test -p ironclaw_integration_tests "${test_args[@]}" '
"\\\n --ignore-rust-version",
lane_runner,
)
def test_selected_integration_lane_keeps_msrv_override(self) -> None:
lane_runner = (
ROOT / "scripts/ci/reborn-coverage-lane-run.sh"
).read_text(encoding="utf-8")
self.assertIn(
'cargo test -p ironclaw_integration_tests "${test_args[@]}" '
"\\\n --ignore-rust-version -- --nocapture",
lane_runner,
)
def test_unmapped_crate_path_widens_instead_of_refusing(self) -> None:
"""A crate path with no owning package widens to the exhaustive plan.
This used to raise. It made every crate deletion or rename unplannable
— `git diff` reports the removed crate's old paths, and CI feeds the
planner that diff — which blocked the PR rather than protecting it.
Widening cannot under-select, so it is the safe resolution; genuinely
malformed input is still rejected by the unclassified-path branch.
"""
plan = self.plan("pull_request", ["crates/deleted/src/lib.rs"])
self.assertEqual(plan["mode"], "full")
self.assertIn("deletion or rename", plan["reasons"][0])
def test_unclassified_build_input_fails_fast(self) -> None:
# Was `Dockerfile` until that gained a decision (see
# `test_container_and_hook_inputs_are_owned_by_static_gates`). The arm
# itself must stay fail-closed, so this keeps a genuinely undecided
# repo-root build input pointed at it.
with self.assertRaisesRegex(ValueError, "unclassified pull-request path"):
self.plan("pull_request", ["Makefile"])
def test_nextest_config_widens_to_exhaustive_plan(self) -> None:
"""`.config/nextest.toml` is runner config every test lane reads.
Regression for the provider-matrix retirement PR: deleting the dead
`live_tests::zizmor_scan*` overrides from `.config/nextest.toml` made
the fail-closed arm raise `unclassified pull-request path`, which
failed `Detect Reborn test scope` and skipped every downstream Reborn
lane. The file is read by every `Tests (Reborn)` lane, so no narrow
lane can exercise a change to it; it must NOT go to static control
(whose membership rule is "no Reborn test lane reads the file").
Widening to the exhaustive plan is the safe resolution — a superset
can never under-select.
"""
plan = self.plan("pull_request", [".config/nextest.toml"])
self.assertEqual(plan["mode"], "full")
self.assertEqual(plan["root_partitions"], [0, 1, 2, 3])
self.assertEqual(plan["integration_lanes"], [0, 1, 2, 3, "groups"])
self.assertIn("nextest runner config changed", plan["reasons"][0])
def test_agent_guidance_is_classified_and_selects_no_rust_lane(self) -> None:
"""`.claude/**` is prose, like `docs/**`.
Regression for the gap #7064 hit: the planner had no rule for
`.claude/`, so its fail-closed arm rejected any PR editing a skill, a
command, or a rule — failing the whole `Tests (Reborn)` roll-up on a
documentation-only change. The assertion is deliberately paired: the
path must be *accepted* AND must select no Rust lane, so a future
"classification" that quietly turns guidance edits into a full matrix
fails here too.
"""
for path in (
".claude/commands/trace.md",
".claude/rules/testing.md",
".claude/skills/reborn-feature/SKILL.md",
):
with self.subTest(path=path):
plan = self.plan("pull_request", [path])
self.assertEqual(plan["mode"], "none", path)
self.assertEqual(plan["crate_buckets"], [], path)
self.assertEqual(plan["root_partitions"], [], path)
self.assertEqual(plan["integration_lanes"], [], path)
def test_ironloop_configuration_is_classified_and_selects_no_rust_lane(self) -> None:
"""`.ironloop/**` is external IronLoop configuration, not a Reborn surface."""
for path in (
".ironloop/config.yaml",
".ironloop/implementer.md",
".ironloop/reviewer.md",
".ironloop/resolver.md",
):
with self.subTest(path=path):
plan = self.plan("pull_request", [path])
self.assertEqual(plan["mode"], "none", path)
self.assertEqual(plan["crate_buckets"], [], path)
self.assertEqual(plan["root_partitions"], [], path)
self.assertEqual(plan["integration_lanes"], [], path)
paired = self.plan(
"pull_request", [path, "crates/alpha/src/lib.rs"]
)
self.assertEqual(paired["mode"], "selected", path)
self.assertNotEqual(paired["crate_buckets"], [], path)
def test_codebase_memory_artifacts_select_no_rust_lane(self) -> None:
"""Shared agent graph data has no Reborn product or test surface."""
for path in (
".codebase-memory/.gitattributes",
".codebase-memory/graph.db.zst",
):
with self.subTest(path=path):
plan = self.plan("pull_request", [path])
self.assertEqual(plan["mode"], "none", path)
self.assertEqual(plan["crate_buckets"], [], path)
self.assertEqual(plan["root_partitions"], [], path)
self.assertEqual(plan["integration_lanes"], [], path)
paired = self.plan(
"pull_request", [path, "crates/alpha/src/lib.rs"]
)
self.assertEqual(paired["mode"], "selected", path)
self.assertNotEqual(paired["crate_buckets"], [], path)
def test_repo_wide_test_guidance_selects_no_rust_lane(self) -> None:
for path in ("tests/CLAUDE.md", "tests/integration/CLAUDE.md"):
with self.subTest(path=path):
plan = self.plan("pull_request", [path])
self.assertEqual(plan["mode"], "none")
self.assertEqual(plan["crate_buckets"], [])
self.assertEqual(plan["root_partitions"], [])
self.assertEqual(plan["integration_lanes"], [])
def test_repo_root_example_env_is_classified_and_selects_no_rust_lane(self) -> None:
"""`.env.example` is documentation, like a repo-root `*.md`.
Regression for the gap PR #7117 hit: root `*.md` was classified but
its non-`.md` sibling was not, so correcting an env-var comment failed
the whole `Tests (Reborn)` roll-up. Nothing reads the file — no crate,
test, or workflow — only doc comments name it.
Paired assertions, same reason as the `.claude/` test above: the path
must be *accepted* AND select no Rust lane, so a future
"classification" that turns a comment fix into a full matrix fails
here too.
"""
plan = self.plan("pull_request", [".env.example"])
self.assertEqual(plan["mode"], "none")
self.assertEqual(plan["crate_buckets"], [])
self.assertEqual(plan["root_partitions"], [])
self.assertEqual(plan["integration_lanes"], [])
# The ignore is per-path, not per-PR: a real change riding along still
# selects its lane.
paired = self.plan(
"pull_request", [".env.example", "crates/alpha/src/lib.rs"]
)
self.assertEqual(paired["mode"], "selected")
self.assertNotEqual(paired["crate_buckets"], [])
# And the fail-closed arm still catches a genuinely unknown root file.
with self.assertRaisesRegex(ValueError, "unclassified pull-request path"):
self.plan("pull_request", [".env.local"])
def test_github_pr_template_is_classified_and_selects_no_rust_lane(self) -> None:
"""`.github/pull_request_template.md` is a GitHub UI template.
Regression for the gap PR #7264 hit: `ISSUE_TEMPLATE/` was classified
but its exact sibling — the PR-body template, the other GitHub UI
template — was not, so a one-line checklist correction failed the whole
`Tests (Reborn)` roll-up. GitHub renders the file into the PR editor;
no crate, test, or workflow reads it (`classify-test-scope.sh` already
pairs it with `ISSUE_TEMPLATE/` in its docs-only arm).
Paired assertions, same reason as the `.claude/` test above: the path
must be *accepted* AND select no Rust lane. The sibling probes then
keep the fail-closed arm authoritative for the rest of `.github/` —
real, undecided neighbours must still refuse.
"""
plan = self.plan("pull_request", [".github/pull_request_template.md"])
self.assertEqual(plan["mode"], "none")
self.assertEqual(plan["crate_buckets"], [])
self.assertEqual(plan["root_partitions"], [])
self.assertEqual(plan["integration_lanes"], [])
# The ignore is per-path, not per-PR: a real change riding along still
# selects its lane.
paired = self.plan(
"pull_request",
[".github/pull_request_template.md", "crates/alpha/src/lib.rs"],
)
self.assertEqual(paired["mode"], "selected")
self.assertNotEqual(paired["crate_buckets"], [])
# And an unknown `.github/` sibling still refuses.
for path in (".github/dependabot.yml", ".github/labeler.yml"):
with self.subTest(path=path):
with self.assertRaisesRegex(
ValueError, "unclassified pull-request path"
):
self.plan("pull_request", [path])
def test_decided_repo_root_paths_are_owned_by_other_workflows(self) -> None:
"""Repo-root files another workflow owns outright.
The `unmapped test or CI path` arm deliberately refuses `scripts/**`
outside `scripts/ci/` so each file gets a decision rather than a
blanket prefix. Each of these has one, recorded beside the constant:
the panic baseline belongs to Code Style, the E2E selector script
belongs to the `Reborn E2E` workflow's own scope detector,
`check-version-bumps.sh` is invoked only by `platform-and-compat.yml`,
`run-reborn-webui.sh` is a local launcher no workflow references,
`mutation-audit.sh` is run only by `nightly-deep-ci.yml`'s
mutation-frontier job (the lane that already owned its self-test;
unclassified until 2026-08-06, when PR #7264's one-line usage-comment
fix in it failed the whole `Tests (Reborn)` roll-up), and
`.gitignore` is read by Code Style's `Reject tracked files that match
.gitignore` guard (#6965 — unclassified until 2026-08-04, which failed
the whole `Tests (Reborn)` roll-up on any PR that added an ignore
rule). None selects a lane in *this* planner — but the sibling that has no
decision must still refuse, which the second half asserts.
The last two were added by WS10 (2026-08-04) after editing
`check-version-bumps.sh` failed `Detect Reborn test scope` outright and
skipped every downstream Reborn lane — the same fail-closed-with-no-rule
shape as the `.claude/` gap the CHECKLIST row already records.
"""
for path in (
"scripts/no_panics_reborn_baseline.txt",
"scripts/reborn-e2e-rust.sh",
"scripts/check-version-bumps.sh",
"scripts/run-reborn-webui.sh",
"scripts/codebase-graph.sh",
"scripts/mutation-audit.sh",
".gitignore",
):
with self.subTest(path=path):
plan = self.plan("pull_request", [path])
self.assertEqual(plan["mode"], "none", path)
self.assertEqual(plan["crate_buckets"], [], path)
self.assertEqual(plan["root_partitions"], [], path)
self.assertEqual(plan["integration_lanes"], [], path)
# The plan must say *why*, so a future reader sees the
# decision rather than a silent "nothing to run".
self.assertTrue(
any(
reason.startswith("static CI or workspace-policy checks own")
and path in reason
for reason in plan["reasons"]
),
plan["reasons"],
)
# The decision is per-path, not per-PR: a real change riding
# along still selects its lane.
paired = self.plan("pull_request", [path, "crates/alpha/src/lib.rs"])
self.assertEqual(paired["mode"], "selected", path)
self.assertNotEqual(paired["crate_buckets"], [], path)
with self.assertRaisesRegex(ValueError, "unmapped test or CI path"):
self.plan("pull_request", ["scripts/some-undecided-helper.sh"])
# The two WASM build/ABI scripts are decided the same way: named in
# `platform-and-compat.yml`'s `has_direct_wasm_abi_risk` classifier,
# which scopes *and* runs them.
for path in (
"scripts/build-wasm-extensions.sh",
"scripts/check-version-bumps.sh",
):
with self.subTest(path=path):
plan = self.plan("pull_request", [path])
self.assertEqual(plan["mode"], "none", path)
self.assertEqual(plan["crate_buckets"], [], path)
def test_container_and_hook_inputs_are_owned_by_static_gates(self) -> None:
"""`Dockerfile`, `.dockerignore` and `.githooks/**` select no Rust lane.
Regression for the #7087 gap, the same class #7064 fixed for
`.claude/`: the planner had no rule for the container build inputs or
the git hooks, so its fail-closed arm rejected any PR that touched
them — which made a `Dockerfile` edit unmergeable even when the edit
was required (Wave 3's `wit/` move had to drop a `COPY wit/ wit/` that
no longer resolved, #7084). The image build belongs to
`platform-and-compat.yml`'s `has_docker_risk` lane and the hooks to
Code Style; no Reborn Rust lane builds an image or runs a hook.
Paired assertion, as in the `.claude/` regression: accepted AND
selecting nothing, so a later "classification" that quietly escalates
these to a full matrix fails here too.
"""
for path in (
"Dockerfile",
".dockerignore",
".githooks/pre-commit",
".githooks/commit-msg",
):
with self.subTest(path=path):
plan = self.plan("pull_request", [path])
self.assertEqual(plan["mode"], "none", path)
self.assertEqual(plan["crate_buckets"], [], path)
self.assertEqual(plan["root_partitions"], [], path)
self.assertEqual(plan["integration_lanes"], [], path)
def test_embedded_package_assets_schedule_the_crate_that_compiles_them(
self,
) -> None:
"""Asset trees outside every crate root route to their embedding crate.
The other half of the #7087 gap. `crates/extensions/packages/<pkg>/`
and `test-tools/<tool>/` hold no `Cargo.toml`, so cargo's package
directories cannot see them and the `crates/` arm failed closed.
Classifying them as *ignored* would have been wrong in the dangerous
direction: a `wasm/*.wasm` under `crates/extensions/packages/` is a
shipped artifact that `ironclaw_extension_support` embeds with
`include_bytes!`, so ignoring it converts a loud failure into a silent
under-schedule of a change to production output. Hence the assertion
below is that the path *selects a lane*, not merely that it is
accepted — the inverse of the `.claude/` prose test.
Driven through the REAL `EMBEDDED_ASSET_OWNERS`, against a workspace
whose packages carry the real owners' names and real manifest paths.
The first cut substituted `alpha`/`beta` owners so it could reuse the
synthetic workspace, which exercised the prefix strings but left the
prefix→owner *pairing* — the table's entire semantic content —
asserted nowhere: swapping the two owners passed. Review caught it
(#7084). `test_embedded_asset_owner_mapping_is_not_stale` supplies the
other half, deriving the same pairing from the real `include_*!` sites
so agreeing with a wrong constant is not enough.
"""
for path, owner in (
(
"crates/extensions/packages/github/wasm/github_tool.wasm",
"ironclaw_extension_support",
),
(
"crates/extensions/packages/github/wasm-src/src/lib.rs",
"ironclaw_extension_support",
),
(
"crates/extensions/packages/gmail/manifest.toml",
"ironclaw_extension_support",
),
("test-tools/market-data/manifest.toml", "ironclaw_extension_host"),
(
"test-tools/hacker-news/wasm-src/src/lib.rs",
"ironclaw_extension_host",
),
):
with self.subTest(path=path):
plan = self.plan_real_owners([path])
self.assertEqual(plan["mode"], "selected", path)
self.assertEqual(plan["changed_packages"], [owner], path)
# Routed as a *production* change, so the crates that
# consume the embedded artifact run too.
self.assertIn(owner, plan["affected_packages"], path)
self.assertNotEqual(plan["crate_buckets"], [], path)
# `ironclaw_extension_host` embeds package manifests too, and is
# covered because it depends on the routed owner — the property
# `test_embedded_asset_owner_mapping_is_not_stale` derives from the
# tree rather than assuming.
plan = self.plan_real_owners(["crates/extensions/packages/gmail/manifest.toml"])
self.assertIn("ironclaw_extension_host", plan["affected_packages"])
# A package that *is* a workspace crate still resolves to itself
# rather than falling through to the asset table, even though its
# path sits under a table prefix.
plan = self.plan_real_owners(["crates/extensions/packages/slack/src/lib.rs"])
self.assertEqual(plan["changed_packages"], ["ironclaw_slack_extension"])
# The arm never goes quiet for an asset tree with no owner. This
# raised `unmapped crate path` until #7065 replaced that fallback
# with the exhaustive plan; `full` is a superset of any narrowing, so
# the property pinned here — an unattributable asset path can never
# *under*-schedule — is now carried by the mode rather than a raise.
# It must never resolve to `none`, which is what "prose" would mean.
unowned = self.plan_real_owners(["crates/extensions/nowhere/thing.bin"])
self.assertEqual(unowned["mode"], "full")
def test_package_prompt_markdown_routes_to_its_compiler_not_to_prose(self) -> None:
"""A shipped `.md` asset is owned by the asset table, not prose.
Regression for the ordering defect found in review of #7141. The
Markdown prose carve-out ran *before* `EMBEDDED_ASSET_OWNERS`, and a
prompt is a `.md` file that no package *directory* owns — so a change
to `packages/*/prompts/**.md`, which the asset table explicitly claims
("manifests, prompts, schemas and built `wasm/*.wasm`") and which
`ironclaw_extension_support` compiles in, planned `mode=none` and
selected no lane at all. Its sibling `manifest.toml` in the same
package selected two. That is the exact "silent under-schedule of a
change to production output" the comment above the table forbids.
Both halves are pinned, because fixing this by making *all* crate-tree
`.md` route somewhere would be the opposite error.
"""
prompt = "crates/extensions/packages/github/prompts/github/create_issue.md"
plan = self.plan_real_owners([prompt])
self.assertEqual(plan["mode"], "selected", prompt)
self.assertIn("ironclaw_extension_support", plan["affected_packages"])
self.assertIn(
f"asset compiled into ironclaw_extension_support changed: {prompt}",
plan["reasons"],
)
# The prompt and the manifest beside it must agree — the defect was
# that they disagreed.
manifest = self.plan_real_owners(
["crates/extensions/packages/github/manifest.toml"]
)
self.assertEqual(manifest["mode"], plan["mode"])
# The `test-tools/` prompts are assets on the same rule, routed to the
# crate that embeds that tree.
fixture_prompt = "test-tools/hacker-news/prompts/hacker-news/top_stories.md"
fixture = self.plan_real_owners([fixture_prompt])
self.assertEqual(fixture["mode"], "selected", fixture_prompt)
self.assertIn("ironclaw_extension_host", fixture["affected_packages"])
# And the carve-out still carves. Markdown that is *not* a prompt stays
# prose even inside an asset tree — this is why the rule is keyed on the
# `prompts/` segment and not on the asset prefixes, which also cover
# documentation.
for prose in (
"crates/AGENTS.md",
"crates/extensions/AGENTS.md",
"test-tools/README.md",
):
with self.subTest(prose=prose):
quiet = self.plan_real_owners([prose])
self.assertEqual(quiet["mode"], "none", prose)
self.assertEqual(quiet["crate_buckets"], [], prose)
def test_bundled_skill_tree_routes_to_its_compiler_not_to_prose(self) -> None:
"""The repo-root `skills/` tree is shipped product output, `.md` included.
Regression fixture: `skills/` had no classification at all, so the
fail-closed arm raised `unclassified pull-request path` on any PR that
edited a product skill — #7157 fell over on
`skills/delegation/SKILL.md`. The tree is the build input of
`ironclaw_extension_host`: its `build.rs` walks `skills/`, parses every
`skills/<name>/SKILL.md`, and embeds each skill's complete file set
into the binary (`src/bundled_skills.rs`), so the whole tree is
production output in exactly the sense the asset-table comment forbids
under-scheduling.
The Markdown half is the sharp edge and gets its own pin: a `SKILL.md`
is the shipped artifact itself, not documentation beside one, so the
prose carve-out that correctly quiets `test-tools/README.md` must not
swallow it. Unlike the package trees — where only `prompts/**.md` is an
asset — *every* `.md` under `skills/` routes, at any depth, because the
build script bundles every file it finds.
"""
for path in (
"skills/delegation/SKILL.md",
"skills/routine-advisor/SKILL.md",
# Depth-independent: the bundler recurses, so a skill's auxiliary
# Markdown two levels down is embedded the same as its SKILL.md.
"skills/github/references/workflows.md",
# Non-Markdown skill files ride the plain asset arm.
"skills/code-review/checklist.toml",
):
with self.subTest(path=path):
plan = self.plan_real_owners([path])
self.assertEqual(plan["mode"], "selected", path)
self.assertEqual(
plan["changed_packages"], ["ironclaw_extension_host"], path
)
self.assertIn(
f"asset compiled into ironclaw_extension_host changed: {path}",
plan["reasons"],
)
self.assertNotEqual(plan["crate_buckets"], [], path)
def test_markdown_owned_by_no_crate_is_prose(self) -> None:
"""`crates/AGENTS.md` and `test-tools/README.md` select no lane.
Nothing compiles a markdown file, so a doc that belongs to no crate is
prose in the same class as `docs/` and `.claude/`. The rule is keyed
on "no package owns this path", not on a literal, so it keeps holding
for a future `crates/<family>/AGENTS.md` after the WS7 family move.
The paired case is `test_nested_crate_markdown_remains_package_owned`:
a doc *inside* a crate still resolves to that crate and keeps
selecting its lane. This must not widen into that.
"""
for path in ("crates/AGENTS.md", "test-tools/README.md"):
with self.subTest(path=path):
plan = self.plan("pull_request", [path])
self.assertEqual(plan["mode"], "none", path)
self.assertEqual(plan["crate_buckets"], [], path)
def test_embedded_asset_owner_mapping_is_not_stale(self) -> None:
"""Every prefix routes to a crate that really compiles the tree in.
The mapping is a hand-written bridge across a boundary cargo cannot
see, so it is exactly the kind of path-keyed constant CHECKLIST WS10
found silently rotting: if an asset tree or its owning crate moves,
the planner would resume failing closed (loud) or, worse, route to a
crate that no longer exists. Fail here first instead.
Existence is necessary and nowhere near sufficient, which is what
review caught on #7084: "the prefix is a directory and the owner is a
crate" also holds for a *wrong* owner, so the pairing has to come out
of the tree. It does, from the `include_str!`/`include_bytes!` sites
themselves. Routing a path to a package schedules that package and
everything that depends on it, so the invariant the planner needs is:
every crate that compiles a table-routed file under `prefix` into
itself is either the routed owner or a dependent of it.
Both halves of that bite. `crates/extensions/packages/` is embedded by
four crates, not one — `ironclaw_extension_host`,
`ironclaw_extension_manager` and `ironclaw_composition` reach
into it alongside `ironclaw_extension_support` — and they are covered
only because each depends on the support crate. Drop that edge and a
shipped-artifact change stops scheduling a crate that embeds it, which
is the silent under-schedule this table exists to prevent.
Two derivation mechanisms feed the pairing, because the trees embed two
ways: `include_str!`/`include_bytes!` literals resolve the package and
fixture trees, and `_build_script_tree_embedders` resolves `skills/`,
whose bundling runs in `build.rs` and whose include literal therefore
names `OUT_DIR` rather than the tree.
"""
crate_dirs = _workspace_crate_directories()
self.assertGreater(len(crate_dirs), 20, "crate inventory looks truncated")
self.assertNotEqual(planner.EMBEDDED_ASSET_OWNERS, ())
for prefix, owner in planner.EMBEDDED_ASSET_OWNERS:
with self.subTest(prefix=prefix):
self.assertTrue(
(ROOT / prefix).is_dir(),
f"asset tree {prefix} no longer exists",
)
self.assertIn(
owner,
crate_dirs,
f"{prefix} routes to {owner}, which is no longer a crate",
)
embedders = _crates_embedding(
prefix, crate_dirs
) | _build_script_tree_embedders(prefix, crate_dirs)
self.assertIn(
owner,
embedders,
f"{prefix} routes to {owner}, which embeds nothing from it; "
f"the crates that do are {sorted(embedders)}",
)
for embedder in sorted(embedders - {owner}):
self.assertTrue(
_depends_on(embedder, owner, crate_dirs),
f"{embedder} compiles files from {prefix} but does not "
f"depend on {owner}, so routing there never schedules it",
)
# `.env.example` classification (the other half of the run-30921860394
# rejection, which sorts first and masked the entrypoint miss below) is
# covered by `test_repo_root_example_env_is_classified_and_selects_no_rust_lane`
# above — main classified it independently via `IGNORED_ROOT_FILES` while
# this branch was in flight, same fix by the same route.
def test_reborn_container_entrypoint_is_owned_by_code_style(self) -> None:
"""`docker/reborn/entrypoint.sh` is shell that Code Style self-tests.
Second half of the same rejection. It is deliberately *not* an ignore:
the plan names an owner, because one exists —
`scripts/ci/test-reborn-docker-entrypoint.sh` drives the real script in
Code Style's script self-test step, and `code_style.yml`'s `has_code`
filter lights that lane for this exact path. No Reborn Rust lane
executes it, so it selects nothing here.
"""
plan = self.plan("pull_request", ["docker/reborn/entrypoint.sh"])
self.assertEqual(plan["mode"], "none")
self.assertEqual(plan["crate_buckets"], [])
self.assertIn(
"static CI or workspace-policy checks own: docker/reborn/entrypoint.sh",
plan["reasons"],
)
def test_classified_operator_paths_do_not_mask_a_real_lane(self) -> None:
"""Neither classification may swallow its neighbours.
The planner raises on the *first* unclassified path in sorted order, so
a per-PR shortcut would look identical to a per-path rule on the diff
that motivated them. Drive both together with a crate change and assert
the crate lane still gets selected.
"""
plan = self.plan(
"pull_request",
[
".env.example",
"docker/reborn/entrypoint.sh",
"crates/alpha/src/lib.rs",
],
)
self.assertEqual(plan["mode"], "selected")
self.assertEqual(plan["changed_packages"], ["alpha"])
self.assertNotEqual(plan["crate_buckets"], [])
def test_sibling_container_inputs_still_require_a_decision(self) -> None:
"""The entrypoint is decided; its neighbours are not.
`docker/` is classified per-file for the same reason repo-root
`scripts/` is: a blanket prefix would silently absorb the runtime
configs, which have no owning lane yet. Keep the fail-closed arm proven
for the paths nobody has decided.
"""
for path in (
"docker/reborn/config.production.toml",
"docker/process-sandbox-entrypoint.sh",
):
with self.subTest(path=path):
with self.assertRaisesRegex(
ValueError, "unclassified pull-request path"
):
self.plan("pull_request", [path])
def test_agent_guidance_does_not_mask_a_real_lane_in_the_same_pr(self) -> None:
"""Classifying `.claude/` must not swallow its neighbours.
A guidance edit riding along with a crate change still selects that
crate's lane — the ignore is per-path, not per-PR.
"""
plan = self.plan(
"pull_request",
[".claude/commands/trace.md", "crates/alpha/src/lib.rs"],
)
self.assertEqual(plan["mode"], "selected")
self.assertNotEqual(plan["crate_buckets"], [])
def test_agent_guidance_paths_are_prose_and_select_nothing(self) -> None:
"""`.claude/**` is guidance, like `docs/**`.
Regression fixture: the planner used to fail closed on every
`.claude/` path, so any PR that updated a rule, skill, or command
alongside its code could not be planned at all.
"""
for path in (
".claude/commands/triage-prs.md",
".claude/rules/safety-and-sandbox.md",
".claude/skills/reborn-feature/SKILL.md",
):
with self.subTest(path=path):
plan = self.plan("pull_request", [path])
docs = self.plan("pull_request", ["docs/reborn/README.md"])
self.assertEqual(plan["mode"], "none")
self.assertEqual(plan, docs)
def test_generated_wiki_paths_are_prose_and_select_nothing(self) -> None:
"""`openwiki/**` is generated prose, like `docs/**`.
Regression fixture for the same class as `.claude/` above: the planner
had no rule for the auto-generated wiki, so its fail-closed arm rejected
any PR that touched it. A crate rename touches it by construction — the
wiki's prose names crate directories — so the whole of WS6 was
unplannable while the gap stood.
Paired against `docs/`: the assertion is that the two are the *same*
plan, so a later change that quietly escalates the wiki to a lane fails
here too.
"""
for path in (
"openwiki/quickstart.md",
"openwiki/architecture/crates.md",
"openwiki/development/workflows.md",
):
with self.subTest(path=path):
plan = self.plan("pull_request", [path])
docs = self.plan("pull_request", ["docs/reborn/README.md"])
self.assertEqual(plan["mode"], "none")
self.assertEqual(plan, docs)
# The decision is per-path, not per-PR: a real change riding along
# still selects its lane.
paired = self.plan(
"pull_request", ["openwiki/quickstart.md", "crates/alpha/src/lib.rs"]
)
self.assertEqual(paired["mode"], "selected")
self.assertNotEqual(paired["crate_buckets"], [])
def test_crate_tree_prose_outside_any_package_selects_nothing(self) -> None:
"""`crates/AGENTS.md` and friends belong to no package.
Regression fixture: the planner fail-closed with "unmapped crate path"
on the family-level guidance files, which the restructure edits in the
same PR as the crates they describe.
"""
docs = self.plan("pull_request", ["docs/reborn/README.md"])
for path in ("crates/AGENTS.md", "crates/README.md", "crates/Architecture.md"):
with self.subTest(path=path):
plan = self.plan("pull_request", [path])
# `reasons` is human-facing narration; the selection must match.
self.assertEqual(
{k: v for k, v in plan.items() if k != "reasons"},
{k: v for k, v in docs.items() if k != "reasons"},
)
def test_crate_prose_stays_narrow_while_crate_code_widens(self) -> None:
"""Negative probe: the prose carve-out must not swallow crate code.
`crates/AGENTS.md` selects nothing; a source file under the same
unmapped directory widens to `full`. If the Markdown check were
wrongly applied, the second case would also select nothing.
"""
self.assertEqual(self.plan("pull_request", ["crates/AGENTS.md"])["mode"], "none")
self.assertEqual(
self.plan("pull_request", ["crates/not_a_package/src/lib.rs"])["mode"],
"full",
)
def test_unclassified_paths_outside_guidance_still_fail_closed(self) -> None:
"""Negative probe: widening IGNORED_PREFIXES must not open the gate.
The build-input probe was `Dockerfile` until #7084 gave that path a
decision (see `test_container_and_hook_inputs_are_owned_by_static_gates`);
`Makefile` is the genuinely undecided repo-root build input that keeps
this arm exercised, matching `test_unclassified_build_input_fails_fast`.
"""
for path in ("Makefile", "claude/rules/x.md", ".claudeignore"):
with self.subTest(path=path):
with self.assertRaisesRegex(
ValueError, "unclassified pull-request path"
):
self.plan("pull_request", [path])
def test_changed_integration_binary_selects_its_exact_lane(self) -> None:
path, lane = next(iter(planner._integration_test_lanes().items()))
plan = self.plan("pull_request", [path])
self.assertEqual(plan["mode"], "selected")
self.assertEqual(plan["integration_lanes"], [lane])
def test_shared_test_support_uses_representative_pr_lanes(self) -> None:
root_plan = self.plan(
"pull_request", ["tests/support/reborn_parity_qa/assertions.rs"]
)
integration_plan = self.plan(
"pull_request", ["tests/integration/support/database.rs"]
)
self.assertEqual(root_plan["root_partitions"], [0])
self.assertEqual(integration_plan["integration_lanes"], [0])
def test_owned_integration_support_selects_its_exact_lane(self) -> None:
for path, owner in planner.INTEGRATION_SUPPORT_OWNERS.items():
with self.subTest(path=path):
expected_lane = planner._integration_test_lanes()[owner]
plan = self.plan("pull_request", [path])
self.assertEqual(plan["integration_lanes"], [expected_lane])
def test_golden_payload_snapshot_selects_its_owning_integration_lane(self) -> None:
owner = planner.INTEGRATION_SNAPSHOT_PREFIX_OWNERS[
"tests/snapshots/golden_payload__"
]
self.assertEqual(owner, "tests/integration/golden_payload.rs")
expected_lane = planner._integration_test_lanes()[owner]
plan = self.plan(
"pull_request", ["tests/snapshots/golden_payload__tool_call.snap"]
)
self.assertEqual(plan["integration_lanes"], [expected_lane])
def test_unowned_snapshot_still_fails_closed(self) -> None:
with self.assertRaisesRegex(ValueError, "unmapped test or CI path"):
self.plan("pull_request", ["tests/snapshots/unowned__case.snap"])
def plan_prompt_surface_owners(self, paths: list[str]) -> dict:
"""Plan a pull request in a workspace naming the prompt-surface crates.
Same shape as `real_owner_metadata`: the planner rejects changed
packages outside the canonical set, so the crates the
`PROMPT_SURFACE_*` tables route from have to exist with their real
manifest paths for the paths under test to classify as production
changes rather than falling into the exhaustive-plan arm.
"""
def package(name: str, manifest: str) -> dict:
return {
"id": name,
"name": name,
"manifest_path": str(ROOT / manifest),
}
packages = [
package("ironclaw_integration_tests", "Cargo.toml"),
package(
"ironclaw_host_runtime",
"crates/kernel/ironclaw_host_runtime/Cargo.toml",
),
package(
"ironclaw_loop_contracts",
"crates/contracts/ironclaw_loop_contracts/Cargo.toml",
),
package(
"ironclaw_host_api",
"crates/contracts/ironclaw_host_api/Cargo.toml",
),
package("ironclaw_turns", "crates/kernel/ironclaw_turns/Cargo.toml"),
package(
"ironclaw_agent_loop",
"crates/loop/ironclaw_agent_loop/Cargo.toml",
),
package(
"ironclaw_loop_host",
"crates/loop/ironclaw_loop_host/Cargo.toml",
),
package("ironclaw_memory", "crates/domains/ironclaw_memory/Cargo.toml"),
]
metadata = {
"workspace_members": [entry["id"] for entry in packages],
"packages": packages,
"resolve": {
"nodes": [{"id": entry["id"], "deps": []} for entry in packages]
},
}
return planner.build_plan(
event="pull_request",
changed_paths=paths,
metadata=metadata,
canonical_packages=[
entry["name"]
for entry in packages
if entry["name"] != "ironclaw_integration_tests"
],
)
def test_prompt_surface_change_schedules_golden_lane_and_crate_bucket(self) -> None:
"""Every configured entry gets a positive case BY CONSTRUCTION.
The cases are derived from the `PROMPT_SURFACE_*` tables themselves
(each exact path, plus one representative file per prefix), so adding
an entry whose crate is missing from the fixture fails here instead of
shipping untested — the expected package is resolved from the fixture
directories, and an unresolvable entry is an explicit failure, not a
skip.
"""
golden_lane = planner._integration_test_lanes()[
planner.PROMPT_SURFACE_GOLDEN_OWNER
]
fixture_directories = {
"crates/kernel/ironclaw_host_runtime": "ironclaw_host_runtime",
"crates/contracts/ironclaw_loop_contracts": "ironclaw_loop_contracts",
"crates/contracts/ironclaw_host_api": "ironclaw_host_api",
"crates/kernel/ironclaw_turns": "ironclaw_turns",
"crates/loop/ironclaw_agent_loop": "ironclaw_agent_loop",
"crates/loop/ironclaw_loop_host": "ironclaw_loop_host",
}
cases = list(planner.PROMPT_SURFACE_PATHS) + [
f"{prefix}golden_probe.md" for prefix in planner.PROMPT_SURFACE_PREFIXES
]
for path in cases:
with self.subTest(path=path):
package = next(
(
name
for directory, name in fixture_directories.items()
if path.startswith(f"{directory}/")
),
None,
)
self.assertIsNotNone(
package,
f"prompt-surface entry {path} names a crate the fixture does "
"not carry; add it to plan_prompt_surface_owners so the "
"entry is actually tested",
)
plan = self.plan_prompt_surface_owners([path])
self.assertIn(
golden_lane,
plan["integration_lanes"],
"a prompt-surface change must run the golden snapshots on the PR",
)
self.assertIn(package, plan["changed_packages"])
def test_non_prompt_production_change_does_not_schedule_golden_lane(self) -> None:
plan = self.plan_prompt_surface_owners(
["crates/domains/ironclaw_memory/src/lib.rs"]
)
self.assertEqual(
plan["integration_lanes"],
[],
"ordinary production changes keep the narrow plan; the merge queue "
"remains the exhaustive gate",
)
self.assertIn("ironclaw_memory", plan["changed_packages"])
def test_workspace_topology_change_defers_exhaustive_matrix_to_queue(self) -> None:
plan = self.plan("pull_request", ["Cargo.toml"])
self.assertEqual(plan["mode"], "none")
self.assertEqual(plan["crate_buckets"], [])
self.assertTrue(plan["run_qa_replay"])
def test_workflow_consumes_plan_and_bounds_each_rust_matrix(self) -> None:
workflow = (ROOT / ".github/workflows/reborn-tests.yml").read_text(
encoding="utf-8"
)
self.assertIn("python3 scripts/ci/reborn_pr_test_plan.py", workflow)
self.assertIn("scripts/ci/discover-reborn-package-crates.sh", workflow)
self.assertIn("--canonical-packages", workflow)
self.assertIn('--base-sha "$BASE_SHA"', workflow)
self.assertIn("needs.changes.outputs.crate_buckets", workflow)
self.assertIn("needs.changes.outputs.root_partitions", workflow)
self.assertIn("needs.changes.outputs.integration_lanes", workflow)
self.assertIn("needs.changes.outputs.run_sandbox_docker", workflow)
self.assertIn("--test user_sandbox_docker_live", workflow)
self.assertIn("--test reborn_integration_sandbox_shell_turn", workflow)
self.assertIn(
'"${feature_args[@]}" --ignore-rust-version --all-targets',
workflow,
)
self.assertIn(
'cargo llvm-cov --branch --skip-functions \\\n'
' "${package_args[@]}" "${feature_args[@]}"',
workflow,
)
self.assertNotIn('coverage/${package}.lcov', workflow)
self.assertIn(
"max-parallel: ${{ github.event_name == 'pull_request' && 3 || 14 }}",
workflow,
)
self.assertIn(
"max-parallel: ${{ github.event_name == 'pull_request' && 1 || 4 }}",
workflow,
)
self.assertIn(
"max-parallel: ${{ github.event_name == 'pull_request' && 1 || 5 }}",
workflow,
)
self.assertIn("github.event.merge_group.base_sha", workflow)
self.assertIn(
"ran with result '${result}' despite planned=false",
workflow,
)
self.assertIn("Full Reborn plan is not exhaustive", workflow)
self.assertIn("Full Reborn plan omitted a required lane", workflow)
self.assertIn(
"github.event_name == 'merge_group' && "
"steps.scope.outputs.should_run == 'true'",
workflow,
)
self.assertIn("scripts/ci/test-critical-mutation-gate.sh", workflow)
self.assertIn("if: github.event_name == 'merge_group'", workflow)
self.assertIn(
"mutation_expected=${{ github.event_name == 'merge_group' }}",
workflow,
)
code_style = (ROOT / ".github/workflows/code_style.yml").read_text(
encoding="utf-8"
)
self.assertIn(
"python3 scripts/ci/changed_workspace_packages.py",
code_style,
)
self.assertIn('package_args+=(-p "${package}")', code_style)
self.assertIn("needs.changes.outputs.has_clippy == 'true'", code_style)
self.assertIn(
"cargo clippy --all --tests --examples ${{ matrix.flags }} -- -D warnings",
code_style,
)
self.assertIn(
"${{ toJSON(matrix.bucket.exact_targets || fromJSON('[]')) }}",
workflow,
)
self.assertIn(
'"${incremental_env[@]}" cargo test \\\n'
' -p "${package}" "--${kind}" "${name}"',
workflow,
)
def test_pr_workflows_do_not_repeat_reborn_rust_contracts(self) -> None:
code_style = (ROOT / ".github/workflows/code_style.yml").read_text(
encoding="utf-8"
)
e2e = (ROOT / ".github/workflows/reborn-e2e.yml").read_text(
encoding="utf-8"
)
reborn_tests = (ROOT / ".github/workflows/reborn-tests.yml").read_text(
encoding="utf-8"
)
regression = (
ROOT / ".github/workflows/regression-test-check.yml"
).read_text(encoding="utf-8")
self.assertIn(
"needs.changes.outputs.has_reborn_cli == 'true' && "
"github.event_name != 'pull_request'",
code_style,
)
self.assertIn(
"needs.changes.outputs.has_e2e_scope == 'true' && "
"github.event_name != 'pull_request'",
e2e,
)
self.assertIn("rust_reborn_optional=true", e2e)
self.assertIn("--validate-manifest-only", code_style)
self.assertIn("pnpm test", code_style)
self.assertIn("pnpm build", code_style)
self.assertNotIn("webui-v2-js-tests:", reborn_tests)
self.assertIn(
"github.event.review.state == 'commented' && 'commented' || 'enforcing'",
regression,
)
def test_reborn_e2e_shards_preserve_all_runtime_and_webui_suites(self) -> None:
workflow = (ROOT / ".github/workflows/reborn-e2e.yml").read_text(
encoding="utf-8"
)
rust_gate = (ROOT / "scripts/reborn-e2e-rust.sh").read_text(
encoding="utf-8"
)
for group in (
"architecture-boundaries",
"architecture-runtime",
"runtimes",
"substrates",
):
with self.subTest(group=group):
self.assertIn(f" - {group}", workflow)
self.assertEqual(
rust_gate.count(
"gateway_maps_deterministic_provider_response_errors_to_invalid_output"
),
1,
)
self.assertEqual(
rust_gate.count(
"deterministic_provider_response_errors_use_bounded_invalid_output_recovery"
),
1,
)
self.assertEqual(
rust_gate.count(
"process_projection::runtime::tests::retry_rejects_checkpoint_rejection_without_creating_a_process"
),
1,
)
self.assertNotIn("projection::tests::nested_dispatch_stream", rust_gate)
for suite in (
"tests/e2e/scenarios/test_reborn_webui_v2_smoke.py",
"tests/e2e/scenarios/test_reborn_webui_v2_sso.py",
"tests/e2e/scenarios/test_reborn_qa_trace_full_path.py",
"tests/e2e/scenarios/test_reborn_qa_trace_replay.py",
"tests/e2e/scenarios/test_reborn_blackbox_smoke.py",
"tests/e2e/reborn_responses_e2e_tests.txt",
):
with self.subTest(suite=suite):
self.assertIn(suite, workflow)
for process in (
"product_build_pid",
"evidence_setup_pid",
):
with self.subTest(process=process):
self.assertIn(f'wait "${{{process}}}" || status=1', workflow)
self.assertIn('default_binary="${RUNNER_TEMP}/ironclaw-default"', workflow)
self.assertIn('mv "${default_binary}" "${target_dir}/debug/ironclaw"', workflow)
self.assertNotIn('--target-dir "${CARGO_TARGET_DIR:-target}/e2e-sso"', workflow)
self.assertIn('shard: "0/4 1/4"', workflow)
self.assertIn('shard: "2/4 3/4"', workflow)
self.assertIn('for provider_shard in "${provider_shards[@]}"', workflow)
self.assertIn(
'IRONCLAW_PROVIDER_OPERATION_SHARD="${provider_shard}"',
workflow,
)
self.assertIn('provider_pids+=("$!")', workflow)
self.assertIn('wait "${provider_pids[$index]}"', workflow)
self.assertIn('> "${provider_log}" 2>&1 &', workflow)
self.assertIn(" webui-v2-test-lanes:", workflow)
self.assertNotIn("lane: fast-contracts", workflow)
self.assertIn("lane: provider-contracts", workflow)
self.assertNotIn("\n blackbox-smoke:", workflow)
self.assertIn("max-parallel: 4", workflow)
self.assertIn("reborn-webui-v2-sso-binary-${{", workflow)
self.assertIn("reborn-webui-v2-binary-${{", workflow)
self.assertIn("touch target/debug/ironclaw", workflow)
self.assertIn("touch target/e2e-sso/debug/ironclaw", workflow)
self.assertIn(
'job_result_ok "webui-v2-test-lanes"',
workflow,
)
def test_main_coverage_cancels_superseded_commits(self) -> None:
workflow = (ROOT / ".github/workflows/coverage.yml").read_text()
self.assertIn("group: code-coverage-${{ github.ref }}", workflow)
self.assertIn("cancel-in-progress: true", workflow)
if __name__ == "__main__":
unittest.main()