Files
ironclaw/scripts/ci/check-generic-without-concrete.sh
Josh Ford 318a6e6748 docs: consolidate docs/reborn/ into docs/internal/reborn/ (#7559)
* docs: consolidate docs/reborn/ into docs/internal/reborn/

Move-only migration; no content changes beyond path references. Executes
the follow-up that PR #7259 left open: docs/.mintignore's reborn/ entry
was kept only because the path was load-bearing, and its comment
documented that it moves under internal/ once its consumers move with it.

- git mv docs/reborn docs/internal/reborn (115 files, history preserved)
- rewrite docs/reborn -> docs/internal/reborn across every consumer
  (crate AGENTS/READMEs and doc-comments, .claude/ skills and rules,
  AGENTS.md, CI scripts, reborn-e2e.yml path filters, Dockerfile, tests,
  docs/internal plans)
- fix six relative internal/adr/ links inside the moved tree for the
  added directory level
- drop reborn/ from docs/.mintignore and FROZEN_MINTIGNORE_PATTERNS in
  scripts/ci/docs_publication_boundary.py (the frozen list only ever
  shrinks); internal/ already fences the new location

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci: classify tests/dockerfile_runtime_home.rs and shrink boundary self-test fixture

Two CI gates failed on the docs/reborn consolidation and forced decisions
this commit records:

- The Reborn PR test planner failed closed on tests/dockerfile_runtime_home.rs
  (its path-rewrite edit is functional: the test reads the moved deploy doc).
  The file was deliberately unmapped because no lane inventoried it. Decide it
  now: _root_test_partitions() and run-reborn-root-partition.sh both inventory
  it alongside support_unit_tests.rs, so the hermetic root-partition lanes run
  it (they previously ran it nowhere) and a change to it selects its partition.
  With the reader laned, map the two config.hosted-single-tenant*.toml readers
  it owns in DOCKER_RUNTIME_CONFIG_OWNERS — root-test owners select their root
  partition, completing the per-file decision set the planner comments left
  open. docker/process-sandbox-entrypoint.sh stays fail-closed.
- test_docs_publication_boundary.py's subset fixture still listed reborn/ in
  the frozen mintignore list; use the surviving entries.

Verified: both self-test suites pass (77 planner + boundary), the planner
emits a valid selected plan for this PR's full 342-path diff, shell and
Python inventories agree on partition assignment (index 0), and
dockerfile_runtime_home passes (19 tests).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-13 09:39:06 +00:00

138 lines
4.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# The automated deletion test (docs/internal/reborn/extension-runtime/overview.md §8,
# checklist DEL-9): every generic Reborn crate's dependency graph must be free
# of concrete extension crates, and its tests must pass without them.
#
# Generic crates are derived from `cargo metadata` — every `ironclaw_*`
# workspace crate declaring a Reborn layer — minus the concrete extension
# crates themselves, the package inventory crate, and the sanctioned
# assemblers (the binary and the architecture test crate).
# This mirrors `crates/app/ironclaw_architecture_tests/tests/reborn_extension_specificity.rs`;
# keep the two lists in sync.
#
# TEMPORARY_EXCEPTIONS below mirrors CONCRETE_DEPENDENCY_EXCEPTIONS in that
# test: crates listed there are skipped with a warning until the phase that
# deletes the edge lands. The list must be empty by P7 (checklist DEL-7/DEL-9).
#
# Usage: scripts/ci/check-generic-without-concrete.sh [--trees-only]
# --trees-only verify dependency graphs but skip the per-crate test runs
# (fast local mode; CI runs the full form)
set -euo pipefail
cd "$(dirname "$0")/../.."
TREES_ONLY=false
if [ "${1:-}" = "--trees-only" ]; then
TREES_ONLY=true
fi
CONCRETE_CRATES=(
ironclaw_slack_extension
ironclaw_telegram_extension
)
# dependent-crate:removal-phase — mirrors CONCRETE_DEPENDENCY_EXCEPTIONS.
# Empty since P6 deleted the composition→slack edge (DEL-7/DEL-9).
TEMPORARY_EXCEPTIONS=()
GENERIC_CRATES=()
while IFS= read -r crate_name; do
GENERIC_CRATES+=("$crate_name")
done < <(cargo metadata --format-version 1 | python3 -c '
import json, sys
REBORN_LAYERS = {"contracts", "substrates", "runtimes", "kernel", "loops", "products", "app"}
EXCLUDED = {
# Concrete extension crates (the deletion test subjects, not its scope).
"ironclaw_slack_extension",
"ironclaw_telegram_extension",
# The package inventory crate owns the concrete packages.
"ironclaw_extension_support",
# Sanctioned assemblers. NOTE: these are cargo *package* names, not crate
# directory names. The CLI package is named "ironclaw" while its directory
# is crates/ironclaw_cli. This entry used to read "ironclaw_cli"
# (a directory name), so it matched no package and left this gate red on
# main for the two concrete extension crates the binary is explicitly
# allowed to link under DEL-7. Same class of bug that
# boundary_rule_names_are_package_names_not_crate_directories catches for
# the dependency-boundary rules.
"ironclaw",
"ironclaw_architecture_tests",
"ironclaw_stress",
}
metadata = json.load(sys.stdin)
for package in metadata["packages"]:
name = package["name"]
if not (name == "ironclaw" or name.startswith("ironclaw_")):
continue
if name in EXCLUDED:
continue
layer = (package.get("metadata") or {}).get("ironclaw", {}).get("layer")
if layer in REBORN_LAYERS:
print(name)
' | sort)
if [ "${#GENERIC_CRATES[@]}" -eq 0 ]; then
echo "error: derived no generic crates from cargo metadata" >&2
exit 1
fi
echo "checking ${#GENERIC_CRATES[@]} generic crates for concrete extension dependencies"
is_excepted() {
local crate="$1"
# `${arr[@]+...}` keeps `set -u` happy on bash 3.2 when the list is empty.
for entry in ${TEMPORARY_EXCEPTIONS[@]+"${TEMPORARY_EXCEPTIONS[@]}"}; do
if [ "${entry%%:*}" = "$crate" ]; then
echo "${entry##*:}"
return 0
fi
done
return 1
}
failures=()
for crate in "${GENERIC_CRATES[@]}"; do
if phase="$(is_excepted "$crate")"; then
echo "SKIP $crate (temporary exception, removed in $phase)"
continue
fi
tree="$(cargo tree -p "$crate" --all-features -e normal,build --prefix none 2>/dev/null || true)"
if [ -z "$tree" ]; then
failures+=("$crate: cargo tree produced no output")
continue
fi
for concrete in "${CONCRETE_CRATES[@]}"; do
if printf '%s\n' "$tree" | grep -q "^${concrete} "; then
failures+=("$crate: dependency graph contains concrete extension crate ${concrete}")
fi
done
done
if [ "${#failures[@]}" -gt 0 ]; then
printf 'concrete extension crates leaked into generic dependency graphs:\n' >&2
printf ' %s\n' "${failures[@]}" >&2
exit 1
fi
echo "dependency graphs clean"
if [ "$TREES_ONLY" = true ]; then
echo "--trees-only: skipping per-crate test runs"
exit 0
fi
for crate in "${GENERIC_CRATES[@]}"; do
if phase="$(is_excepted "$crate")"; then
continue
fi
# Run each crate with the SAME feature recipe the CI crate-tests job uses
# (feature-gated test targets like product_adapters' contract tests do not
# compile bare).
flags="$(scripts/ci/package-feature-flags.sh "$crate")"
echo "==> cargo test -p $crate ${flags}"
# shellcheck disable=SC2086
cargo test -p "$crate" ${flags} --quiet
done
echo "generic crates build and pass tests without concrete extension crates"