Skip to content

feat(schema,scip): PR#10 slice 1 -- reference_evidence dual-write (ev… #291

feat(schema,scip): PR#10 slice 1 -- reference_evidence dual-write (ev…

feat(schema,scip): PR#10 slice 1 -- reference_evidence dual-write (ev… #291

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
verify:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
with:
components: clippy, rustfmt
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
- name: Format check
run: cargo fmt --all -- --check
- name: Clippy
run: cargo clippy --workspace --all-targets -- -D warnings
- name: Test
run: cargo test --workspace
- name: Audit
run: cargo install cargo-audit --locked && cargo audit
# Dogfoods action.yml against this repo's own PRs -- proves the
# published-npm-package + composite-action path actually works end to
# end, not just that `cargo test` passes against the Rust source directly.
# continue-on-error: shadow mode while this is new (matches this repo's
# own rollout convention for new gates -- elicit_hub_confirm, verification,
# etc. all default off/observe-only at first).
#
# Audit 11.4 (2026-08-06): reviewed this job's run history before
# considering promotion. Found two real infra failure modes, not false
# positives from the guard's own risk analysis -- neither is "the gate
# doing its job":
# 1. `error: unrecognized subcommand 'guard'` -- ran against a commit
# from before this job's build-from-source+alias steps existed
# (older npm-install-only version of this job). Self-resolved by
# the current job definition; not expected to recur.
# 2. `fatal: origin/main...HEAD: no merge base` -- action.yml's
# shallow-fetch fix only fetched the base ref at depth=1, sharing no
# history with this checkout's own depth=1 HEAD. Fixed in the same
# commit as this comment (action.yml's "Resolve review scope" step
# now fetches depth=100 + falls back to `--unshallow`).
# Promotion criteria (remove `continue-on-error` once ALL of these hold):
# - 10 consecutive PRs where this job actually ran (not skipped) with
# zero infra-category failures (subcommand/merge-base/install/
# timeout) -- a real "aggregate_risk >= fail-on" block is NOT an
# infra failure and doesn't reset this count.
# - At least one of those 10 was a PR this job correctly blocked or
# would have blocked (fail-on triggered), confirmed by manual review
# to be a genuine high-risk change, not a false positive.
# - No open false-positive report against calm guard's risk model
# older than 7 days.
calm-guard-dogfood:
runs-on: ubuntu-latest
timeout-minutes: 15
continue-on-error: true
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
# Full history, not action.yml's own best-effort shallow-fetch
# fallback -- this job is specifically what proves the merge-base
# calculation is correct, so it shouldn't lean on the same
# fallback it's meant to be testing.
fetch-depth: 0
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
# Dogfooding must exercise THIS commit's own calm-cli, not whatever
# was last published to npm -- action.yml's default `version: latest`
# install can lag a real subcommand by days (found for real: `guard`
# landed in 9773d41, after the v0.5.0 npm tag, so the plain npm-install
# path 404'd on `error: unrecognized subcommand 'guard'` here until
# this fix). Building from source and aliasing it as `calm-mcp` on
# PATH, then passing skip-install: true, tests the actual wiring this
# PR/push is changing instead of a stale release.
- name: Build calm-cli from source (this commit, not the last npm release)
run: cargo build -p calm-cli
- name: Alias the freshly built binary as calm-mcp on PATH
run: |
mkdir -p "$HOME/.local/bin"
ln -sf "$GITHUB_WORKSPACE/target/debug/calm" "$HOME/.local/bin/calm-mcp"
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- uses: ./
with:
skip-install: true
# No `base`/`commits` input -- exercises action.yml's own
# pull_request/push auto-detection, not an explicit override.
stack-graphs-corpus:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
- name: Run Stack Graphs Regression Corpus
run: cargo test --test parity_test test_formal_edges -- --nocapture
# WS-1 task 4.8 crash-injection suite (docs/plans/2026-08-02-phase1-p0-
# execution-plan.md §6 milestone gate item 1: "kill -9 tại mọi giá trị
# TxState, ≥100 lần mỗi transition, 0 trường hợp disk thay đổi mà không
# có tx_events row tương ứng"). `#[ignore]`d in the normal test binary
# (300 real subprocess spawns is too slow for routine `cargo test
# --workspace`) -- this job is the dedicated place it actually runs on
# every push/PR to main, Linux, matching the milestone's own platform.
txn-crash-injection:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
- name: Crash-inject every reachable TxState transition (100x each, real SIGKILL)
run: cargo test -p calm-cli --test txn_crash_injection -- --ignored
embeddings:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
with:
components: clippy
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
# The `embeddings` feature (model2vec-rs). The default model's weights
# are fetched from HuggingFace Hub and checksum-verified by
# crates/calm-core/build.rs::ensure_embedding_weights at compile time
# (2026-07-12 — used to be vendored via Git LFS, which exhausted the
# GitHub account's LFS bandwidth budget; see
# docs/superskills/specs/2026-07-12-edge-release-binary-distribution.md).
# No special checkout step needed here any more — build.rs handles it,
# and degrades to a placeholder (never fails the build) if the fetch
# doesn't succeed, same as before.
- name: Clippy (embeddings)
run: cargo clippy -p calm-core --all-targets --features embeddings -- -D warnings
- name: Test (embeddings)
run: cargo test -p calm-core --features embeddings
no-stack-graphs-formal:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
with:
components: clippy
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
# D1 (2026-07-30 stack-graphs-demotion-lever): the ONLY dependency
# chain in the whole workspace that pins core `tree-sitter` to "^0.24"
# (verified via `cargo tree -i -p tree-sitter@0.24.7` -- real precedent
# for why this matters: Perl was dropped from the 25-language list
# (2026-07-10) for exactly this reason, see
# docs/superskills/plans/2026-07-10-25-language-expansion.md §1.4).
# `resolver::formal` compiles to a same-API stub without this feature
# (crates/calm-core/src/resolver/mod.rs) -- this job is the only CI
# coverage of that stub actually compiling and behaving correctly
# (`cargo check` alone can silently bit-rot a cfg branch nothing else
# ever builds). Keeps every OTHER default feature on
# (embeddings/tier0-5/scip-overlay) so this isolates JUST the
# stack-graphs-formal absence, not a combination of several features
# being off at once.
- name: Clippy (no stack-graphs-formal)
run: cargo clippy -p calm-core --all-targets --no-default-features --features embeddings,tier0-5,scip-overlay -- -D warnings
- name: Test (no stack-graphs-formal)
run: cargo test -p calm-core --no-default-features --features embeddings,tier0-5,scip-overlay
- name: Verify tree-sitter core is actually unpinned (not just a feature flag on paper)
run: |
set -e
LEAKED=$(cargo tree -p calm-core --no-default-features --features embeddings,tier0-5,scip-overlay 2>&1 | grep -ic "stack-graph" || true)
if [ "$LEAKED" -ne 0 ]; then
echo "::error::stack-graphs family still present in the dependency tree without stack-graphs-formal -- the feature gate is not actually cutting the dependency"
exit 1
fi
all-languages:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
with:
components: clippy
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
# The 11 Phase B/C languages (kotlin/swift/scala/dart/lua/elixir/
# haskell/ocaml/zig/powershell/groovy) are deliberately NOT in the
# `tier0-5` default bundle (opt-in only until proven stable — see
# calm-core/Cargo.toml's own comment on `lang-kotlin`), which means
# `verify`'s plain `cargo test --workspace` above NEVER compiles or
# tests a single line of their code — `lang_feature_flags_match_*`
# (crates/calm-core/tests/lang_feature_parity.rs) only checks that the
# Cargo.toml *declarations* line up across the 3 crates, not that the
# actual grammar-gated code builds/passes. This job is the real
# coverage: same "all 11 new lang-* features enabled together"
# combination first run by hand at the end of the 2026-07-10/11
# 25-language-expansion session (786 passed then) — promoted to a
# permanent CI job instead of staying a one-off manual check. Bundled
# with `lsp-overlay` too: the D.0-D.4 LspProvider generalization
# (2026-07-11) is also gated behind a non-default feature with no
# other CI coverage.
- name: Clippy (all languages + lsp-overlay)
run: cargo clippy --workspace --all-targets --features tier0-5,lang-kotlin,lang-swift,lang-scala,lang-dart,lang-lua,lang-elixir,lang-haskell,lang-ocaml,lang-zig,lang-powershell,lang-groovy,scip-overlay,lsp-overlay -- -D warnings
- name: Test (all languages + lsp-overlay)
run: cargo test --workspace --features tier0-5,lang-kotlin,lang-swift,lang-scala,lang-dart,lang-lua,lang-elixir,lang-haskell,lang-ocaml,lang-zig,lang-powershell,lang-groovy,scip-overlay,lsp-overlay
# Real cross-SDK MCP interop check (2026-07-14 upgrade item, ported from
# modelcontextprotocol/rust-sdk's own crates/rmcp/tests/test_with_js.rs —
# verified against that real source before porting). Every OTHER test in
# this repo (the `verify`/`all-languages` jobs above, the bash hook
# suite) drives `calm serve` with rmcp's own client code — same SDK,
# same author, on both sides of the wire, so a protocol-shape bug rmcp's
# own client happens to tolerate would never surface. This job is the
# other half: the official TypeScript MCP SDK (@modelcontextprotocol/sdk,
# a genuinely independent implementation) drives a real `calm serve`
# child process over real stdio and validates `tools/list` +
# `tools/call` end-to-end, including strict structuredContent-vs-
# outputSchema validation the TS SDK enforces that rmcp's own test
# client does not — this is exactly what caught a real schema/impl
# mismatch bug (`weak_cross_reference_languages` and 13 sibling fields
# missing `#[serde(default)]`) the very first time this check ran.
js-client-interop:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
- name: Build calm binary
run: cargo build --bin calm
# ubuntu-latest already ships a recent Node.js + npm (same assumption
# release.yml's npm-publish job already makes) — no actions/setup-node
# needed.
- name: Install JS interop test dependencies
working-directory: tests/js_client_interop
run: npm ci
- name: Run cross-SDK MCP interop check
working-directory: tests/js_client_interop
run: node client.mjs "$GITHUB_WORKSPACE/target/debug/calm" "$GITHUB_WORKSPACE/crates/calm-core/tests/fixtures/rust_workspace"
# Feature-matrix coverage for the 2026-07-28 OTel/HTTP upgrade
# (docs/superskills/plans/2026-07-28-otel-http-dynamic-toolsets.md) --
# `verify`'s plain `cargo build/test --workspace` above never compiles a
# single line of otel.rs/http.rs, same gap `all-languages` closes for the
# opt-in language features.
#
# The single-otel-version guard below locks in the audit's A2 finding
# (opentelemetry Rust crates version-skew: a bare `cargo add` to latest
# resolves opentelemetry to two coexisting core versions, silently
# broken) so a future dependency bump can't reintroduce it unnoticed.
# Uses `cargo metadata | jq` on the exact package name, NOT a `cargo
# tree | grep` substring match: a bare `grep -c 'opentelemetry v0.3[^1]'`
# (no tree-connector anchor) false-positive-matches the *substring*
# inside `tracing-opentelemetry v0.32.1` -- verified live 2026-07-28,
# that naive pattern reports count=1 even in the correctly-pinned,
# healthy state, which would make this job permanently red. `jq`
# filtering on `.name == "opentelemetry"` operates on cargo's own
# structured package list, so it can't confuse a real second core with
# an unrelated crate whose name happens to contain the same substring.
otel-http-features:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
with:
components: clippy
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
- name: Clippy (otel)
run: cargo clippy -p calm-cli --features otel -- -D warnings
- name: Test (otel)
run: cargo test -p calm-cli --features otel
- name: Guard against opentelemetry core version skew (locks in audit A2 finding)
run: |
VERSIONS=$(cargo metadata --format-version 1 --features otel \
| jq -r '.packages[] | select(.name == "opentelemetry") | .version' | sort -u)
COUNT=$(printf '%s\n' "$VERSIONS" | grep -c .)
echo "Resolved opentelemetry core version(s): $VERSIONS"
if [ "$COUNT" -ne 1 ]; then
echo "::error::Expected exactly ONE resolved 'opentelemetry' core version, found $COUNT: $VERSIONS"
exit 1
fi
if ! printf '%s\n' "$VERSIONS" | grep -q '^0\.32\.'; then
echo "::error::Resolved opentelemetry core is $VERSIONS, expected the pinned 0.32.x line -- update crates/calm-cli/src/otel.rs's version-note comment and Cargo.toml's pin comment together if this is an intentional bump"
exit 1
fi
- name: Clippy (http)
run: cargo clippy -p calm-server -p calm-cli --features http -- -D warnings
- name: Test (http)
run: cargo test -p calm-server -p calm-cli --features http
# Wires `calm fitness-check` (README: "CI gate, exits 1 on failure") into
# actual CI — previously documented as a CI gate but never invoked by any
# workflow, so a declared `[[boundaries]]`/threshold violation could sit
# "over gate" indefinitely without ever failing a build. Runs `calm index`
# first since fitness-check reads `import_edges`/`symbols` from this repo's
# own `.calm/index.db`, which a fresh checkout doesn't have yet.
fitness-check:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
- name: Build calm binary
run: cargo build --bin calm
- name: Index this repo
run: ./target/debug/calm index --project-root .
- name: Fitness check
# --config is NOT optional despite the CLI's "uses defaults if not
# provided" framing: the numeric thresholds do have real defaults,
# but [[boundaries]]/[config_drift] have no conceptual default
# (an empty rule set) — omitting --config silently skips checking
# this repo's own declared boundary rules entirely rather than
# erroring, verified locally (`no [[boundaries]] rules declared`).
run: ./target/debug/calm fitness-check --project-root . --config thresholds.toml
# WS-13 (docs/plans/2026-08-01-calm-adopt-from-vheatm-plan.md P3-2): fails
# the build if docs/status.generated.md (scripts/gen-status.sh) has drifted
# from the tool schemas / Cargo features it's generated from — the same
# class of doc drift ("pilot" README vs "complete" registry) VHEATM's own
# repo is currently carrying. No Rust build needed here, just jq + bash.
status-drift:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Check docs/status.generated.md is not stale
run: ./scripts/gen-status.sh --check
# 2026-08-03 (docs/plans/2026-08-03-product-truth-closure-plan.md):
# gen-status.sh --check above only protects status.generated.md
# itself -- README/AGENTS.md/llms.txt/marketplace.json/plugin.json/
# workflow.rs are hand-authored and drifted from it even after it
# was regenerated correctly for the same tool-count bump.
- name: Check hand-authored docs match the generated tool count
run: ./scripts/check-doc-truth.sh
- name: Check benchmark claims registry is structurally consistent
run: ./scripts/check-claims-registry.sh