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
61 changes: 61 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: CI

on:
push:
branches:
- main
- feat/**
pull_request:
branches:
- main
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
quality:
name: Python ${{ matrix.python-version }}
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
python-version:
- "3.12"

steps:
- name: Checkout
uses: actions/checkout@v6

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true

- name: Sync dependencies
run: uv sync --frozen

- name: Ruff
run: uv run ruff check src tests examples migrations

- name: Mypy
run: uv run mypy src

- name: Tests
run: uv run pytest -q

- name: Full project CI gate
run: make ci

- name: Deterministic SkillRewind demo
run: make demo
56 changes: 56 additions & 0 deletions .github/workflows/postgres.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: PostgreSQL Integration

on:
pull_request:
branches:
- main
push:
branches:
- main
workflow_dispatch:

permissions:
contents: read

jobs:
postgres:
runs-on: ubuntu-latest

services:
postgres:
image: postgres:17
env:
POSTGRES_USER: skillrewind
POSTGRES_PASSWORD: skillrewind_test
POSTGRES_DB: skillrewind_test
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U skillrewind -d skillrewind_test"
--health-interval 10s
--health-timeout 5s
--health-retries 5

env:
SKILLREWIND_TEST_POSTGRES_URL: >-
postgresql+psycopg://skillrewind:skillrewind_test@localhost:5432/skillrewind_test

steps:
- name: Checkout
uses: actions/checkout@v6

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true

- name: Sync dependencies
run: uv sync --frozen

- name: Run PostgreSQL integration tests
run: uv run pytest -q
41 changes: 41 additions & 0 deletions .github/workflows/spec-contract.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Specification Contract

on:
pull_request:
branches:
- main
push:
branches:
- main

permissions:
contents: read

jobs:
contract:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v6

- uses: actions/setup-python@v5
with:
python-version: "3.12"

- uses: astral-sh/setup-uv@v6
with:
enable-cache: true

- run: uv sync --frozen

- name: Generate OpenAPI
run: make openapi

- name: Verify generated contract is committed
run: git diff --exit-code docs/openapi-v1.json

- name: Conformance self-test
run: make conformance-self-test

- name: Spec tests
run: uv run pytest tests/spec tests/conformance -q
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,26 @@ All notable changes to this project are documented here. Format loosely follows
- SSE ordering/resumability is proven at the `JobQueue.events(after_event_id=...)` level (what the SSE endpoint itself polls), not via a full streaming HTTP client test.
- The 40+-item failure/security/policy-mode test matrix in the milestone spec is covered by a representative subset (documented above), not exhaustively enumerated.

### Added — Phase C2.4: core hardening, API freeze, and integration contract
- New standalone read API: `GET /api/v1/rebuilds/{id}[/support|/exclusions|/output|/verification]`, `GET /api/v1/revocations/{id}/rebuilds`, `GET /api/v1/verifications/{id}[/checks|/safety|/utility|/integrity]` — closes the Phase C2.3 "reduced-depth" gap above; safety/utility/integrity are never collapsed into one boolean.
- Waiver semantics fixed to be a true dynamic policy overlay (closes the other Phase C2.3 "reduced-depth" gap above): `create_waiver` no longer mutates `artifact.status`/the quarantine entry; `resolve_alias` (both Lite and Service mode) and `rebuild.planner.plan_rebuild` now dynamically evaluate active, correctly-scoped, unexpired, unrevoked waivers on every call. Canonical scopes `serving`/`rebuild-support` (with `quarantine-release`/`quarantine` accepted as backward-compatible aliases for `serving`).
- Real end-to-end HTTP SSE resume test (`tests/integration/test_sse_resume_http.py`) against a genuine `uvicorn` socket — discovered along the way that `httpx.ASGITransport`/`TestClient` cannot test a truly open-ended SSE stream at all (it buffers the full response until the ASGI coroutine returns), which is why this test uses a real server thread instead.
- `docs/integration-contract-v1.md`, `docs/api-stability-v1.md`, `docs/event-contract-v1.md`, `docs/threat-model.md`, `docs/release-readiness-v0.3.md`: the public integration contract, endpoint stability categorization, canonical event envelope, trust-boundary analysis, and honest component-status table.
- `docs/openapi-v1.json`, generated from the live FastAPI app via `make openapi` / `skillrewind openapi-export`; staleness-checked by `tests/unit/test_openapi_not_stale.py`.
- `skillrewind.conformance` (`describe()`/`run_self_test()`) + `skillrewind conformance describe`/`self-test` CLI commands: machine-readable Level 1/2/3 contract requirements and a local proof the Service-mode API satisfies its own contract.
- `skillrewind.adapters.protocols` (6 reference `Protocol`s: `ArtifactProvider`, `DerivationProvider`, `ResolutionEnforcer`, `ReplayProvider`, `RebuildProvider`, `EventConsumer`, with no internal-class coupling) + `skillrewind.adapters.reference.InMemoryReferenceAdapter`.
- `tests/smoke/clean_install_smoke.py` / `make clean-install-smoke`: builds a real wheel and installs it into a fresh `uv`-managed venv outside the source tree, then exercises import/CLI/Lite/Service-instantiate/API flow.
- `tests/integration/test_public_contract_freeze_e2e.py`: the full poisoned-descendant workflow driven only through public `stable-v1` HTTP endpoints (the one exception being running the durable job worker, an operational/deployment action with no public "process next job" endpoint).
- `tests/integration/test_safety_invariants_c24.py`, `tests/integration/test_waiver_semantics_c24.py`: targeted additions to the critical safety test matrix (concurrent duplicate ingest, CAS corruption surfacing through the API, API-key-never-leaked, Problem Details shape stability, cross-actor read-isolation model, duplicate-relation idempotency, waiver expiry/revocation/restart-persistence/concurrent-resolution/rebuild-support-scope interaction).
- Version bumped to `0.3.0a1` (`pyproject.toml`, `CITATION.cff`); README and SECURITY.md repositioned for the v0.3 alpha integration-preview scope.

### Fixed — Phase C2.4
- `LocalCAS.get_bytes()`/`open_stream()` never verified the digest of bytes read from disk against the requested `digest_hex` on the normal read path (only the separate `verify_integrity()` did) — `GET /api/v1/artifacts/{id}/content` silently served corrupted/tampered CAS content as `200 OK`. Fixed to hash on every read and raise `CASIntegrityError` on mismatch, surfaced by the API as a `500 Integrity Error` Problem Details response.
- `find_by_alias` (both Lite and Service `ArtifactRepository`) filtered `status = 'active'` at the SQL level, which made the waiver-overlay fix above impossible (a quarantined artifact could never be found by alias regardless of an active waiver). Broadened to `status IN ('active', 'quarantined')`; the actual eligibility decision is entirely `resolve_alias`'s.

### Noted, not fixed this milestone
- `skillrewind.lineage.candidates.recover_candidates` (used internally by `run_revocation` to re-derive candidates during an actual revocation) and `skillrewind.lineage.service_recovery.run_candidate_recovery` (used by the async `POST /api/v1/lineage/recovery-runs` API) are two independent scoring implementations that can disagree on borderline content — see `docs/release-readiness-v0.3.md` for detail.

## [0.2.0] — Research Preview

### Added
Expand Down
2 changes: 1 addition & 1 deletion CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ authors:
- family-names: "Al-Anqoudi"
given-names: "Faisal Ali Said"
affiliation: "Nuqta Technologies"
version: 0.1.0
version: 0.3.0a1
date-released: 2026-08-09
license: Apache-2.0
abstract: >-
Expand Down
17 changes: 15 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.PHONY: bootstrap bootstrap-service format lint typecheck test test-unit test-integration test-property test-e2e test-security \
schemas bench-smoke demo demo-reset docs paper docker-build docker-smoke db-migrate db-current ci clean
schemas bench-smoke demo demo-reset docs paper docker-build docker-smoke db-migrate db-current ci clean \
openapi openapi-check conformance-self-test clean-install-smoke

VENV := .venv
PY := $(VENV)/bin/python
Expand Down Expand Up @@ -86,7 +87,19 @@ db-current:
fi
$(PY) -c "from skillrewind.persistence.service.engine import build_engine, schema_current; import os; e = build_engine(os.environ['SKILLREWIND_DATABASE_URL']); ok, detail = schema_current(e); print(detail); raise SystemExit(0 if ok else 1)"

ci: lint typecheck test schemas bench-smoke
openapi:
$(VENV)/bin/skillrewind openapi-export --output docs/openapi-v1.json

openapi-check:
$(PY) -m pytest tests/unit/test_openapi_not_stale.py -q

conformance-self-test:
$(VENV)/bin/skillrewind conformance self-test

clean-install-smoke:
$(PY) tests/smoke/clean_install_smoke.py

ci: lint typecheck test schemas bench-smoke openapi-check

clean:
rm -rf build dist *.egg-info src/*.egg-info paper/build paper/rendered paper/rendered-final .runs .skillrewind-demo
Expand Down
Loading
Loading