Skip to content

check_mcp_refs: ide is supplied by the editor, not by a config file #10

check_mcp_refs: ide is supplied by the editor, not by a config file

check_mcp_refs: ide is supplied by the editor, not by a config file #10

Workflow file for this run

name: ci
on:
push:
pull_request:
jobs:
contracts:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
# Every registered check must prove it can fail. This also runs each
# tool's own --selftest, so the negative controls are exercised on CI
# rather than only on the author's machine.
- name: selftest contract
run: python3 selftest_gate.py
# The README declares its own load-bearing claims; verify each anchor
# still exists in the text and the budget is respected.
- name: README doc contract
run: python3 doc_contract.py --document README.md
# A gate that has never been observed to fail is the thing this repo is
# about. Prove the two exit-1 paths on CI, not just in the selftests.
- name: prove doc_contract rejects a violation
run: |
cat > /tmp/bad.md <<'MD'
# x
<!-- load-bearing:begin -->
## L
| # | a | b |
|---|---|---|
| 1 | `absent from body` | boom |
<!-- load-bearing:end -->
body
MD
sed -i 's/^ //' /tmp/bad.md
# Nonzero is not the claim. Measured: a SyntaxError in the tool exits
# 1 and a typo'd flag exits 2 -- both used to print "rejected as
# expected" here, which is the shape of failure this repository is
# about.
set +e
out=$(python3 doc_contract.py --document /tmp/bad.md 2>&1); rc=$?
set -e
printf '%s\n' "$out"
if [ "$rc" -eq 0 ]; then
echo "::error::doc_contract accepted a missing anchor"; exit 1
fi
if [ "$rc" -ne 1 ]; then
echo "::error::doc_contract exited $rc, not 1 -- a crash or a usage error is not a rejection"; exit 1
fi
printf '%s\n' "$out" | grep -Fq "anchor not present in document: 'absent from body'" || {
echo "::error::doc_contract exited 1, but not on the missing-anchor rule this step exists to prove"; exit 1; }
echo "rejected as expected, by the missing-anchor rule"
- name: prove selftest_gate rejects a silent check
run: |
mkdir -p /tmp/g && printf 'import sys\nif "--selftest" in sys.argv: sys.exit(0)\n' > /tmp/g/silent.py
# `documented_samples` is required by load_registry, so the fixture
# declares it. Without it this registry is rejected at LOAD time and
# never reaches the rule this step exists to prove -- which is exactly
# what happened on 2026-08-02 and is why the grep below is on the rule
# rather than on the exit code.
printf '{"checks":[{"id":"silent","path":"silent.py","min_controls":1}],"documented_samples":["silent.py"]}\n' > /tmp/g/registry.json
set +e
out=$(python3 selftest_gate.py --registry /tmp/g/registry.json 2>&1); rc=$?
set -e
printf '%s\n' "$out"
if [ "$rc" -eq 0 ]; then
echo "::error::selftest_gate accepted a check that reports no controls"; exit 1
fi
if [ "$rc" -ne 1 ]; then
echo "::error::selftest_gate exited $rc, not 1 -- a crash or a usage error is not a rejection"; exit 1
fi
# Names the check AND the rule. The rule that actually fires on this
# fixture is the missing-summary one, not min_controls, which is what
# the old message claimed.
printf '%s\n' "$out" | grep -Fq '[FAIL] silent: selftest output has no `passed=N failed=M` line' || {
echo "::error::selftest_gate exited 1 for a reason other than the silent check's missing summary"; exit 1; }
echo "rejected as expected, on the missing-summary rule, naming the check"