Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,14 @@ jobs:

- name: Run focused regression suite
env:
SCM_DATA_DIR: /tmp/scm-ci-data-${{ matrix.python-version }}
LLM_PROVIDER: ""
SCM_EMBEDDING_BACKEND: hash
SCM_AUTO_SLEEP_DISABLE: "1"
IDLE_LEARNER_ENABLED: "false"
run: |
pytest \
tests/production \
tests/test_scm_sdk.py \
tests/test_product_runtime_api.py \
tests/test_mcp_contract.py \
Expand Down Expand Up @@ -148,6 +151,7 @@ jobs:
import importlib.resources as resources
from scm import SCMClient, SCMEngine

assert SCMClient.__name__ == "SCMClient"
assert resources.files("src.core").joinpath("locales/en.json").is_file()
assert resources.files("src.api").joinpath("static/app.html").is_file()

Expand Down Expand Up @@ -248,3 +252,4 @@ jobs:
cd sdk/js
npm test
npm pack --dry-run
npm pack --dry-run
Comment on lines 252 to +255
44 changes: 44 additions & 0 deletions .github/workflows/product-qualification.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Product Qualification

on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:

jobs:
release-gate:
name: Adversarial release gate
runs-on: ubuntu-latest
timeout-minutes: 30

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip

- uses: actions/setup-node@v4
with:
node-version: "20"

- name: Install qualification dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]" build twine

- name: Run credential-free product qualification
run: |
python scripts/run_product_qualification.py \
--output product-qualification.json

- name: Upload qualification evidence
if: always()
uses: actions/upload-artifact@v4
with:
name: product-qualification
path: product-qualification.json
if-no-files-found: error
90 changes: 85 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,23 +1,82 @@
name: Publish to PyPI
name: Release to PyPI

on:
push:
tags:
- "v*.*.*"
workflow_dispatch:
inputs:
version:
description: "Version tag (must match pyproject.toml)"
description: "Existing version tag to publish, for example v0.9.2"
required: true
type: string

jobs:
qualify:
name: Product release qualification
runs-on: ubuntu-latest
timeout-minutes: 30

steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.version || github.ref }}

- uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip

- uses: actions/setup-node@v4
with:
node-version: "20"

- name: Install qualification dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]" build twine

- name: Verify tag matches package version
env:
TARGET_VERSION: ${{ github.event_name == 'workflow_dispatch' && inputs.version || github.ref_name }}
run: |
python - <<'PY'
import os
import tomllib

expected = os.environ["TARGET_VERSION"].removeprefix("v")
with open("pyproject.toml", "rb") as handle:
actual = tomllib.load(handle)["project"]["version"]
if actual != expected:
raise SystemExit(f"tag/package mismatch: tag={expected} package={actual}")
PY

- name: Run full credential-free release gate
run: |
python scripts/run_product_qualification.py \
--output product-qualification.json

- name: Upload qualification evidence
if: always()
uses: actions/upload-artifact@v4
with:
name: release-qualification
path: product-qualification.json
if-no-files-found: error

publish:
name: Publish to PyPI
runs-on: ubuntu-latest
needs: qualify
environment: pypi-release

permissions:
id-token: write

steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.version || github.ref }}

- name: Set up Python 3.11
uses: actions/setup-python@v5
Expand All @@ -39,6 +98,27 @@ jobs:

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
# Manual until PyPI trusted publishing is configured for:
# repo=clyrai/SCM_OpenSource, workflow=.github/workflows/release.yml,
# environment=pypi-release.

github-release:
name: Create GitHub Release
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
needs: publish

permissions:
contents: write

steps:
- uses: actions/checkout@v4

- name: Create release notes from CHANGELOG
run: |
VERSION="${GITHUB_REF_NAME#v}"
awk "/^## v${VERSION}/{flag=1; next} /^## /{flag=0} flag" CHANGELOG.md > release_notes.md

- name: Create GitHub release
uses: softprops/action-gh-release@v2
with:
body_path: release_notes.md
draft: false
prerelease: false
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,4 @@ railway.json
research/metrics/
research/reproducibility/
research/benchmarks/
quality/reports/
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,24 @@ Format: each release lists what shipped, why it shipped, what tests verified it,
- Documented the optional `scm-memory[llm]` extra required for OpenAI or other
OpenAI-compatible providers, while preserving the no-key local first run.
- Exposed `scm.__version__` for normal SDK version checks.
- Added checksummed atomic per-user REST/MCP snapshots, restart restoration,
corrupt-state quarantine, same-user operation serialization, bounded queues,
and graceful pool draining on shutdown.
- Tightened the public API with strict request schemas, payload limits, safe
CORS defaults, real OpenAPI paths for the five canonical tools, and encrypted
BYOK storage with legacy-read compatibility.
- Added a credential-free release qualification runner and 46 deterministic
production tests covering abuse inputs, concurrency, durability, recovery,
contract parity, security, and bounded latency.

### Verification

- Added regression coverage for from-to contradiction detection, explicit
transition versioning, and SDK schema extraction after sleep.
- Re-ran the paper's compact `gpt-5.4-mini` lifecycle scenario against an
installed wheel after the behavior fixes.
- Full clean-wheel qualification validates the CLI, demo, REST contract,
server restart durability, JavaScript SDK, dependency audit, and secret scan.

---

Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,19 @@ scm mcp

Use it with Claude Desktop, Cursor, VS Code, or any MCP-compatible agent.

## Release Qualification

Before publishing a package, run the credential-free product gate:

```bash
venv/bin/python scripts/run_product_qualification.py
```

It runs the full regression suite, builds and installs the wheel outside the
repository, verifies REST state survives a server restart, and exercises the
live JavaScript SDK. The exact release contract is in
[docs/QUALITY_GATES.md](docs/QUALITY_GATES.md).

## Optional Provider Quality

SCM does not require a cloud key for the local-first path. When you want
Expand Down
Loading
Loading