Skip to content

feat(memory): injection receipts + blame path (T1+T2+T3, decision 4255039) #713

feat(memory): injection receipts + blame path (T1+T2+T3, decision 4255039)

feat(memory): injection receipts + blame path (T1+T2+T3, decision 4255039) #713

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
name: Test (Python ${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]
env:
DATABASE_URL: postgresql://cortex:cortex@localhost:5432/cortex
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
# Provision PostgreSQL + pgvector on the runner itself, instead of a
# Docker-Hub service container. Anonymous `pgvector/pgvector:pg17` pulls
# from registry-1.docker.io are rate-limited and outage-prone (observed:
# "context deadline exceeded" failing container init before any test ran,
# CI run 27190427877). The runner ships PostgreSQL preinstalled; pgvector
# comes from the PGDG apt repo (apt.postgresql.org) — no registry, no pull
# rate limit. source: runner image (actions/runner-images) + PGDG.
- name: Set up PostgreSQL + pgvector (runner-local, no registry pull)
run: |
set -euxo pipefail
sudo systemctl start postgresql
PG_VER="$(pg_lsclusters -h | awk 'NR==1 {print $1}')"
# Ensure the PGDG apt repo (canonical pgvector source); idempotent.
sudo install -d /usr/share/postgresql-common/pgdg
sudo curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc \
-o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc
echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" \
| sudo tee /etc/apt/sources.list.d/pgdg.list
for i in 1 2 3; do sudo apt-get update && break || sleep 5; done
for i in 1 2 3; do sudo apt-get install -y "postgresql-${PG_VER}-pgvector" && break || sleep 5; done
# Role + DB expected by DATABASE_URL.
sudo -u postgres psql -v ON_ERROR_STOP=1 \
-c "CREATE ROLE cortex LOGIN SUPERUSER PASSWORD 'cortex';"
sudo -u postgres createdb -O cortex cortex
sudo -u postgres psql -v ON_ERROR_STOP=1 -d cortex \
-c "CREATE EXTENSION IF NOT EXISTS vector; CREATE EXTENSION IF NOT EXISTS pg_trgm;"
# Wait until reachable over TCP with password auth.
for i in $(seq 1 30); do
PGPASSWORD=cortex pg_isready -h localhost -U cortex -d cortex && break || sleep 1
done
PGPASSWORD=cortex psql -h localhost -U cortex -d cortex -c "SELECT version();"
- name: Cache pip
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('pyproject.toml') }}
restore-keys: |
${{ runner.os }}-pip-${{ matrix.python-version }}-
- name: Cache HuggingFace models
uses: actions/cache@v4
with:
path: ~/.cache/huggingface
key: ${{ runner.os }}-hf-all-MiniLM-L6-v2
- name: Install dependencies
run: pip install -e ".[dev,postgresql,codebase]"
# Populate the HuggingFace cache before the (offline) test run. A transient
# huggingface.co blip must not leave the cache empty — that surfaced as 58
# spurious "couldn't connect to huggingface.co" test failures on a single
# matrix leg (CI run 28495801728, Python 3.10, 2026-07-01) while every other
# leg was fine. Retry with backoff so a blip self-heals; fail loudly here (no
# continue-on-error) instead of cascading into a misleading test failure.
- name: Pre-download embedding model
run: |
for attempt in 1 2 3 4 5; do
python -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('all-MiniLM-L6-v2', device='cpu')" && exit 0
echo "HF pre-download attempt ${attempt} failed; retrying in $((attempt * 10))s" >&2
sleep $((attempt * 10))
done
echo "HF pre-download failed after 5 attempts" >&2
exit 1
# Offline: the model is already cached by the step above, so tests must never
# reach out to huggingface.co mid-suite — deterministic and flake-free.
- name: Run tests
env:
HF_HUB_OFFLINE: "1"
TRANSFORMERS_OFFLINE: "1"
run: pytest --tb=short -q
- name: Run tests with coverage
if: matrix.python-version == '3.12'
env:
HF_HUB_OFFLINE: "1"
TRANSFORMERS_OFFLINE: "1"
run: pytest --cov=mcp_server --cov-report=xml --cov-report=term-missing
- name: Upload coverage
if: matrix.python-version == '3.12'
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage.xml
test-sqlite:
name: Test (SQLite backend)
runs-on: ubuntu-latest
# Force the SQLite fallback path — no PostgreSQL installed or started.
# The conftest detects PG-unreachable and sets CORTEX_MEMORY_STORE_BACKEND=sqlite
# automatically; this env var makes the selection explicit and observable in logs.
# source: mcp_server/infrastructure/memory_store.py _construct_store() — the
# 'sqlite' backend branch is always reachable without PG; conftest.py line 99
# mirrors this override when _USE_PG is False.
env:
CORTEX_MEMORY_STORE_BACKEND: sqlite
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Cache pip
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-3.12-sqlite-${{ hashFiles('pyproject.toml') }}
restore-keys: |
${{ runner.os }}-pip-3.12-sqlite-
- name: Cache HuggingFace models
uses: actions/cache@v4
with:
path: ~/.cache/huggingface
key: ${{ runner.os }}-hf-all-MiniLM-L6-v2
- name: Install dependencies (no postgresql extra)
run: pip install -e ".[dev,sqlite]"
# Retry-with-backoff, fail-loudly: see the `test` job's pre-download step
# for the root-cause rationale (CI run 28495801728, 2026-07-01).
- name: Pre-download embedding model
run: |
for attempt in 1 2 3 4 5; do
python -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('all-MiniLM-L6-v2', device='cpu')" && exit 0
echo "HF pre-download attempt ${attempt} failed; retrying in $((attempt * 10))s" >&2
sleep $((attempt * 10))
done
echo "HF pre-download failed after 5 attempts" >&2
exit 1
- name: Run SQLite backend tests
env:
HF_HUB_OFFLINE: "1"
TRANSFORMERS_OFFLINE: "1"
# Scope: the SQLite fallback is intentionally NOT at full feature parity
# with the mandatory PostgreSQL backend (some SqliteMemoryStore methods
# and PG-specific tests do not apply). Run the dedicated SQLite backend
# suite, which exercises CRUD, heat_base columns/indexes, FTS, and
# backend selection on the fallback path. Broadening to the full suite
# on SQLite is tracked separately (full-parity effort).
run: pytest tests_py/infrastructure/test_sqlite_backend.py --tb=short -q
test-windows:
name: Test (Windows, SQLite backend)
runs-on: windows-latest
# Real NT proof for the cross-platform fixes (fcntl→msvcrt, sys.executable,
# NTFS exec bits, $HOME override, path separators). We use the SQLite
# fallback so no PostgreSQL has to be provisioned on the Windows runner —
# the conftest selects it when PG is unreachable; the env var makes it
# explicit. source: RAPPORT_INSTALLATION_CORTEX_WINDOWS.md §10 (CI Windows)
env:
CORTEX_MEMORY_STORE_BACKEND: sqlite
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Cache HuggingFace models
uses: actions/cache@v4
with:
path: ~/.cache/huggingface
key: ${{ runner.os }}-hf-all-MiniLM-L6-v2
- name: Install dependencies (no postgresql extra)
run: pip install -e ".[dev,sqlite]"
# Import smoke: the modules that previously crashed at load on Windows
# (fcntl import) or silently misbehaved. If any fails to import, the
# platform branches are wrong — fail fast before the suite.
- name: Import smoke (formerly Windows-broken modules)
run: >-
python -c "import mcp_server.shared.platform,
mcp_server.infrastructure.pipeline_install_lock,
mcp_server.infrastructure.pipeline_discovery,
mcp_server.core.staleness, mcp_server.doctor, mcp_server.doctor_mcp;
print('windows import smoke OK')"
# Retry-with-backoff, fail-loudly: see the `test` job's pre-download step
# for the root-cause rationale (CI run 28495801728, 2026-07-01). shell: bash
# so the retry loop runs under Git Bash rather than the Windows default pwsh.
- name: Pre-download embedding model
shell: bash
run: |
for attempt in 1 2 3 4 5; do
python -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('all-MiniLM-L6-v2', device='cpu')" && exit 0
echo "HF pre-download attempt ${attempt} failed; retrying in $((attempt * 10))s" >&2
sleep $((attempt * 10))
done
echo "HF pre-download failed after 5 attempts" >&2
exit 1
# Scope (explicit, not silent): the portability tests plus the modules
# carrying Windows-specific branches and the SQLite backend suite. The
# full PG suite is not run here — it is covered by the ubuntu `test` job.
- name: Run portability + backend tests
env:
HF_HUB_OFFLINE: "1"
TRANSFORMERS_OFFLINE: "1"
run: >-
pytest --tb=short -q
tests_py/shared/test_platform.py
tests_py/infrastructure/test_pipeline_discovery.py
tests_py/infrastructure/test_pipeline_install_lock.py
tests_py/core/test_staleness.py
tests_py/infrastructure/test_sqlite_backend.py
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install ruff
run: pip install ruff
- name: Check formatting
run: ruff format --check .
- name: Check linting
run: ruff check .
typecheck:
name: Type Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"
# Pyright resolves third-party imports from ./.venv (pinned in
# pyrightconfig.json: venvPath="."/venv=".venv"). The full stub set MUST
# be installed or every import collapses to Unknown — and Unknown
# SUPPRESSES downstream type errors, making the baseline meaningless.
# source: measured 2026-06-18 — an unresolved env reports 566 errors, a
# fully-resolved env reports 593 (Unknown was masking 27+ real errors).
# flashrank (core reranker) + sqlite-vec live outside dev/postgresql/codebase.
- name: Create .venv with the full type-check environment
run: |
python -m venv .venv
.venv/bin/pip install -U pip
.venv/bin/pip install -e ".[dev,postgresql,sqlite,codebase]" flashrank
# Pin pyright — diagnostic output drifts between releases, so the
# committed baseline is only comparable against the pinned version.
.venv/bin/pip install pyright==1.1.410
# The ratchet IS the gate. pyright always exits non-zero while the
# 568-error backlog stands, so its own exit is ignored (|| true); the
# build fails only when a --blocking rule regresses past its committed
# baseline (0). Add rules to --blocking as each batch drives them to
# their floor — see docs/provenance/pyright-remediation-plan.md.
- name: Run pyright + per-rule ratchet gate
run: |
.venv/bin/python -m pyright --outputjson mcp_server/ > pyright-current.json || true
.venv/bin/python scripts/check_pyright_ratchet.py \
pyright-current.json typecheck-baseline.json \
--blocking reportOptionalMemberAccess reportOptionalSubscript
build:
name: Build Package
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install build tools
run: pip install build
- name: Build sdist and wheel
run: python -m build
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/