From 459d1c56980402cee319b3a65f676a229f50802a Mon Sep 17 00:00:00 2001 From: Tom Ballard Date: Fri, 24 Jul 2026 18:22:09 +0100 Subject: [PATCH] feat: cut CI wrappers over to native AsDecided --- .github/workflows/tests.yml | 11 ++++--- README.md | 31 ++++++++++--------- gatekeeper/github/action.yml | 35 ++++++++------------- herald/github/action.yml | 33 ++++++++------------ herald/github/render.py | 18 +++++------ recordkeeper/README.md | 3 +- registrar/github/action.yml | 37 ++++++++--------------- shared/install-native.sh | 57 +++++++++++++++++++++++++++++++++++ tests/test_herald_action.py | 17 +++++------ tests/test_herald_renderer.py | 11 ++++--- tests/test_pr_gate_action.py | 17 +++++------ tests/test_validate_action.py | 17 +++++------ watchkeeper/github/action.yml | 31 ++++++------------- 13 files changed, 170 insertions(+), 148 deletions(-) create mode 100644 shared/install-native.sh diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 55825f1..18b39d5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,7 +1,7 @@ name: tests # Pin the capability wrappers' contract: each action.yml stays a thin composite -# action that runs the right `rac` command, uploads SARIF, and re-surfaces the +# action that runs the right `decided` command, uploads SARIF, and re-surfaces the # CLI exit code. Structural only (parses YAML) — no engine install needed. on: push: @@ -29,7 +29,7 @@ jobs: run: python -m pytest tests/ -q --ignore=tests/test_herald_renderer.py # The Herald renderer is behavioral, not just structural: it shells the real - # `rac decisions-for --json` (published rac-core — the same contract the + # `decided decisions-for --json` (published asdecided-core — the same contract the # wrapper consumes in the field, ADR-063) and must render byte-identical # comments for the same corpus and diff. Kept out of the structural job so # that tier stays engine-free. @@ -43,8 +43,11 @@ jobs: with: python-version: "3.12" - - name: Install test deps and the published engine - run: python -m pip install --quiet --upgrade pip pytest rac-core + - name: Install test deps + run: python -m pip install --quiet --upgrade pip pytest + + - name: Install the verified native engine + run: bash shared/install-native.sh 0.23.1 - name: Run the renderer battery run: python -m pytest tests/test_herald_renderer.py -q diff --git a/README.md b/README.md index c00f63c..ba2969f 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,30 @@ -# rac-ci +# asdecided-ci -The CI delivery surface for [RAC](https://github.com/itsthelore/rac-core) +The CI delivery surface for [RAC](https://github.com/itsthelore/asdecided-core) (requirements-as-code) — one subdir per **capability**, with delivery platforms nested inside (`github/` first). Per ADR-092 (one repo per concern, subdir per -member) this consolidates the CI wrappers that previously lived in `rac-core` and +member) this consolidates the CI wrappers that previously lived in `asdecided-core` and in the standalone `rac-actions` / `lore-watchkeeper` / `lore-gatekeeper` repos. -Every capability is a **thin wrapper over the public `rac` CLI** (ADR-063); all -analysis and policy live in the engine package. The wrappers install the -published `rac-core` from PyPI (pin with the `rac-version` input). +Every capability is a **thin wrapper over the public `decided` CLI** (ADR-063); +all analysis and policy live in the native engine. The wrappers download a +checksum-verified `asdecided-core` release (pin with the +`asdecided-version` input). Python is used only by Herald's comment renderer, +not as the engine. ## Capabilities | Capability | Subdir | Wraps | Consumed as | | --- | --- | --- | --- | -| Watchkeeper | [`watchkeeper/github/`](watchkeeper/github/) | `rac watchkeeper` (PR knowledge review) | `uses: itsthelore/rac-ci/watchkeeper/github@` | -| Gatekeeper | [`gatekeeper/github/`](gatekeeper/github/) | `rac gate --sarif` (required merge gate) | `uses: itsthelore/rac-ci/gatekeeper/github@` | -| Registrar | [`registrar/github/`](registrar/github/) | `rac validate --sarif` (well-formedness, ADR-058) | `uses: itsthelore/rac-ci/registrar/github@` | -| Herald | [`herald/github/`](herald/github/) | `rac decisions-for --json` (advisory governing-decisions comment on PRs) | `uses: itsthelore/rac-ci/herald/github@` | +| Watchkeeper | [`watchkeeper/github/`](watchkeeper/github/) | `decided watchkeeper` (PR knowledge review) | `uses: itsthelore/asdecided-ci/watchkeeper/github@` | +| Gatekeeper | [`gatekeeper/github/`](gatekeeper/github/) | `decided gate --sarif` (required merge gate) | `uses: itsthelore/asdecided-ci/gatekeeper/github@` | +| Registrar | [`registrar/github/`](registrar/github/) | `decided validate --sarif` (well-formedness, ADR-058) | `uses: itsthelore/asdecided-ci/registrar/github@` | +| Herald | [`herald/github/`](herald/github/) | `decided decisions-for --json` (advisory governing-decisions comment on PRs) | `uses: itsthelore/asdecided-ci/herald/github@` | | Recordkeeper | [`recordkeeper/`](recordkeeper/) | read-access audit recorder (ADR-084) | *placeholder — not yet shipped* | A reusable Watchkeeper workflow is also published at [`.github/workflows/watchkeeper.yml`](.github/workflows/watchkeeper.yml) -(`uses: itsthelore/rac-ci/.github/workflows/watchkeeper.yml@`). +(`uses: itsthelore/asdecided-ci/.github/workflows/watchkeeper.yml@`). `bitbucket/` and `jenkins/` platform wrappers join under each capability when demanded; the engine is already platform-neutral (SARIF/JSON), so that work is in @@ -31,6 +33,7 @@ the wrappers, not the engine. ## History The `watchkeeper/`, `gatekeeper/`, and `registrar/` wrappers moved here from -`rac-core` with history preserved (ADR-092 convergence). Consumers pinned to the -old `itsthelore/rac-core@`, `…/pr-gate-action@v0`, or `…/validate-action@v0` -paths keep resolving on those tags; new consumers use the `rac-ci` paths above. +`asdecided-core` with history preserved (ADR-092 convergence). Consumers pinned to the +old `itsthelore/asdecided-core@`, `…/pr-gate-action@v0`, or `…/validate-action@v0` +paths keep resolving on those tags; new consumers use the `asdecided-ci` paths +above. diff --git a/gatekeeper/github/action.yml b/gatekeeper/github/action.yml index 62922f0..7f57c15 100644 --- a/gatekeeper/github/action.yml +++ b/gatekeeper/github/action.yml @@ -1,7 +1,7 @@ # RAC PR-gate composite action (v0.21.14, ADR-049 / ADR-063). # # Carries the full RAC contract into a single required pull-request check: -# install RAC and run `rac gate` — one command that enforces validation, +# install RAC and run `decided gate` — one command that enforces validation, # relationship integrity, and review under the corpus enforcement policy # (`.rac/config.yaml`), emitting one SARIF document (ADR-054). The single SARIF # is uploaded to GitHub Code Scanning under one category (`rac-gate`), and the @@ -11,11 +11,11 @@ # # The Watchkeeper action lives at `watchkeeper/github/` and the validate # (Registrar) action at `registrar/github/`; this gate is referenced as -# `uses: itsthelore/rac-ci/gatekeeper/github@`. +# `uses: itsthelore/asdecided-ci/gatekeeper/github@`. name: "RAC PR gate" description: >- Enforce a requirements-as-code (RAC) corpus on a pull request with a single - `rac gate` command — validation, relationship integrity, and review under the + `decided gate` command — validation, relationship integrity, and review under the corpus enforcement policy — surfaced inline via GitHub Code Scanning (SARIF) as a required status check. A thin wrapper over the `rac` CLI. author: "Tom Ballard" @@ -26,7 +26,7 @@ branding: inputs: path: - description: "The RAC corpus directory to enforce (passed to `rac gate`)." + description: "The RAC corpus directory to enforce (passed to `decided gate`)." required: false default: "rac" upload-sarif: @@ -39,35 +39,24 @@ inputs: description: "Directory the single `gate.sarif` document is written to." required: false default: "rac-sarif" - rac-version: + asdecided-version: description: >- - Exact rac-core version to install from PyPI. Empty installs the - latest release. + Verified native asdecided-core release to install. required: false - default: "" + default: "0.23.1" runs: using: "composite" steps: - - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install RAC + - name: Install AsDecided shell: bash - env: - RAC_VERSION: ${{ inputs.rac-version }} run: | - python -m pip install --quiet --upgrade pip - if [ -n "$RAC_VERSION" ]; then - python -m pip install --quiet "rac-core==$RAC_VERSION" - else - python -m pip install --quiet rac-core - fi + bash "$GITHUB_ACTION_PATH/../../shared/install-native.sh" \ + "${{ inputs.asdecided-version }}" # The CLI is the source of truth (ADR-058). `set +e` lets a non-zero exit # still produce SARIF for upload; the exit code is re-surfaced below. One - # command, one SARIF document — `rac gate` already composes validation, + # command, one SARIF document — `decided gate` already composes validation, # relationships, and review under the corpus enforcement policy. - name: Run RAC gate (SARIF) id: gate @@ -78,7 +67,7 @@ runs: run: | set +e mkdir -p "$SARIF_DIR" - rac gate "$INPUT_PATH" --sarif > "$SARIF_DIR/gate.sarif" + decided gate "$INPUT_PATH" --sarif > "$SARIF_DIR/gate.sarif" code=$? echo "gate exited $code" echo "exit_code=$code" >> "$GITHUB_OUTPUT" diff --git a/herald/github/action.yml b/herald/github/action.yml index 60d481e..04bdfe6 100644 --- a/herald/github/action.yml +++ b/herald/github/action.yml @@ -3,7 +3,7 @@ # # Proclaims the recorded decisions that govern a pull request's changed paths # as ONE advisory comment: compute the merge-base diff, run -# `rac decisions-for --json` per changed path (the live-decisions lookup the +# `decided decisions-for --json` per changed path (the live-decisions lookup the # CLI and MCP serve, `decision-to-code-proximity`), and post a terse, # deduplicated comment that re-runs update in place. Facts, never verdicts # (ADR-034): the comment names what governs and recommends review; it never @@ -15,18 +15,18 @@ # # Siblings: Watchkeeper at `watchkeeper/github/`, the gate at # `gatekeeper/github/`, validate at `registrar/github/`. This surface is -# referenced as `uses: itsthelore/rac-ci/herald/github@`. +# referenced as `uses: itsthelore/asdecided-ci/herald/github@`. # # Requirements: a pull_request-triggered workflow, `fetch-depth: 0` on the # checkout (the diff needs the merge base, like Watchkeeper), and # `pull-requests: write` on the job for the comment. On forks, where the # token is read-only, the comment falls back to the step summary instead of # failing the check. -name: "Lore decisions on PR" +name: "AsDecided decisions on PR" description: >- Comment the recorded decisions that govern a pull request's changed paths — id, title, and the declared scope that matched — as one advisory, - update-in-place comment. A thin wrapper over `rac decisions-for`; facts, + update-in-place comment. A thin wrapper over `decided decisions-for`; facts, never a merge gate. author: "Tom Ballard" branding: @@ -34,7 +34,7 @@ branding: color: "purple" inputs: path: - description: "The RAC corpus directory (passed to `rac decisions-for`)." + description: "The RAC corpus directory (passed to `decided decisions-for`)." required: false default: "rac" max-inline: @@ -43,29 +43,22 @@ inputs: details expander. required: false default: "5" - rac-version: + asdecided-version: description: >- - Exact rac-core version to install from PyPI. Empty installs the - latest release. + Verified native asdecided-core release to install. required: false - default: "" + default: "0.23.1" runs: using: "composite" steps: - uses: actions/setup-python@v5 with: python-version: "3.12" - - name: Install RAC + - name: Install AsDecided shell: bash - env: - RAC_VERSION: ${{ inputs.rac-version }} run: | - python -m pip install --quiet --upgrade pip - if [ -n "$RAC_VERSION" ]; then - python -m pip install --quiet "rac-core==$RAC_VERSION" - else - python -m pip install --quiet rac-core - fi + bash "$GITHUB_ACTION_PATH/../../shared/install-native.sh" \ + "${{ inputs.asdecided-version }}" # The PR's changed paths, from the merge base (triple-dot): what the PR # itself changes, correct even when the base branch has moved ahead. # Requires fetch-depth: 0 on the checkout. @@ -76,12 +69,12 @@ runs: HEAD_SHA: ${{ github.event.pull_request.head.sha }} run: | if [ -z "$BASE_SHA" ] || [ -z "$HEAD_SHA" ]; then - echo "::error::Lore decisions on PR runs on pull_request events only (no base/head sha in this event)." + echo "::error::AsDecided decisions on PR runs on pull_request events only (no base/head sha in this event)." exit 1 fi git diff --name-only "$BASE_SHA...$HEAD_SHA" > lore-changed-paths.txt echo "$(wc -l < lore-changed-paths.txt) changed path(s)" - # One deterministic comment body: `rac decisions-for --json` per changed + # One deterministic comment body: `decided decisions-for --json` per changed # path, merged by decision id, sorted, no wall-clock input — the same # bytes for the same corpus and diff. - name: Render governing decisions diff --git a/herald/github/render.py b/herald/github/render.py index f5171d8..b831bb6 100644 --- a/herald/github/render.py +++ b/herald/github/render.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Render the decisions-on-PR comment from `rac decisions-for --json`. +"""Render the decisions-on-PR comment from `decided decisions-for --json`. The thin-client half of the `pr-decision-surfacing` design (ADR-063): run the engine's live-decisions lookup for each changed path, merge by decision id, @@ -34,17 +34,17 @@ _PATHS_SHOWN = 3 -def _lookup(rac_bin: str, path: str, corpus: str) -> dict | None: - """One `rac decisions-for --json` call, parsed, or None.""" +def _lookup(decided_bin: str, path: str, corpus: str) -> dict | None: + """One `decided decisions-for --json` call, parsed, or None.""" try: proc = subprocess.run( - [rac_bin, "decisions-for", path, corpus, "--json"], + [decided_bin, "decisions-for", path, corpus, "--json"], capture_output=True, text=True, check=False, ) except FileNotFoundError: - print(f"error: rac binary not found: {rac_bin!r}", file=sys.stderr) + print(f"error: decided binary not found: {decided_bin!r}", file=sys.stderr) raise SystemExit(1) from None if proc.returncode != 0: print( @@ -59,7 +59,7 @@ def _lookup(rac_bin: str, path: str, corpus: str) -> dict | None: return None -def collect(rac_bin: str, corpus: str, changed_paths: list[str]) -> list[dict]: +def collect(decided_bin: str, corpus: str, changed_paths: list[str]) -> list[dict]: """Governing decisions merged across paths, sorted by decision id. Each entry: id, title, status, path (the artifact file), matching entries @@ -68,7 +68,7 @@ def collect(rac_bin: str, corpus: str, changed_paths: list[str]) -> list[dict]: """ merged: dict[str, dict] = {} for changed in changed_paths: - result = _lookup(rac_bin, changed, corpus) + result = _lookup(decided_bin, changed, corpus) if not result: continue for decision in result.get("decisions", []): @@ -143,11 +143,11 @@ def main(argv: list[str] | None = None) -> int: parser.add_argument("--link-base", default="") parser.add_argument("--max-inline", type=int, default=5) parser.add_argument("--out", required=True) - parser.add_argument("--rac-bin", default="rac") + parser.add_argument("--decided-bin", default="decided") args = parser.parse_args(argv) with open(args.paths_file, encoding="utf-8") as fh: changed_paths = sorted({line.strip() for line in fh if line.strip()}) - decisions = collect(args.rac_bin, args.corpus, changed_paths) + decisions = collect(args.decided_bin, args.corpus, changed_paths) body = render(decisions, args.link_base.rstrip("/"), args.max_inline) with open(args.out, "w", encoding="utf-8") as fh: fh.write(body) diff --git a/recordkeeper/README.md b/recordkeeper/README.md index 1ecb692..18ff8c2 100644 --- a/recordkeeper/README.md +++ b/recordkeeper/README.md @@ -2,6 +2,7 @@ Reserved for the **read-access audit recorder** (ADR-084) — a CI capability that records who read which artifacts. No wrapper ships yet; this directory holds the -capability's place in the `rac-ci` layout so it joins as `recordkeeper/github/` +capability's place in the `asdecided-ci` layout so it joins as +`recordkeeper/github/` (and other platforms) when the recorder lands, without re-litigating repository structure. diff --git a/registrar/github/action.yml b/registrar/github/action.yml index da02b3d..78958a2 100644 --- a/registrar/github/action.yml +++ b/registrar/github/action.yml @@ -1,11 +1,11 @@ # RAC validate composite action (v0.17.2, ADR-058). # -# A thin wrapper: install RAC, run one `rac validate --sarif`, upload the SARIF +# A thin wrapper: install RAC, run one `decided validate --sarif`, upload the SARIF # to GitHub Code Scanning, and re-surface the CLI exit code. All analysis and # severity policy live in the package (ADR-015 / ADR-053); the action never # reinterprets findings. The Watchkeeper action lives at `watchkeeper/github/`; # this Registrar (validate) action is referenced as -# `uses: itsthelore/rac-ci/registrar/github@`. +# `uses: itsthelore/asdecided-ci/registrar/github@`. name: "RAC Registrar" description: >- Validate a requirements-as-code (RAC) corpus and surface findings on the pull @@ -18,7 +18,7 @@ branding: inputs: path: - description: "The RAC corpus directory to validate (passed to `rac validate`)." + description: "The RAC corpus directory to validate (passed to `decided validate`)." required: false default: "rac" upload-sarif: @@ -31,35 +31,24 @@ inputs: description: "Where the SARIF document is written." required: false default: "rac.sarif" - rac-version: + asdecided-version: description: >- - Exact rac-core version to install from PyPI. Empty installs the - latest release. + Verified native asdecided-core release to install. required: false - default: "" + default: "0.23.1" runs: using: "composite" steps: - - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install RAC + - name: Install AsDecided shell: bash - env: - RAC_VERSION: ${{ inputs.rac-version }} run: | - python -m pip install --quiet --upgrade pip - if [ -n "$RAC_VERSION" ]; then - python -m pip install --quiet "rac-core==$RAC_VERSION" - else - python -m pip install --quiet rac-core - fi + bash "$GITHUB_ACTION_PATH/../../shared/install-native.sh" \ + "${{ inputs.asdecided-version }}" # The CLI is the source of truth (ADR-058). `set +e` lets a non-zero exit # still produce SARIF for upload; the exit code is re-surfaced below. - - name: Run rac validate (SARIF) + - name: Run decided validate (SARIF) id: validate shell: bash env: @@ -67,7 +56,7 @@ runs: SARIF_FILE: ${{ inputs.sarif-file }} run: | set +e - rac validate "$INPUT_PATH" --sarif > "$SARIF_FILE" + decided validate "$INPUT_PATH" --sarif > "$SARIF_FILE" echo "exit_code=$?" >> "$GITHUB_OUTPUT" - name: Upload SARIF to Code Scanning @@ -84,7 +73,7 @@ runs: EXIT_CODE: ${{ steps.validate.outputs.exit_code }} run: | if [ "$EXIT_CODE" != "0" ]; then - echo "::error::rac validate exited $EXIT_CODE — see the Code Scanning annotations." + echo "::error::decided validate exited $EXIT_CODE — see the Code Scanning annotations." exit "$EXIT_CODE" fi - echo "rac validate passed." + echo "decided validate passed." diff --git a/shared/install-native.sh b/shared/install-native.sh new file mode 100644 index 0000000..2abb4c7 --- /dev/null +++ b/shared/install-native.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +set -euo pipefail + +version="${1:-0.23.1}" +version="${version#v}" +if [[ "$version" != "0.23.1" ]]; then + echo "::error::asdecided-ci does not have a verified checksum for asdecided-core $version" + exit 1 +fi + +case "${RUNNER_OS:-}-$(uname -m)" in + Linux-x86_64) + archive="asdecided-x86_64-unknown-linux-gnu.tar.gz" + digest="51cce8025a7cb2f8b2caea93a8ea71be0ad8c5c316fd0ecced688267bf97b8ac" + executable="decided" + ;; + macOS-arm64) + archive="asdecided-aarch64-apple-darwin.tar.gz" + digest="40d7129541609cbcf967f7d7d453e7689412bbaac81aa8d3c84e636367804628" + executable="decided" + ;; + Windows-x86_64) + archive="asdecided-x86_64-pc-windows-msvc.zip" + digest="14e3fd9b1c693a11f364f51587f74ec7f9ba6fa09ae183e049bc4b378d35cd25" + executable="decided.exe" + ;; + *) + echo "::error::Unsupported runner: ${RUNNER_OS:-unknown} $(uname -m)" + exit 1 + ;; +esac + +install_dir="${RUNNER_TEMP}/asdecided-${version}" +download="${RUNNER_TEMP}/${archive}" +url="https://github.com/itsthelore/asdecided-core/releases/download/v${version}/${archive}" + +mkdir -p "$install_dir" +curl --fail --location --retry 3 --silent --show-error "$url" --output "$download" + +if command -v sha256sum >/dev/null 2>&1; then + echo "$digest $download" | sha256sum --check - +else + actual="$(shasum -a 256 "$download" | awk '{print $1}')" + [[ "$actual" == "$digest" ]] || { + echo "::error::Checksum mismatch for $archive" + exit 1 + } +fi + +if [[ "$archive" == *.zip ]]; then + unzip -q "$download" -d "$install_dir" +else + tar -xzf "$download" -C "$install_dir" +fi + +echo "$install_dir" >> "$GITHUB_PATH" +"$install_dir/$executable" --version diff --git a/tests/test_herald_action.py b/tests/test_herald_action.py index 3b19dd0..55e2021 100644 --- a/tests/test_herald_action.py +++ b/tests/test_herald_action.py @@ -1,6 +1,6 @@ -"""Structural tests for the Lore decisions-on-PR (Herald) composite action. +"""Structural tests for the AsDecided decisions-on-PR (Herald) action. -The action is a thin wrapper (ADR-063) over `rac decisions-for --json`: compute +The action is a thin wrapper (ADR-063) over `decided decisions-for --json`: compute the PR's merge-base diff, render one deterministic advisory comment, and post it update-in-place. Matching and liveness are the engine's; these tests pin the action's *contract* — inputs, the triple-dot diff, the renderer hand-off, and @@ -30,22 +30,21 @@ def _step(name_fragment: str) -> dict: def test_action_is_composite(): a = _action() assert a["runs"]["using"] == "composite" - assert a["name"] == "Lore decisions on PR" + assert a["name"] == "AsDecided decisions on PR" def test_action_declares_exactly_expected_inputs(): inputs = _action()["inputs"] - assert set(inputs) == {"path", "max-inline", "rac-version"} + assert set(inputs) == {"path", "max-inline", "asdecided-version"} assert inputs["path"]["default"] == "rac" assert inputs["max-inline"]["default"] == "5" -def test_action_installs_published_rac_core(): - # Published engine only (pinned via rac-version, else latest); no - # source-install mode reaching outside the action directory. +def test_action_installs_verified_native_engine(): run_steps = " ".join(s.get("run", "") for s in _action()["runs"]["steps"]) - assert "rac-core" in run_steps - assert "GITHUB_ACTION_PATH/.." not in run_steps + assert "shared/install-native.sh" in run_steps + assert "pip install" not in run_steps + assert _action()["inputs"]["asdecided-version"]["default"] == "0.23.1" def test_action_diffs_from_the_merge_base(): diff --git a/tests/test_herald_renderer.py b/tests/test_herald_renderer.py index 3f712e9..6f0702b 100644 --- a/tests/test_herald_renderer.py +++ b/tests/test_herald_renderer.py @@ -2,7 +2,7 @@ Unlike the structural action-contract tests, these exercise the real published engine: build a tiny corpus, shell `render.py` (which shells -`rac decisions-for --json` per changed path — the same contract the wrapper +`decided decisions-for --json` per changed path — the same contract the wrapper consumes in the field, ADR-063), and pin the rendered comment. Facts, never verdicts (ADR-034): governed and ungoverned diffs both exit 0, and the same corpus and diff must render byte-identical output. @@ -11,13 +11,14 @@ from __future__ import annotations import subprocess +import shutil import sys from pathlib import Path RENDER = Path(__file__).parent.parent / "herald" / "github" / "render.py" -# The `rac` console script installed alongside the interpreter running pytest. -RAC_BIN = Path(sys.executable).parent / "rac" +# The native engine installed by the workflow (or local developer environment). +DECIDED_BIN = shutil.which("decided") or "decided" LINK_BASE = "https://example.com/blob/HEAD" @@ -89,8 +90,8 @@ def _render( str(max_inline), "--out", str(out), - "--rac-bin", - str(RAC_BIN), + "--decided-bin", + str(DECIDED_BIN), ], cwd=repo, capture_output=True, diff --git a/tests/test_pr_gate_action.py b/tests/test_pr_gate_action.py index 3aa26ea..4a64429 100644 --- a/tests/test_pr_gate_action.py +++ b/tests/test_pr_gate_action.py @@ -1,6 +1,6 @@ """Structural tests for the RAC PR-gate composite action (v0.21.14). -The action is a thin wrapper that runs a single `rac gate --sarif` — one +The action is a thin wrapper that runs a single `decided gate --sarif` — one command that composes validation, relationship integrity, and review under the corpus enforcement policy — uploads the single SARIF document, and re-surfaces the CLI exit code. Its analysis is owned by the (separately tested) CLI; these @@ -29,7 +29,7 @@ def test_action_is_composite(): def test_action_declares_expected_inputs(): inputs = _action()["inputs"] - for name in ("path", "upload-sarif", "sarif-dir", "rac-version"): + for name in ("path", "upload-sarif", "sarif-dir", "asdecided-version"): assert name in inputs, f"missing input: {name}" assert inputs["path"]["default"] == "rac" assert inputs["upload-sarif"]["default"] == "true" @@ -37,10 +37,10 @@ def test_action_declares_expected_inputs(): def test_action_runs_single_gate_command(): run_steps = " ".join(s.get("run", "") for s in _action()["runs"]["steps"]) - assert "rac gate" in run_steps + assert "decided gate" in run_steps assert "--sarif" in run_steps # The three separate checks are now collapsed into the one gate command. - assert "rac validate" not in run_steps + assert "decided validate" not in run_steps assert "rac relationships" not in run_steps assert "rac review" not in run_steps @@ -59,9 +59,8 @@ def test_action_resurfaces_exit_code(): assert 'exit "$EXIT_CODE"' in run_steps -def test_action_installs_published_rac_core(): - # The action installs the published rac-core (pinned via rac-version, else - # latest); source-install dogfood lives in rac-core's own CI, not here. +def test_action_installs_verified_native_engine(): run_steps = " ".join(s.get("run", "") for s in _action()["runs"]["steps"]) - assert "pip install" in run_steps - assert "rac-core" in run_steps + assert "shared/install-native.sh" in run_steps + assert "pip install" not in run_steps + assert _action()["inputs"]["asdecided-version"]["default"] == "0.23.1" diff --git a/tests/test_validate_action.py b/tests/test_validate_action.py index ccf1488..64db012 100644 --- a/tests/test_validate_action.py +++ b/tests/test_validate_action.py @@ -1,8 +1,8 @@ """Structural tests for the Registrar (validate) composite action (ADR-058). -The action is a thin wrapper over `rac validate --sarif`; its behaviour is owned +The action is a thin wrapper over `decided validate --sarif`; its behaviour is owned by the (separately tested) CLI. These tests pin the action's *contract* — that it -stays a composite action that runs `rac validate --sarif`, uploads SARIF, and +stays a composite action that runs `decided validate --sarif`, uploads SARIF, and re-surfaces the CLI exit code — so the wiring cannot silently drift. """ @@ -27,7 +27,7 @@ def test_action_is_composite(): def test_action_declares_expected_inputs(): inputs = _action()["inputs"] - for name in ("path", "upload-sarif", "sarif-file", "rac-version"): + for name in ("path", "upload-sarif", "sarif-file", "asdecided-version"): assert name in inputs, f"missing input: {name}" assert inputs["path"]["default"] == "rac" assert inputs["upload-sarif"]["default"] == "true" @@ -36,7 +36,7 @@ def test_action_declares_expected_inputs(): def test_action_runs_rac_validate_sarif(): steps = _action()["runs"]["steps"] run_steps = " ".join(s.get("run", "") for s in steps) - assert "rac validate" in run_steps + assert "decided validate" in run_steps assert "--sarif" in run_steps @@ -54,9 +54,8 @@ def test_action_resurfaces_exit_code(): assert 'exit "$EXIT_CODE"' in run_steps -def test_action_installs_published_rac_core(): - # The action installs the published rac-core (pinned via rac-version, else - # latest); source-install dogfood lives in rac-core's own CI, not here. +def test_action_installs_verified_native_engine(): run_steps = " ".join(s.get("run", "") for s in _action()["runs"]["steps"]) - assert "pip install" in run_steps - assert "rac-core" in run_steps + assert "shared/install-native.sh" in run_steps + assert "pip install" not in run_steps + assert _action()["inputs"]["asdecided-version"]["default"] == "0.23.1" diff --git a/watchkeeper/github/action.yml b/watchkeeper/github/action.yml index a80bb6f..3bbf2b9 100644 --- a/watchkeeper/github/action.yml +++ b/watchkeeper/github/action.yml @@ -1,7 +1,7 @@ # RAC Watchkeeper composite action (v0.12.3). # # A thin wrapper: install RAC, resolve the base revision, run one -# `rac watchkeeper --format github` invocation, and route its outputs — +# `decided watchkeeper --format github` invocation, and route its outputs — # stdout to the step summary, stderr (workflow-command annotations) to the # step log. All analysis, rendering, and failure policy live in the Python # package (ADR-015); the action never reinterprets exit codes. @@ -19,7 +19,7 @@ branding: inputs: path: description: >- - Corpus directory to compare (passed to `rac watchkeeper`). + Corpus directory to compare (passed to `decided watchkeeper`). required: false default: "rac" base: @@ -30,7 +30,7 @@ inputs: default: "" fail-on: description: >- - Failure policy forwarded to `rac watchkeeper --fail-on`: + Failure policy forwarded to `decided watchkeeper --fail-on`: `error` (fail when review is recommended), `warning` (also fail on any warning finding), or `none` (never fail, still report). required: false @@ -40,31 +40,20 @@ inputs: Emit inline annotations via workflow commands (`true` or `false`). required: false default: "true" - rac-version: + asdecided-version: description: >- - Exact rac-core version to install from PyPI. Empty installs the - latest release. + Verified native asdecided-core release to install. required: false - default: "" + default: "0.23.1" runs: using: "composite" steps: - - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install RAC + - name: Install AsDecided shell: bash - env: - RAC_VERSION: ${{ inputs.rac-version }} run: | - python -m pip install --quiet --upgrade pip - if [ -n "$RAC_VERSION" ]; then - python -m pip install --quiet "rac-core==$RAC_VERSION" - else - python -m pip install --quiet rac-core - fi + bash "$GITHUB_ACTION_PATH/../../shared/install-native.sh" \ + "${{ inputs.asdecided-version }}" - name: Resolve base revision shell: bash @@ -102,7 +91,7 @@ runs: # stdout becomes the job's step summary; stderr stays in the live # step log where the runner parses the annotation commands. The # exit code propagates unchanged — policy lives in --fail-on. - rac watchkeeper "$INPUT_PATH" \ + decided watchkeeper "$INPUT_PATH" \ --base "$WATCHKEEPER_BASE" \ --format github \ --fail-on "$FAIL_ON" \