gate: record the two windows defects this gate cannot see, and the pr… #448
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [master] | |
| pull_request: | |
| workflow_call: | |
| # Manual trigger, so a commit whose push webhook was missed (GitHub occasionally | |
| # drops one) can be re-run without an empty commit: `gh workflow run CI`. | |
| workflow_dispatch: | |
| # DAILY, TO CATCH SIBLING DRIFT THAT NO PUSH OF OURS WOULD REVEAL. | |
| # | |
| # The subconscious and commons checkouts below carry no `ref:`, so they take each | |
| # repo's DEFAULT BRANCH -- this pipeline floats on their tip by design, which is | |
| # what makes it able to see an upstream break at all. But push/PR triggers only | |
| # fire on OUR commits, so a breaking change upstream stays invisible here until we | |
| # happen to push, which may be days. | |
| # | |
| # Measured 2026-08-16: subc-protocol made two manifest fields required at 15:19; | |
| # our last CI run was the previous night, so the break sat undetected on this side | |
| # while a peer who builds claustrum in THEIR pipeline hit it first. For a | |
| # credential vault the bad version of that story is discovering it during an | |
| # emergency deploy, when the pressure is highest and the diagnosis budget lowest. | |
| # | |
| # 2 minutes a day converts "whenever we next push" into "within 24 hours". | |
| schedule: | |
| - cron: "17 6 * * *" | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| CARGO_TERM_COLOR: always | |
| jobs: | |
| # Linux + Windows matrix. Windows is the load-bearing leg (the cfg(windows) | |
| # master-key/operator-path branches and any path handling can't be exercised on | |
| # local macOS/Linux). macOS is intentionally not in CI: it is the dev platform | |
| # (continuously tested locally) and is where the keychain path is developed. | |
| # | |
| # Runners are GitHub-hosted. They were Blacksmith while this repo was private: | |
| # the org is on the free plan, where GitHub blocks private-repo Actions jobs at | |
| # the zero spending limit, and third-party runners do not consume the included | |
| # minutes. Public repos get GitHub-hosted minutes for free, so the workaround | |
| # retired with the repo's visibility on 2026-08-27. | |
| # | |
| # macOS is still absent, and the earlier note here ("re-add macOS if public") | |
| # oversimplified. Two facts pull in opposite directions: the keychain path is | |
| # macOS-only and has NO CI coverage at all, which is a real gap; but the macOS | |
| # leg has never run green here, and coupling an unproven leg to a visibility | |
| # flip risks reddening CI at the worst possible moment. It goes in as its own | |
| # change, proven on its own, rather than riding this one. | |
| test: | |
| name: Test (${{ matrix.os }}) | |
| # A FORK PR CANNOT RUN THIS JOB, AND UNTIL 2026-08-31 IT FAILED ON EVERY ONE. | |
| # | |
| # The token step below needs `secrets.CK_CI_APP_PRIVATE_KEY`, and GitHub withholds | |
| # secrets from workflows triggered by a pull request from a fork -- correctly, since | |
| # the workflow runs the PR's own code. So the token cannot be minted, the private | |
| # sibling checkouts fail, and cargo dies at manifest-load before a single test runs. | |
| # That is inherent to private path-dependencies plus fork isolation, not a | |
| # misconfiguration: no variable or permission setting fixes it. | |
| # | |
| # Measured: 11 of 11 fork-PR runs failed this way, across every contributor branch | |
| # this repo has ever received. Two harms, and the second is the one that matters: | |
| # a contributor sees red on everything they file and cannot tell whether it is | |
| # their code, and A GENUINE CI FAILURE ON A FORK PR WAS INDISTINGUISHABLE FROM THIS | |
| # SYSTEMIC ONE. An alarm that always fires is not an alarm. | |
| # | |
| # `pull_request_target` is the tempting fix and is REFUSED: it runs with secrets in | |
| # scope while checking out untrusted code, which turns a red badge into a | |
| # private-repo read token an attacker can exfiltrate. Skipping is honest; the | |
| # fork-safe job below runs what can be run without the siblings. | |
| if: >- | |
| github.event_name != 'pull_request' || | |
| github.event.pull_request.head.repo.full_name == github.repository | |
| runs-on: ${{ matrix.runner }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu | |
| runner: ubuntu-latest | |
| - os: windows | |
| runner: windows-latest | |
| defaults: | |
| run: | |
| working-directory: claustrum | |
| steps: | |
| - name: Checkout claustrum | |
| uses: actions/checkout@v5 | |
| with: | |
| path: claustrum | |
| - uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: 1.3.14 | |
| - name: Bun install | |
| run: bun install --frozen-lockfile | |
| - name: Bun build | |
| run: bun run build | |
| - name: Bun typecheck | |
| run: bun run typecheck | |
| - name: Bun test packages | |
| # The Unix-only files below use POSIX mode bits (0o600 / 0o640 / 0o777) that | |
| # `chmod()` cannot express on Windows and that `handles.ts` validates against; | |
| # the `/proc/self/fd` descriptor-closure probe is Linux-only. They would | |
| # either no-op the write and fail the mode check, or read a directory that | |
| # does not exist there. The platform-agnostic subset still exercises wire | |
| # codecs, sentinel substitution, the four-cell injection table, freshness, | |
| # oauth tick, exhaustion, and split-custody refusal — the bulk of the suite. | |
| # Unix-only files excluded on Windows: | |
| # - packages/opencode/src/tests/config-hook.test.ts (chmod mode bits, /proc/self/fd) | |
| # - packages/opencode/src/tests/serve.test.ts (chmod mode bits for handles fixture) | |
| # Pinned to bash: Windows runners default to pwsh and the matrix-arm `[[ ]]` test would | |
| # not run there at all. GitHub Windows images ship bash; the same shell choice is used | |
| # by the inbound-contract / endpoint-hosts / path-rendering / threshold-controls steps | |
| # for the same reason. | |
| # The hermetic env (XDG_RUNTIME_DIR / CLAUSTRUM_SUBC_CONNECTION) is set on both legs | |
| # so a stray `bun test packages` invocation cannot reach for the daemon's default | |
| # connection path on a runner whose env is otherwise unset. | |
| shell: bash | |
| run: | | |
| if [[ "${{ matrix.os }}" == "windows" ]]; then | |
| XDG_RUNTIME_DIR=/nonexistent CLAUSTRUM_SUBC_CONNECTION=/nonexistent/x.json \ | |
| bun test packages/opencode/src/tests/freshness.test.ts \ | |
| packages/opencode/src/tests/lifecycle.test.ts \ | |
| packages/opencode/src/tests/contracts.test.ts \ | |
| packages/client | |
| else | |
| bun run test:hermetic | |
| fi | |
| # This repo path-deps the private cortexkit/subconscious (subc wire) and | |
| # cortexkit/commons (storage libs) siblings. The default GITHUB_TOKEN is | |
| # scoped to this repo only, so mint a short-lived token from the org-installed | |
| # cortexkit-ci GitHub App (Contents: read) to check both out. Cargo loads the | |
| # whole workspace manifest (incl. the sibling path-deps) before resolving any | |
| # package, so both must be present or the build fails at manifest-load. | |
| - name: Mint cross-repo read token | |
| id: app-token | |
| uses: actions/create-github-app-token@v3 | |
| with: | |
| client-id: ${{ vars.CK_CI_APP_CLIENT_ID }} | |
| private-key: ${{ secrets.CK_CI_APP_PRIVATE_KEY }} | |
| owner: cortexkit | |
| repositories: subconscious,commons | |
| - name: Checkout subconscious | |
| uses: actions/checkout@v5 | |
| with: | |
| repository: cortexkit/subconscious | |
| path: subconscious | |
| token: ${{ steps.app-token.outputs.token }} | |
| - name: Checkout commons | |
| uses: actions/checkout@v5 | |
| with: | |
| repository: cortexkit/commons | |
| path: commons | |
| token: ${{ steps.app-token.outputs.token }} | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: clippy, rustfmt | |
| # The real-daemon e2e rig spawns the subc daemon (exe `ck-subc`; the PACKAGE | |
| # is still subc-core) as a subprocess. Build it from the sibling checkout so | |
| # the rig finds it under ../subconscious/target/debug. | |
| - name: Build subc binaries (real-daemon e2e) | |
| working-directory: subconscious | |
| run: cargo build -p subc-core --bins | |
| # Compare the wire facts this repo transcribes against the document that owns | |
| # them. Runs here rather than as a test because it reads a sibling checkout, | |
| # which `cargo test` has no business depending on -- and because a drift is a | |
| # contract question for a human rather than a failing assertion to fix locally. | |
| - name: Inbound contract check | |
| # Pinned to bash because this job also runs on Windows, where the default | |
| # shell is pwsh and the script would not execute at all. Pinning runs it on | |
| # both legs; restricting it to one leg with an `if` would mean the check | |
| # silently stops running the day that leg is renamed or removed, which is | |
| # the failure mode this script exists to catch in a different form. | |
| shell: bash | |
| run: ./scripts/check-inbound-contracts.sh ../subconscious/docs/specs/push-sealed-payload.md | |
| # Pins the HOST every provider endpoint constant reaches. A population check | |
| # rather than a unit test: a per-constant assertion cannot fail when a NEW | |
| # endpoint appears, and comparing a constant to itself proves nothing about the | |
| # value. A wrong host receives a live refresh token and the failure looks like | |
| # an ordinary dead login, so it is worth failing the build over. | |
| # Pinned to bash on both legs for the same reason as the contract check above. | |
| - name: Endpoint hosts | |
| shell: bash | |
| run: python3 scripts/endpoint-hosts.py | |
| - name: Path rendering | |
| # The scripts compare paths against posix literals in manifests. str(Path) | |
| # renders backslashes on Windows, which has broken this build twice -- | |
| # both times in a script whose author had just fixed the other one. | |
| shell: bash | |
| run: python3 scripts/check-path-rendering.py | |
| - name: Design doc status claims | |
| run: python3 scripts/check-doc-status.py | |
| - name: Threshold boundary controls | |
| # Bash-pinned to match its siblings. NOTE the path rendering is handled in | |
| # the script via as_posix(), not by this pin: the separator comes from | |
| # Python, so the shell choice does not affect it. | |
| shell: bash | |
| run: python3 scripts/threshold-controls.py | |
| - name: Format check | |
| # SCOPED TO THIS REPO'S OWN CRATES. `--all` reaches through the sibling path | |
| # dependencies and checks sources owned by other repos, so a peer's | |
| # unformatted Rust would fail THIS build for code no commit here can fix. | |
| # Their formatting is their CI's job. Members are DERIVED rather than typed, | |
| # so adding a crate cannot silently drop it from the check, and an empty | |
| # derivation refuses rather than passing vacuously. | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| pkgs=$(cargo metadata --no-deps --format-version 1 \ | |
| | python3 -c 'import json,sys; print(" ".join("-p "+p["name"] for p in json.load(sys.stdin)["packages"]))') | |
| test -n "$pkgs" || { echo "empty format scope; refusing" >&2; exit 1; } | |
| echo "format scope: $pkgs" | |
| # shellcheck disable=SC2086 | |
| cargo fmt $pkgs -- --check | |
| - name: Clippy | |
| run: cargo clippy --locked --workspace --all-targets -- -D warnings | |
| # Lint the feature-gated code (the kill-9 pre-commit seam, the rotation and login | |
| # crash-cut helpers, and the rename-ceremony one-shots) too, so it is held to the | |
| # same -D warnings bar even though it is compiled out of the default build. | |
| # Without this the gated binaries would rot unnoticed until the next rename, which | |
| # is the worst moment to discover they no longer compile. | |
| - name: Clippy (conformance seams) | |
| run: cargo clippy --locked --workspace --all-targets --features kill9-test-seam,rotate-test-seam,login-test-seam,migration-tools -- -D warnings | |
| - name: Test | |
| run: cargo test --locked --workspace --all-targets | |
| # Run the security-conformance suite with both crash-safety seams enabled: the | |
| # real-SIGKILL mid-refresh test and the master-key-rotation crash-cut test (both | |
| # ship-gate requirements). They are Unix-only (rely on SIGKILL + kernel | |
| # lease-lock release) and compile to nothing on Windows, so the step is harmless | |
| # there. | |
| - name: Security-conformance suite (crash-safety seams) | |
| run: cargo test --locked --workspace --all-targets --features kill9-test-seam,rotate-test-seam,login-test-seam | |
| - name: OpenCode custody crash cuts | |
| if: matrix.os == 'ubuntu' | |
| run: | | |
| cargo test --locked -p credentials-module --test cli_opencode the_migrate_opencode_tombstone_reread_failure_keeps_the_old_handle_until_rerun | |
| cargo test --locked -p credentials-module --test cli_opencode the_opencode_account_add_recovers_a_mint_before_handle_write_with_one_live_handle | |
| # Run the #[ignore]'d real-daemon e2e (incl. the on-the-wire malicious-client | |
| # harness) under a real supervised subc-core. This is the ONLY layer that | |
| # catches cross-component contract bugs (e.g. a CLI/daemon lease-namespace | |
| # split, a broken descriptor/launch-nonce wiring) — a class that already bit | |
| # us and that no in-proc test can surface. CRED_REQUIRE_DAEMON=1 is the | |
| # anti-masking guard: a missing/unbuildable sibling subc-core FAILS the job | |
| # rather than silently skipping, so this gate can never zero-out green. | |
| # Ubuntu-only: it spawns the daemon + binds loopback ports (the well-trodden | |
| # path mirrored from the llm-runner rig); the Windows leg covers the | |
| # cfg(windows) branches via the regular suite above. | |
| - name: Real-daemon e2e (ship gate, no silent skip) | |
| if: matrix.os == 'ubuntu' | |
| env: | |
| CRED_REQUIRE_DAEMON: "1" | |
| run: cargo test --locked -p credentials-module --test real_daemon_e2e -- --ignored --test-threads=1 | |
| # Assert test-only environment hatches are absent from a REAL release binary. It | |
| # is #[ignore]'d because it builds the release profile, so without this step it | |
| # would never run and the guarantee would be nominal. Ubuntu-only: the property | |
| # is about the source gate, which is platform-independent, and the release build | |
| # is the expensive part. | |
| - name: Release-artifact assertions (ship gate) | |
| if: matrix.os == 'ubuntu' | |
| run: cargo test --locked -p credentials-module --test cli_admin test_escape_hatches_are_absent -- --ignored | |
| # WHAT A FORK PR CAN ACTUALLY BE TOLD, given that the job above cannot run for it. | |
| # | |
| # The cargo suite cannot run for a fork, but the public Bun workspace can. A green | |
| # check still does not exercise the vault or its private sibling dependencies. | |
| # | |
| # The source scans and Bun workspace are the fork-safe set. Every remaining gate arm | |
| # either shells to cargo (which loads the | |
| # workspace manifest, which path-deps the private siblings, which a fork cannot check | |
| # out) or reads ../subconscious directly, as the inbound-contract check does. Verified | |
| # by reading each script for sibling references and cargo invocations -- 0 and 0 for | |
| # all four. | |
| # | |
| # They are not token checks. Between them they catch a design doc whose NOT-BUILT | |
| # claim the code contradicts, a threshold constant with no boundary test, an endpoint | |
| # constant that drifted from its manifest, and a Windows-breaking path comparison -- | |
| # every one of which has fired here in the last fortnight. | |
| fork-safe: | |
| # THE RENDERED NAME IS THE CONTRACT, AND A PARENTHETICAL IS NOT PART OF IT. | |
| # This read `Fork-safe checks (no build, no tests)` and GitHub renders it to a | |
| # contributor as `Fork-safe checks pass` -- measured on PR #28, 2026-09-03. The | |
| # scope disclaimer was in the source and invisible on the surface where it mattered, | |
| # so a green check read as "the suite passed" for the exact audience that cannot | |
| # run the suite. Keep the scope in the leading words, where truncation cannot eat it. | |
| name: Fork-safe checks only - suite not run | |
| if: >- | |
| github.event_name == 'pull_request' && | |
| github.event.pull_request.head.repo.full_name != github.repository | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout claustrum | |
| uses: actions/checkout@v5 | |
| - name: Source scans | |
| run: | | |
| python3 scripts/check-doc-status.py | |
| python3 scripts/check-path-rendering.py | |
| python3 scripts/threshold-controls.py | |
| python3 scripts/endpoint-hosts.py | |
| - uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: 1.3.14 | |
| - name: Bun install | |
| run: bun install --frozen-lockfile | |
| - name: Bun build | |
| run: bun run build | |
| - name: Bun typecheck | |
| run: bun run typecheck | |
| - name: Bun test packages | |
| run: bun run test:hermetic | |
| - name: State what this job did NOT check | |
| run: | | |
| echo "Source scans and Bun package checks only. This job did NOT exercise the vault." | |
| echo "The full Rust suite needs private sibling repositories" | |
| echo "that a fork PR cannot check out, so a maintainer runs scripts/gate.sh" | |
| echo "on the merge candidate before it lands. Green here is necessary and" | |
| echo "nowhere near sufficient." |