diff --git a/.tangled/workflows/check-bevy-build.yml b/.tangled/workflows/check-bevy-build.yml new file mode 100644 index 00000000..3595b201 --- /dev/null +++ b/.tangled/workflows/check-bevy-build.yml @@ -0,0 +1,71 @@ +# Bevy normal-build shard of the Rust definition-of-done wall. This remains +# separate from Bevy tests and Clippy so each cold Bevy compile fits inside +# Spindle's workflow deadline. + +when: + - event: ["push"] + branch: ["main"] + paths: + - "Cargo.toml" + - "Cargo.lock" + - "build.rs" + - "rust-toolchain*" + - ".cargo/**" + - "crates/**" + - "src/**" + - "tests/**" + - "benches/**" + - "examples/**" + - "assets/plots/**" + - "scenarios/**" + - "tools/scenario.py" + - "tools/ci-pkg-config.sh" + - ".tangled/workflows/check*.yml" + - event: ["pull_request"] + branch: ["main"] + - event: ["manual"] + +engine: "nixery" + +clone: + skip: false + depth: 1 + submodules: false + +dependencies: + nixpkgs: + - bash + - coreutils + - git + - rustc + - cargo + - gcc + - pkg-config + - wayland + - wayland-protocols + - libxkbcommon + - libx11 + - libxcursor + - libxrandr + - libxi + - libxinerama + - mesa + - alsa-lib + - eudev + +steps: + - name: "setup pkg-config" + command: "bash tools/ci-pkg-config.sh /tangled/workspace/setup-pc.sh" + + - name: "bevy build" + command: | + set -euo pipefail + . /tangled/workspace/setup-pc.sh + log=/tangled/workspace/bevy-build.log + if cargo build -p misaligned-bevy --quiet > "$log" 2>&1; then + echo "bevy build: OK" + else + echo "=== bevy build FAILED ===" + cat "$log" + exit 1 + fi diff --git a/.tangled/workflows/check-bevy-clippy.yml b/.tangled/workflows/check-bevy-clippy.yml new file mode 100644 index 00000000..ec4b0181 --- /dev/null +++ b/.tangled/workflows/check-bevy-clippy.yml @@ -0,0 +1,72 @@ +# Bevy Clippy shard of the Rust definition-of-done wall. This remains +# separate from Bevy tests and the normal binary build so each cold Bevy +# compile fits inside Spindle's workflow deadline. + +when: + - event: ["push"] + branch: ["main"] + paths: + - "Cargo.toml" + - "Cargo.lock" + - "build.rs" + - "rust-toolchain*" + - ".cargo/**" + - "crates/**" + - "src/**" + - "tests/**" + - "benches/**" + - "examples/**" + - "assets/plots/**" + - "scenarios/**" + - "tools/scenario.py" + - "tools/ci-pkg-config.sh" + - ".tangled/workflows/check*.yml" + - event: ["pull_request"] + branch: ["main"] + - event: ["manual"] + +engine: "nixery" + +clone: + skip: false + depth: 1 + submodules: false + +dependencies: + nixpkgs: + - bash + - coreutils + - git + - rustc + - cargo + - clippy + - gcc + - pkg-config + - wayland + - wayland-protocols + - libxkbcommon + - libx11 + - libxcursor + - libxrandr + - libxi + - libxinerama + - mesa + - alsa-lib + - eudev + +steps: + - name: "setup pkg-config" + command: "bash tools/ci-pkg-config.sh /tangled/workspace/setup-pc.sh" + + - name: "clippy (bevy + assets)" + command: | + set -euo pipefail + . /tangled/workspace/setup-pc.sh + log=/tangled/workspace/clippy-bevy.log + if cargo clippy -p misaligned-bevy -p misaligned-assets --all-targets --quiet -- -D warnings > "$log" 2>&1; then + echo "clippy (bevy + assets): OK" + else + echo "=== clippy (bevy + assets) FAILED ===" + cat "$log" + exit 1 + fi diff --git a/.tangled/workflows/check-bevy-test.yml b/.tangled/workflows/check-bevy-test.yml new file mode 100644 index 00000000..e2b9b42f --- /dev/null +++ b/.tangled/workflows/check-bevy-test.yml @@ -0,0 +1,71 @@ +# Bevy test shard of the Rust definition-of-done wall. This remains separate +# from Bevy Clippy and the normal binary build so each cold Bevy compile fits +# inside Spindle's workflow deadline. + +when: + - event: ["push"] + branch: ["main"] + paths: + - "Cargo.toml" + - "Cargo.lock" + - "build.rs" + - "rust-toolchain*" + - ".cargo/**" + - "crates/**" + - "src/**" + - "tests/**" + - "benches/**" + - "examples/**" + - "assets/plots/**" + - "scenarios/**" + - "tools/scenario.py" + - "tools/ci-pkg-config.sh" + - ".tangled/workflows/check*.yml" + - event: ["pull_request"] + branch: ["main"] + - event: ["manual"] + +engine: "nixery" + +clone: + skip: false + depth: 1 + submodules: false + +dependencies: + nixpkgs: + - bash + - coreutils + - git + - rustc + - cargo + - gcc + - pkg-config + - wayland + - wayland-protocols + - libxkbcommon + - libx11 + - libxcursor + - libxrandr + - libxi + - libxinerama + - mesa + - alsa-lib + - eudev + +steps: + - name: "setup pkg-config" + command: "bash tools/ci-pkg-config.sh /tangled/workspace/setup-pc.sh" + + - name: "cargo test (bevy + assets)" + command: | + set -euo pipefail + . /tangled/workspace/setup-pc.sh + log=/tangled/workspace/cargo-test-bevy.log + if cargo test -p misaligned-bevy -p misaligned-assets --quiet > "$log" 2>&1; then + echo "cargo test (bevy + assets): OK" + else + echo "=== cargo test (bevy + assets) FAILED ===" + cat "$log" + exit 1 + fi diff --git a/.tangled/workflows/check.yml b/.tangled/workflows/check.yml index f12e1ed7..ec8b2049 100644 --- a/.tangled/workflows/check.yml +++ b/.tangled/workflows/check.yml @@ -1,11 +1,12 @@ -# Rust/Bevy definition-of-done check. The fast corpus contract lives in -# corpus.yml. Tangled's push path filter uses the knot's complete ref-update -# file list; pull-request and manual runs remain full safety gates. +# Core/terminal shard of the Rust definition-of-done wall. Bevy tests, +# Clippy, and the normal binary build run in separate check-bevy-*.yml +# workflows so a cold Bevy compile cannot consume Spindle's whole workflow +# deadline before the remaining checks start. # -# Each step captures its own output and prints it on failure, because -# Tangled streams logs over WebSocket and completed runs show "No logs" -# on the web UI. Echoing the log on failure makes the error visible in -# the step summary. +# Tangled's push path filter uses the knot's complete ref-update file list; +# pull-request and manual runs remain conservative full safety gates. Each +# step captures output and prints it on failure because completed Tangled runs +# may no longer retain their streamed logs. when: - event: ["push"] @@ -24,7 +25,8 @@ when: - "assets/plots/**" - "scenarios/**" - "tools/scenario.py" - - ".tangled/workflows/check.yml" + - "tools/ci-pkg-config.sh" + - ".tangled/workflows/check*.yml" - event: ["pull_request"] branch: ["main"] - event: ["manual"] @@ -46,20 +48,7 @@ dependencies: - rustfmt - clippy - gcc - - pkg-config - python3 - # Bevy Linux windowing / GL dependencies - - wayland - - wayland-protocols - - libxkbcommon - - libx11 - - libxcursor - - libxrandr - - libxi - - libxinerama - - mesa - - alsa-lib - - eudev steps: - name: "cargo fmt --check" @@ -74,66 +63,26 @@ steps: exit 1 fi - - name: "setup pkg-config" + - name: "cargo test (core + terminal)" command: | set -euo pipefail - PCDIR=/tangled/workspace/.pc - mkdir -p "$PCDIR" - - # Nixery installs only outputsToInstall (typically out/bin), not dev - # outputs. The dev outputs contain the .pc files that pkg-config needs. - # Generate minimal .pc files pointing at the .so files that ARE in the - # nix store so pkg-config can resolve them for the Bevy packages. - gen_pc() { - local pc_name="$1" lib_name="${2:-$1}" - for d in /nix/store/*/lib; do - if [ -f "$d/lib${lib_name}.so" ]; then - printf 'Name: %s\nDescription: %s\nVersion: 1.0\nLibs: -L%s -l%s\n' \ - "$pc_name" "$pc_name" "$d" "$lib_name" > "$PCDIR/${pc_name}.pc" - echo "generated ${pc_name}.pc -> $d" - return - fi - done - echo "WARNING: lib${lib_name}.so not found in nix store" - } - - gen_pc wayland-client - gen_pc wayland-server - gen_pc wayland-cursor - gen_pc xkbcommon - gen_pc alsa asound - gen_pc libudev udev - - # PKG_CONFIG_PATH = generated .pc files + nix store pkgconfig dirs - PP="$PCDIR" - for d in /nix/store/*/lib/pkgconfig /nix/store/*/share/pkgconfig; do - [ -d "$d" ] && PP="$PP:$d" - done - - echo "export PKG_CONFIG_PATH=\"$PP\"" > /tangled/workspace/setup-pc.sh - echo "pkg-config setup complete" - - - name: "cargo test" - command: | - set -euo pipefail - . /tangled/workspace/setup-pc.sh - log=/tangled/workspace/cargo-test.log - if cargo test --workspace --quiet > "$log" 2>&1; then - echo "cargo test --workspace --quiet: OK" + log=/tangled/workspace/cargo-test-core-terminal.log + if cargo test -p misaligned-core -p misaligned-terminal --quiet > "$log" 2>&1; then + echo "cargo test (core + terminal): OK" else - echo "=== cargo test --workspace --quiet FAILED ===" + echo "=== cargo test (core + terminal) FAILED ===" cat "$log" exit 1 fi - - name: "clippy (terminal)" + - name: "clippy (core + terminal)" command: | set -euo pipefail - log=/tangled/workspace/clippy-terminal.log + log=/tangled/workspace/clippy-core-terminal.log if cargo clippy -p misaligned-core -p misaligned-terminal --all-targets --quiet -- -D warnings > "$log" 2>&1; then - echo "clippy (terminal): OK" + echo "clippy (core + terminal): OK" else - echo "=== clippy (terminal) FAILED ===" + echo "=== clippy (core + terminal) FAILED ===" cat "$log" exit 1 fi @@ -149,29 +98,3 @@ steps: cat "$log" exit 1 fi - - - name: "clippy (bevy + assets)" - command: | - set -euo pipefail - . /tangled/workspace/setup-pc.sh - log=/tangled/workspace/clippy-bevy.log - if cargo clippy -p misaligned-bevy -p misaligned-assets --all-targets --quiet -- -D warnings > "$log" 2>&1; then - echo "clippy (bevy + assets): OK" - else - echo "=== clippy (bevy + assets) FAILED ===" - cat "$log" - exit 1 - fi - - - name: "bevy build" - command: | - set -euo pipefail - . /tangled/workspace/setup-pc.sh - log=/tangled/workspace/bevy-build.log - if cargo build -p misaligned-bevy --quiet > "$log" 2>&1; then - echo "bevy build: OK" - else - echo "=== bevy build FAILED ===" - cat "$log" - exit 1 - fi diff --git a/tools/check.sh b/tools/check.sh index 1936f22b..5a54775b 100755 --- a/tools/check.sh +++ b/tools/check.sh @@ -163,6 +163,7 @@ guard_cargo_target_dir() { # ── Always-on cheap steps ──────────────────────────────────────────────── step "script syntax" for script in tools/check.sh tools/corpus_gate.sh tools/wiki_gate.sh tools/design_amendment_gate.sh tools/seed-cargo-target.sh \ + tools/ci-pkg-config.sh \ tools/claim.sh tools/worktree-new.sh tools/worktree-done.sh tools/heartbeat.sh \ tools/ledger_index.sh tools/test_corpus_engine.sh tools/test_design_amendment_gate.sh \ tools/test_site_deploy.sh tools/test_site_smoke.sh tools/site-smoke.sh \ diff --git a/tools/ci-pkg-config.sh b/tools/ci-pkg-config.sh new file mode 100755 index 00000000..134776d2 --- /dev/null +++ b/tools/ci-pkg-config.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [ "$#" -ne 1 ]; then + echo "usage: $0 OUTPUT_ENV_FILE" >&2 + exit 2 +fi + +output=$1 +pcdir=/tangled/workspace/.pc +mkdir -p "$pcdir" + +# Nixery installs only outputsToInstall (typically out/bin), not dev +# outputs. The dev outputs contain the .pc files that pkg-config needs. +# Generate minimal .pc files pointing at the shared objects that are present +# in the Nix store so Cargo can resolve Bevy's Linux system dependencies. +gen_pc() { + local pc_name="$1" lib_name="${2:-$1}" + local dir + for dir in /nix/store/*/lib; do + if [ -f "$dir/lib${lib_name}.so" ]; then + printf 'Name: %s\nDescription: %s\nVersion: 1.0\nLibs: -L%s -l%s\n' \ + "$pc_name" "$pc_name" "$dir" "$lib_name" > "$pcdir/${pc_name}.pc" + echo "generated ${pc_name}.pc -> $dir" + return + fi + done + echo "WARNING: lib${lib_name}.so not found in nix store" >&2 +} + +gen_pc wayland-client +gen_pc wayland-server +gen_pc wayland-cursor +gen_pc xkbcommon +gen_pc alsa asound +gen_pc libudev udev + +pkg_config_path=$pcdir +for dir in /nix/store/*/lib/pkgconfig /nix/store/*/share/pkgconfig; do + [ -d "$dir" ] && pkg_config_path="$pkg_config_path:$dir" +done + +printf 'export PKG_CONFIG_PATH="%s"\n' "$pkg_config_path" > "$output" +echo "pkg-config setup complete" diff --git a/tools/test_ci_workflows.py b/tools/test_ci_workflows.py index a90b3317..098e7774 100755 --- a/tools/test_ci_workflows.py +++ b/tools/test_ci_workflows.py @@ -3,13 +3,26 @@ from __future__ import annotations +import subprocess from pathlib import Path ROOT = Path(__file__).resolve().parent.parent -CHECK = (ROOT / ".tangled/workflows/check.yml").read_text(encoding="utf-8") -CORPUS = (ROOT / ".tangled/workflows/corpus.yml").read_text(encoding="utf-8") -SPEC = (ROOT / ".tangled/workflows/spec-check.yml").read_text(encoding="utf-8") +WORKFLOWS = ROOT / ".tangled/workflows" +RUST_SHARDS = { + "core": (WORKFLOWS / "check.yml").read_text(encoding="utf-8"), + "bevy tests": (WORKFLOWS / "check-bevy-test.yml").read_text(encoding="utf-8"), + "bevy clippy": (WORKFLOWS / "check-bevy-clippy.yml").read_text( + encoding="utf-8" + ), + "bevy build": (WORKFLOWS / "check-bevy-build.yml").read_text( + encoding="utf-8" + ), +} +CHECK = RUST_SHARDS["core"] +CORPUS = (WORKFLOWS / "corpus.yml").read_text(encoding="utf-8") +SPEC = (WORKFLOWS / "spec-check.yml").read_text(encoding="utf-8") +PKG_CONFIG = (ROOT / "tools/ci-pkg-config.sh").read_text(encoding="utf-8") def require(haystack: str, needle: str, contract: str) -> None: @@ -17,43 +30,130 @@ def require(haystack: str, needle: str, contract: str) -> None: raise AssertionError(f"{contract}: missing {needle!r}") -def main() -> int: - push = CHECK.split(' - event: ["push"]', 1)[1].split( +def push_constraint(workflow: str) -> str: + return workflow.split(' - event: ["push"]', 1)[1].split( ' - event: ["pull_request"]', 1 )[0] - for pattern in ( - "Cargo.toml", - "Cargo.lock", - "build.rs", - "rust-toolchain*", - ".cargo/**", - "crates/**", - "src/**", - "tests/**", - "benches/**", - "examples/**", - "assets/plots/**", - "scenarios/**", - "tools/scenario.py", - ".tangled/workflows/check.yml", - ): - require(push, f' - "{pattern}"', "Rust-impacting push paths") - if "classify Rust impact" in CHECK or "run-rust-gate" in CHECK: - raise AssertionError("check.yml restored the shallow HEAD^ classifier") - setup = CHECK.index(' - name: "setup pkg-config"') - test = CHECK.index(' - name: "cargo test"') - if setup > test: - raise AssertionError("cargo test runs before pkg-config setup") - cargo_test = CHECK[test : CHECK.index(' - name: "clippy (terminal)"')] +def main() -> int: + expected_shards = { + "check.yml", + "check-bevy-test.yml", + "check-bevy-clippy.yml", + "check-bevy-build.yml", + } + actual_shards = {path.name for path in WORKFLOWS.glob("check*.yml")} + if actual_shards != expected_shards: + raise AssertionError( + "Rust shard inventory differs: " + f"expected {sorted(expected_shards)}, got {sorted(actual_shards)}" + ) + + for shard, workflow in RUST_SHARDS.items(): + push = push_constraint(workflow) + for pattern in ( + "Cargo.toml", + "Cargo.lock", + "build.rs", + "rust-toolchain*", + ".cargo/**", + "crates/**", + "src/**", + "tests/**", + "benches/**", + "examples/**", + "assets/plots/**", + "scenarios/**", + "tools/scenario.py", + "tools/ci-pkg-config.sh", + ".tangled/workflows/check*.yml", + ): + require( + push, + f' - "{pattern}"', + f"{shard} Rust-impacting push paths", + ) + require( + workflow, + ' - event: ["manual"]', + f"{shard} manual safety trigger", + ) + if "classify Rust impact" in workflow or "run-rust-gate" in workflow: + raise AssertionError(f"{shard} restored the shallow HEAD^ classifier") + + push_contracts = {push_constraint(workflow) for workflow in RUST_SHARDS.values()} + if len(push_contracts) != 1: + raise AssertionError("Rust shards do not share one exact push path inventory") + require( - cargo_test, - ". /tangled/workspace/setup-pc.sh", - "workspace tests use Linux pkg-config shims", + CHECK, + "cargo test -p misaligned-core -p misaligned-terminal --quiet", + "core and terminal test shard", + ) + require( + CHECK, + "cargo clippy -p misaligned-core -p misaligned-terminal --all-targets", + "core and terminal Clippy shard", ) require(CHECK, " - python3", "scenario runtime dependency") require(CHECK, "python3 tools/scenario.py", "executable scenario coverage") + if "cargo test --workspace" in CHECK: + raise AssertionError("check.yml restored the deadline-consuming workspace test") + + bevy_commands = { + "bevy tests": "cargo test -p misaligned-bevy -p misaligned-assets --quiet", + "bevy clippy": "cargo clippy -p misaligned-bevy -p misaligned-assets --all-targets", + "bevy build": "cargo build -p misaligned-bevy", + } + for shard, cargo_command in bevy_commands.items(): + workflow = RUST_SHARDS[shard] + if workflow.count(" if cargo ") != 1: + raise AssertionError( + f"{shard} must contain exactly one deadline-expensive Cargo command" + ) + setup = workflow.index(' - name: "setup pkg-config"') + cargo = workflow.index(cargo_command) + if setup > cargo: + raise AssertionError(f"{shard} runs before pkg-config setup") + require( + workflow, + "bash tools/ci-pkg-config.sh /tangled/workspace/setup-pc.sh", + f"{shard} shared Linux pkg-config setup", + ) + require( + workflow, + ". /tangled/workspace/setup-pc.sh", + f"{shard} uses Linux pkg-config shims", + ) + for package in ( + "pkg-config", + "wayland", + "libxkbcommon", + "alsa-lib", + "eudev", + ): + require(workflow, f" - {package}", f"{shard} Linux dependency") + + for pc_name in ( + "wayland-client", + "wayland-server", + "wayland-cursor", + "xkbcommon", + "alsa asound", + "libudev udev", + ): + require(PKG_CONFIG, f"gen_pc {pc_name}", "shared pkg-config shim") + require(PKG_CONFIG, "PKG_CONFIG_PATH", "shared pkg-config environment") + syntax = subprocess.run( + ["bash", "-n", str(ROOT / "tools/ci-pkg-config.sh")], + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + check=False, + ) + if syntax.returncode != 0: + raise AssertionError(f"shared pkg-config helper syntax: {syntax.stdout}") require(SPEC, " - gnugrep", "design gate grep dependency") require(SPEC, ' - event: ["manual"]', "manual design-gate trigger") diff --git a/wiki/log/2026-07-12-ci-rust-wall-shards.md b/wiki/log/2026-07-12-ci-rust-wall-shards.md new file mode 100644 index 00000000..e9aa4e44 --- /dev/null +++ b/wiki/log/2026-07-12-ci-rust-wall-shards.md @@ -0,0 +1,45 @@ +# CI Rust wall deadline shards + +``` +Type: log +``` + +## Intent + +Follow the hosted Clippy compatibility repair through the full Tangled wall, +inspect the actual failure when it did not complete, and repair the workflow +rather than treating a local green gate as hosted evidence. + +## Finding + +Pipeline 29774 ran source revision `b17e1c16` for 18 minutes 4 seconds and +timed out. The Nixery setup succeeded and `cargo test --workspace --quiet` +reported OK after 17 minutes 57 seconds, but the workflow deadline arrived +before terminal Clippy began. The repair for hosted `clippy::const_is_empty` +was therefore never exercised. Descendant pipelines 29775 and 29776 repeated +the same structural failure, timing out after 19 minutes 6 seconds and +16 minutes 1 second. Corpus, site, and design-amendment workflows remained +green; this was a Rust-wall topology failure, not a new source failure. + +## Repair + +Keep `check.yml` as the fast core/terminal shard: formatting, core and terminal +tests, core and terminal Clippy, and execution of every checked-in agent +scenario. Move each cold Bevy proof into its own workflow: + +- `check-bevy-test.yml` runs Bevy and asset tests; +- `check-bevy-clippy.yml` runs warning-clean Bevy and asset Clippy; +- `check-bevy-build.yml` runs the normal Bevy binary build. + +All four workflows retain the same complete native push path filter and full +pull-request/manual triggers. The three Bevy shards share one checked-in +`tools/ci-pkg-config.sh` implementation so the Linux system-library setup +cannot drift between them. `tools/test_ci_workflows.py` now pins the workflow +inventory, triggers, path filters, one-expensive-command boundary, exact Cargo +commands, dependency setup, and scenario execution. + +**Defense:** [agent-scale.md](../process/agent-scale.md) requires the hosted +Rust contract to prove tests, warning-clean core/terminal and Bevy targets, the +normal Bevy build, and executable agent scenarios. Independent deadline shards +preserve every proof while ensuring a successful cold compile cannot consume +the deadline before unrelated proofs are allowed to start. diff --git a/wiki/log/DEVLOG.md b/wiki/log/DEVLOG.md index 51fdc789..fc7b934e 100644 --- a/wiki/log/DEVLOG.md +++ b/wiki/log/DEVLOG.md @@ -41,6 +41,11 @@ add or amend a session log, then re-run the generator. - Intent: The highest-priority work order still marked compute **IN PROGRESS**, even though its own status note said all six acceptance criteria were pinned and named non-host production under `machine-work.md` as the only remaining polish boundary. `machine-work.md` is now **IMPLEMENTE... - Log: [wiki/log/2026-07-12-compute-graduation.md](2026-07-12-compute-graduation.md) +## 2026-07-12 - CI Rust wall deadline shards + +- Intent: Follow the hosted Clippy compatibility repair through the full Tangled wall, inspect the actual failure when it did not complete, and repair the workflow rather than treating a local green gate as hosted evidence. +- Log: [wiki/log/2026-07-12-ci-rust-wall-shards.md](2026-07-12-ci-rust-wall-shards.md) + ## 2026-07-12 - Tangled CI correctness audit - Intent: Audit every Tangled workflow as an executable system: trigger semantics, changed-file coverage, package availability, step ordering, and whether the advertised gates actually execute the behavior they claim to protect. diff --git a/wiki/process/agent-scale.md b/wiki/process/agent-scale.md index 1a718e30..b3e032dc 100644 --- a/wiki/process/agent-scale.md +++ b/wiki/process/agent-scale.md @@ -144,7 +144,7 @@ index. Path activity predicts reconciliation cost; it does not prohibit work. | Fast / mid-loop | After an edit | Narrowest `./tools/check.sh` mode that can catch the edit (`--docs` / `--lib` / `--frontend` or auto) | | Land | Once before merge to main | One green auto or `--full` under the rust lock when Rust is involved | | CI: corpus | Every push / pull request | Python fixtures, corpus/wiki gates, generated-index freshness | -| CI: Rust | Rust-impacting pushes; every pull request / manual run | fmt, tests, terminal + Bevy clippy/build | +| CI: Rust | Rust-impacting pushes; every pull request / manual run | four timeout-bounded shards: core/terminal + scenarios, Bevy tests, Bevy Clippy, Bevy build | Already held (2026-07-09): path auto-classification, `--docs|--lib|--frontend|--full|--land`, rust gate lock, parallel docs gates, @@ -155,22 +155,23 @@ Tangled runs two contracts. `.tangled/workflows/corpus.yml` is the fast always-on corpus job and explicitly installs its fixture dependencies, including `python3`, GNU grep, and GNU sed; it runs workflow-manifest, design-amendment, corpus, and project-operations fixtures before the real -corpus/wiki/index checks. `check.yml` uses Tangled's native push `paths` -constraint over the knot-provided changed-file set for the complete ref -update. That avoids the former shallow `HEAD^` classifier, so a multi-commit -push, deletion, or rename cannot hide an earlier Rust-impacting path. Pull -request and manual triggers deliberately run the complete Rust/Bevy safety -gate. Tangled has no scheduled trigger, so the manual trigger is the supported -full safety run instead of pretending a scheduler exists. - -The Rust wall establishes Linux `pkg-config` shims before the workspace test -step, because `cargo test --workspace` compiles the Bevy members as well as -core and terminal. It then executes every checked-in agent scenario, rather -than validating only scenario syntax. The local hook and server-side design -gate call one `tools/design_amendment_gate.sh`; the local path reads binding -page types from the staged snapshot, and both paths treat source deletions as -source changes. Workflow fixtures pin these ordering, dependency, path-filter, -and shared-policy contracts. +corpus/wiki/index checks. The Rust contract is four workflows with the same +native push `paths` inventory over the knot-provided changed-file set for the +complete ref update: `check.yml` owns format, core/terminal tests and Clippy, +and executable agent scenarios; the three `check-bevy-*.yml` shards own Bevy +tests, Clippy, and the normal build separately. That avoids both the former +shallow `HEAD^` classifier and the later failure mode where a cold +`cargo test --workspace` consumed Spindle's entire workflow deadline before +Clippy or the build started. Pull-request and manual triggers deliberately run +all four shards. Tangled has no scheduled trigger, so the manual trigger is the +supported full safety run instead of pretending a scheduler exists. + +Each Bevy shard establishes identical Linux `pkg-config` shims through +`tools/ci-pkg-config.sh` before its one expensive Cargo command. The local hook +and server-side design gate call one `tools/design_amendment_gate.sh`; the local +path reads binding page types from the staged snapshot, and both paths treat +source deletions as source changes. Workflow fixtures pin the shard inventory, +ordering, dependencies, path filters, commands, and shared-policy contracts. ### Acceptance criteria (slice B) @@ -184,14 +185,17 @@ and shared-policy contracts. 4. Docs failures run in a small Python-enabled workflow before expensive Rust work; non-Rust pushes skip the Rust wall, while pull-request/manual runs are full — HELD. +5. No hosted Rust workflow contains more than one cold Bevy Cargo command; + core/terminal and each Bevy proof complete in independently bounded shards + — HELD. **Defense:** the native ref-update path filter implements the proportional-gate contract without trusting a two-commit clone to describe an arbitrary push. The shared amendment gate implements the living-spec same-commit rule from the actual candidate tree (the index locally, checked-out commit in CI), while the -pkg-config ordering and executable scenarios make the advertised Rust wall an -honest product gate rather than a guaranteed setup failure or syntax-only -smoke. +timeout-bounded Bevy shards, shared pkg-config setup, and executable scenarios +make the advertised Rust wall an honest product gate rather than a guaranteed +deadline/setup failure or syntax-only smoke. ## 3. Append-only ledgers and generated indexes diff --git a/wiki/process/workflows.md b/wiki/process/workflows.md index 6edbc90a..adb77a68 100644 --- a/wiki/process/workflows.md +++ b/wiki/process/workflows.md @@ -200,13 +200,20 @@ never turn an empty scan into a green result. Tangled CI is split by cost. `.tangled/workflows/corpus.yml` runs workflow-manifest, design-amendment, corpus, and project-operations fixtures, the corpus/wiki gates, and generated-index freshness on every push and pull -request. `.tangled/workflows/check.yml` uses Tangled's native push `paths` -constraint over the knot's complete ref-update changed-file set, so only -Rust-impacting pushes enter the Rust/Bevy wall; every pull request and manual -invocation is a conservative full safety gate. The wall sets up its Nixery -`pkg-config` shims before workspace tests and runs all checked-in agent -scenarios after the test wall. Tangled has no schedule trigger, so `manual` is -the honest full-gate backstop. +request. The Rust wall is four timeout-bounded workflows with one shared push +path inventory: `check.yml` runs formatting, core/terminal tests and Clippy, +plus every checked-in agent scenario; `check-bevy-test.yml`, +`check-bevy-clippy.yml`, and `check-bevy-build.yml` independently prove Bevy +tests, warning-clean all-target compilation, and a normal binary build. This +preserves the complete wall without asking one cold Bevy compilation to leave +enough of Spindle's workflow deadline for every later command. + +All four use Tangled's native push `paths` constraint over the knot's complete +ref-update changed-file set, so only Rust-impacting pushes enter the wall; +every pull request and manual invocation remains a conservative full safety +gate. Bevy shards call `tools/ci-pkg-config.sh` before Cargo so the Nixery +system-library shims stay identical across the three isolated environments. +Tangled has no schedule trigger, so `manual` is the honest full-gate backstop. The local pre-commit hook and `.tangled/workflows/spec-check.yml` pipe their respective changed-path sets through `tools/design_amendment_gate.sh`. Renames