Skip to content

cli: name the daemon's temp connection file by subc's own derivation #469

cli: name the daemon's temp connection file by subc's own derivation

cli: name the daemon's temp connection file by subc's own derivation #469

Workflow file for this run

name: CI
on:
push:
# `train/**` IS A PRECONDITION FOR THE CI-GATED TRAIN SHAPE, NOT A CONVENIENCE.
# Under that shape a branch push is the gate and main is fast-forwarded only on a
# green run, with GitHub required-status-checks refusing any sha that carries no
# check. So enabling protection while this trigger still said `[master]` alone would
# make main unpushable and the check that would clear it would never run -- the
# branch push produces nothing to wait for. Widened first, deliberately, before any
# protection is enabled.
branches: [master, 'train/**']
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: Fixture line endings
# Guards a defect only the Windows job can see: git rewrites LF to CRLF on
# checkout, so a byte-exact assertion against a fixture fails there and only
# there. Runs on every platform because the CHECK is about .gitattributes
# coverage, not about the running host.
shell: bash
run: python3 scripts/check-fixture-line-endings.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."