diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..d331df5fd --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: + - package-ecosystem: "rust-toolchain" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 1 diff --git a/.github/workflows/apply-rust-nightly-refresh.yml b/.github/workflows/apply-rust-nightly-refresh.yml new file mode 100644 index 000000000..7f3186b39 --- /dev/null +++ b/.github/workflows/apply-rust-nightly-refresh.yml @@ -0,0 +1,57 @@ +name: Materialize Rust nightly refresh once + +on: + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + materialize-owned-branch: + if: >- + github.repository == 'ContextualWisdomLab/OriginWeave' && + github.event.pull_request.head.repo.full_name == github.repository && + github.event.pull_request.head.ref == 'agent/rust-toolchain-refresh-2026-08-19' && + github.event.pull_request.user.login == 'seonghobae' + runs-on: ubuntu-24.04 + timeout-minutes: 10 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.event.pull_request.head.sha }} + persist-credentials: false + - name: Materialize only the reviewed nightly snapshot + run: | + set -euo pipefail + python3 - <<'PY' + from pathlib import Path + + source_path = Path('.github/workflows/hourly-product-development.yml') + source = source_path.read_text(encoding='utf-8') + old = 'nightly-2026-08-01' + new = 'nightly-2026-08-18' + old_count = source.count(old) + new_count = source.count(new) + if old_count == 2 and new_count == 0: + refreshed_source = source.replace(old, new) + elif old_count == 0 and new_count == 2: + refreshed_source = source + else: + raise SystemExit( + f'expected exactly two selectors in one state, found old={old_count}, new={new_count}' + ) + output = Path('nightly-refresh-artifact/hourly-product-development.yml') + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text(refreshed_source, encoding='utf-8') + refreshed = output.read_text(encoding='utf-8') + if old in refreshed or refreshed.count(new) < 2: + raise SystemExit('nightly refresh artifact failed its replacement contract') + PY + - name: Upload exact refreshed workflow + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: hourly-rust-nightly-${{ github.event.pull_request.head.sha }} + path: nightly-refresh-artifact/hourly-product-development.yml + if-no-files-found: error + retention-days: 1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f804f7496..95c2fa1d7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,13 +73,13 @@ jobs: persist-credentials: false - uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 with: - toolchain: nightly-2026-08-01 + toolchain: nightly-2026-08-18 components: llvm-tools-preview - name: Install pinned cargo-llvm-cov run: cargo +1.97.1 install cargo-llvm-cov --version 0.8.6 --locked - name: Measure production functions, lines, regions, and branches run: >- - cargo +nightly-2026-08-01 llvm-cov + cargo +nightly-2026-08-18 llvm-cov --locked --workspace --all-features @@ -88,7 +88,7 @@ jobs: --output-path coverage.json - name: Record uncovered production lines run: >- - cargo +nightly-2026-08-01 llvm-cov report + cargo +nightly-2026-08-18 llvm-cov report --branch --text --show-missing-lines diff --git a/.github/workflows/hourly-product-development.yml b/.github/workflows/hourly-product-development.yml index 672754c69..396af4a95 100644 --- a/.github/workflows/hourly-product-development.yml +++ b/.github/workflows/hourly-product-development.yml @@ -154,7 +154,7 @@ jobs: run: | set -euo pipefail rustup toolchain install 1.97.1 --profile minimal --component clippy,rustfmt - rustup toolchain install nightly-2026-08-01 --profile minimal --component llvm-tools-preview + rustup toolchain install nightly-2026-08-18 --profile minimal --component llvm-tools-preview cargo +1.97.1 install cargo-llvm-cov --version 0.8.6 --locked archive="${RUNNER_TEMP}/opencode-linux-x64.tar.gz" curl -fsSL -o "$archive" \ @@ -894,7 +894,7 @@ jobs: cargo +1.97.1 test --locked --workspace --all-targets cargo +1.97.1 clippy --locked --workspace --all-targets -- -D warnings RUSTDOCFLAGS='-D warnings' cargo +1.97.1 doc --locked --workspace --no-deps - cargo +nightly-2026-08-01 llvm-cov \ + cargo +nightly-2026-08-18 llvm-cov \ --locked --workspace --all-features --branch --json --summary-only \ --output-path "${RUNNER_TEMP}/coverage.json" python3 scripts/ci/verify_coverage.py "${RUNNER_TEMP}/coverage.json" diff --git a/CHANGELOG.md b/CHANGELOG.md index d17419927..879c3407e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ All notable changes to OriginWeave are documented in this file. The format follo ### Changed +- Aligned the hourly product-development branch-coverage toolchain and its one-shot materializer with the reviewed `nightly-2026-08-18` pin, and corrected the official Dependabot Rust-toolchain reference. - Separated logical origin authority from resolved network destination authority; an origin grant no longer implies permission to connect to every resolver result. - Separated resolved-address authorization from direct transport evidence; an approved IP now becomes a usable stream only after the operating system reports the exact requested IP and port. - Separated exact TCP peer proof from authenticated TLS service identity; an observed peer becomes an authenticated HTTPS stream only after explicit-root, fixed-time, SAN-bound WebPKI verification over that same stream. diff --git a/docs/doctoring/rust-toolchain-freshness.md b/docs/doctoring/rust-toolchain-freshness.md new file mode 100644 index 000000000..a00e7fb08 --- /dev/null +++ b/docs/doctoring/rust-toolchain-freshness.md @@ -0,0 +1,44 @@ +# Rust toolchain freshness and reproducibility + +## Decision + +OriginWeave keeps Rust `1.97.1` as the exact stable compiler baseline. As of +2026-08-19 this is the current stable point release, so the generic compiler +suggestion to upgrade does not justify replacing it with a floating `stable` +channel. + +Production line, region, and function coverage remains on the stable compiler. +Branch coverage uses the independently date-pinned `nightly-2026-08-18` +toolchain because upstream `cargo-llvm-cov` still identifies Rust branch +coverage as unstable and nightly-only. Every branch-coverage command must use +the same date pin, and exact-head CI must prove that `llvm-tools-preview`, the +pinned `cargo-llvm-cov` release, the workspace, and the coverage verifier remain +compatible before merge. + +The root `rust-toolchain.toml` is tracked through GitHub Dependabot's +`rust-toolchain` ecosystem. Toolchain changes therefore arrive as reviewable +pull requests rather than silently changing underneath local or CI builds. +Date-pinned branch-coverage nightly updates remain explicit infrastructure +changes and must preserve the repository contract test. + +## Failure interpretation + +The historical OriginWeave coverage failure at PR #192 predecessor head +`ccb7d31dfe7654bab800d463c2391cc1a19c7d74` was not proof that the compiler was +too old. The compiler emitted the generic note while rejecting a non-stable +const conversion in test code. The current PR #192 head moved that conversion +out of a constant and passed the complete native CI workflow. Toolchain +freshness and source compatibility are therefore maintained as separate +controls. + +## References + +GitHub. (2025, August 19). *Dependabot now supports Rust toolchain updates*. +GitHub Changelog. +https://github.blog/changelog/2025-08-19-dependabot-now-supports-rust-toolchain-updates/ + +Rust Project Developers. (2026, July 16). *Announcing Rust 1.97.1*. Rust Blog. +https://blog.rust-lang.org/2026/07/16/Rust-1.97.1/ + +Taiki Endo and contributors. (2026). *cargo-llvm-cov* (Version 0.8.6) +[Computer software]. GitHub. https://github.com/taiki-e/cargo-llvm-cov diff --git a/tests/test_rust_toolchain_contract.py b/tests/test_rust_toolchain_contract.py new file mode 100644 index 000000000..058add241 --- /dev/null +++ b/tests/test_rust_toolchain_contract.py @@ -0,0 +1,53 @@ +"""Regression contracts for the reproducible Rust compiler baseline.""" + +from __future__ import annotations + +import tomllib +import unittest +from pathlib import Path + + +REPOSITORY_ROOT = Path(__file__).resolve().parents[1] +RUST_TOOLCHAIN = REPOSITORY_ROOT / "rust-toolchain.toml" +CI_WORKFLOW = REPOSITORY_ROOT / ".github" / "workflows" / "ci.yml" +HOURLY_WORKFLOW = REPOSITORY_ROOT / ".github" / "workflows" / "hourly-product-development.yml" +REFRESH_WORKFLOW = REPOSITORY_ROOT / ".github" / "workflows" / "apply-rust-nightly-refresh.yml" +DEPENDABOT = REPOSITORY_ROOT / ".github" / "dependabot.yml" + + +class RustToolchainContractTests(unittest.TestCase): + """Keep stable builds reproducible and branch coverage intentionally fresh.""" + + def test_stable_toolchain_is_exact_and_automatically_tracked(self) -> None: + """The stable compiler changes only through a reviewable manifest update.""" + + manifest = tomllib.loads(RUST_TOOLCHAIN.read_text(encoding="utf-8")) + self.assertEqual(manifest["toolchain"]["channel"], "1.97.1") + + dependabot = DEPENDABOT.read_text(encoding="utf-8") + self.assertIn('package-ecosystem: "rust-toolchain"', dependabot) + self.assertIn('directory: "/"', dependabot) + self.assertIn('interval: "weekly"', dependabot) + + def test_branch_coverage_uses_one_current_date_pinned_nightly(self) -> None: + """Every branch-coverage command uses the same reviewed nightly snapshot.""" + + workflow = CI_WORKFLOW.read_text(encoding="utf-8") + self.assertEqual(workflow.count("nightly-2026-08-18"), 3) + self.assertNotIn("nightly-2026-08-01", workflow) + + hourly_workflow = HOURLY_WORKFLOW.read_text(encoding="utf-8") + self.assertEqual(hourly_workflow.count("nightly-2026-08-18"), 2) + self.assertNotIn("nightly-2026-08-01", hourly_workflow) + + def test_nightly_refresh_accepts_only_old_or_already_refreshed_source(self) -> None: + """The one-shot materializer remains valid after the source is refreshed.""" + workflow = REFRESH_WORKFLOW.read_text(encoding="utf-8") + self.assertIn("old_count = source.count(old)", workflow) + self.assertIn("new_count = source.count(new)", workflow) + self.assertIn("if old_count == 2 and new_count == 0:", workflow) + self.assertIn("elif old_count == 0 and new_count == 2:", workflow) + + +if __name__ == "__main__": # pragma: no cover + unittest.main()