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
13 changes: 13 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
* text=auto eol=lf
*.py text eol=lf
*.toml text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
*.md text eol=lf
*.bat text eol=crlf
*.cmd text eol=crlf
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
90 changes: 90 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: CI

on:
push:
branches: [main, "claude/**"]
pull_request:
branches: [main]
workflow_dispatch:

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

permissions:
contents: read

jobs:
lint:
name: Lint and format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v3
with:
enable-cache: true
- name: Set up Python
run: uv python install 3.12
- name: Install dependencies
run: uv sync --all-extras --dev
- name: Ruff lint
run: uv run ruff check .
- name: Ruff format check
run: uv run ruff format --check .

typecheck:
name: Typecheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v3
with:
enable-cache: true
- name: Set up Python
run: uv python install 3.12
- name: Install dependencies
run: uv sync --all-extras --dev
- name: Mypy
run: uv run mypy src tests

test:
name: Test py${{ matrix.python }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python: ["3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v3
with:
enable-cache: true
- name: Set up Python
run: uv python install ${{ matrix.python }}
- name: Install dependencies
run: uv sync --all-extras --dev
- name: Pytest with coverage
run: uv run pytest --cov=spine_lite --cov-report=term-missing --cov-report=xml --cov-fail-under=95
- name: Upload coverage
if: matrix.os == 'ubuntu-latest' && matrix.python == '3.12'
uses: actions/upload-artifact@v4
with:
name: coverage-xml
path: coverage.xml
if-no-files-found: error

docs-build:
name: Build docs (strict)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v3
with:
enable-cache: true
- name: Set up Python
run: uv python install 3.12
- name: Install dependencies
run: uv sync --all-extras --dev
- name: Build site
run: uv run mkdocs build --strict
45 changes: 45 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Deploy docs

on:
push:
branches: [main]
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages-${{ github.ref }}
cancel-in-progress: false

jobs:
build:
name: Build site
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v3
with:
enable-cache: true
- name: Set up Python
run: uv python install 3.12
- name: Install dependencies
run: uv sync --all-extras --dev
- name: Build site
run: uv run mkdocs build --strict
- uses: actions/upload-pages-artifact@v3
with:
path: site

deploy:
name: Deploy to GitHub Pages
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v4
46 changes: 46 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
dist/
*.egg-info/
*.egg
.eggs/

# Virtual environments
.venv/
venv/
env/
.nox/
.tox/

# Testing and coverage
.pytest_cache/
.coverage
.coverage.*
coverage.xml
htmlcov/
.hypothesis/

# Type and lint caches
.mypy_cache/
.ruff_cache/

# IDE / editor
.vscode/
.idea/
*.swp
*.swo

# OS
.DS_Store
Thumbs.db

# Docs build output
site/

# uv tool cache (uv.lock IS tracked; see docs/design-rationale.md)
.uv/
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Changelog

All notable changes to this project are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.1.0a0] — 2026-05-08

### Added

- Closed six-class effects taxonomy: `READ`, `WRITE`, `NETWORK`, `EXECUTE`, `SPAWN`, `DESTRUCTIVE`. Canonical precedence ordering and `most_restrictive()` collapse function.
- Public exception hierarchy rooted at `SpineLiteError` with `ManifestError`, `ClassificationError`, `PostureError`, and `HookError` subclasses.
- Scaffolds for `classifier`, `manifest`, `posture`, `receipt`, and `hook` modules — implementations land in Phases 2 and 3.
- `spine-lite` console script with a `version` subcommand.
- CI matrix across Python 3.11/3.12/3.13 on Linux, macOS, and Windows.
- MkDocs documentation with `mkdocstrings`, deployable to GitHub Pages.
- Repo governance file (`CLAUDE.md`) and build-progress receipt log (`RECEIPTS.md`).

[Unreleased]: https://github.com/MacFall7/spine-lite-python/compare/v0.1.0a0...HEAD
[0.1.0a0]: https://github.com/MacFall7/spine-lite-python/releases/tag/v0.1.0a0
91 changes: 91 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# CLAUDE.md

Operating manual for Claude Code sessions in this repo.

## Mission

Python port of M87-Spine-lite (TypeScript). Deterministic policy and effects runtime for LLM tool calls. Public API and observable semantics must mirror the TypeScript reference within the closed six-class taxonomy.

## Authority

You decide:

- File contents, signatures, types, tests, commit messages.
- Refactors that don't change the public API.
- Lint, format, and typecheck rule application.

Mac decides — halt and ask:

- Anything in `src/spine_lite/__init__.py`'s `__all__`.
- New dependencies beyond `pyproject.toml`.
- Phase boundary transitions (1→2, 2→3).
- Semantic divergence from the TypeScript reference.
- PyPI publish, repo visibility, GitHub Pages enablement.

## Architectural invariants

1. Closed effects taxonomy. Six members: `READ`, `WRITE`, `NETWORK`, `EXECUTE`, `SPAWN`, `DESTRUCTIVE`. No additions without project sign-off.
2. Precedence: `DESTRUCTIVE > SPAWN > EXECUTE > NETWORK > WRITE > READ`. `most_restrictive()` returns the highest.
3. Determinism. No timestamps, UUIDs, or randomness in `effects.py`, `classifier.py`, `posture.py`, `manifest.py`, `receipt.py`. Inject entropy where you need it.
4. No I/O in those five modules. I/O lives in `hook.py`, `cli.py`, and tests.
5. `mypy --strict` clean on every commit. No `Any` without a comment justifying it.
6. Public API gate. Anything not in `__all__` is private. Private names start with `_`.
7. This file ≤ 150 lines. Refactor before letting it sprawl.

## Style

- Python 3.11+. `from __future__ import annotations` at the top of every module.
- Frozen, slotted, kw-only dataclasses by default.
- Google-style docstrings on every public symbol.
- `logging.getLogger(__name__)` with a `NullHandler` at package init. Library never configures logging.
- Conventional Commits. Subject ≤ 72 chars, imperative mood, no trailing period.
- Direct prose. No marketing tone, no LLM boilerplate, no performative empathy.

## Verification gates

Before any commit:

```
nox -s lint typecheck test
```

All three green or no commit. Before any push, also `coverage` and `docs`. Coverage ≥ 95% on touched files. Docs build with `--strict`.

## Stop conditions

Halt and report when:

- A phase exit gate item is unclear.
- Python and TS reference diverge semantically and you can't tell which is right.
- A test fails you can't explain in 15 minutes.
- You're about to add a dependency.
- You're about to modify `__all__`.
- Two consecutive CI runs fail on the same root cause.

Halt format:

```
HALT: <one-line reason>
Context: <what surfaced>
Options: <2-3 with tradeoffs>
Recommendation: <pick + reasoning>
Awaiting: <decision needed>
```

## Phase plan

- **Phase 1** — scaffold + CI + docs deploy. Tags `v0.1.0a0`.
- **Phase 2** — `manifest` and `classifier` complete. Pydantic v2 models, parity tests against TS reference fixtures, `hypothesis` for invariants. Tags `v0.2.0a0`.
- **Phase 3** — `posture`, `receipt`, `hook`, `cli` complete. End-to-end PreToolUse integration with Claude Code. Tags `v0.3.0a0`.

Phase exit gates and receipts live in `RECEIPTS.md`.

## Scope

- In-repo only. Don't touch the TypeScript reference (read-only spec).
- Don't invoke Patronus, Braintrust, or Arize SDKs (operator-decision pending).
- No network calls in tests. No LLM calls anywhere in the runtime.

## Run registry

Append to `RECEIPTS.md` after every phase day or major milestone. Never edit prior entries. Mac mirrors entries to the canonical run-registry on his side.
58 changes: 58 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Contributing

This repo is governed under M87 Studio. External PRs are welcome, but the closed six-class effects taxonomy and the public API surface are non-negotiable — proposals to change either need a written rationale on an issue before code lands.

## Local setup

```bash
git clone https://github.com/MacFall7/spine-lite-python
cd spine-lite-python
uv venv
uv sync --all-extras --dev
```

## Verification

Before every commit:

```bash
nox -s lint typecheck test
```

Before every push, also:

```bash
nox -s coverage docs
```

Coverage must stay at or above 95% on every commit and at 100% on the modules a phase implements at its exit gate. Docs build with `--strict`.

## Style

- Python 3.11+. `from __future__ import annotations` at the top of every module.
- `mypy --strict` clean. No `Any` without a comment explaining why.
- Google-style docstrings on every public symbol.
- Frozen, slotted, kw-only dataclasses by default. Mutable only with explicit justification.
- Conventional Commits: `feat:`, `fix:`, `docs:`, `chore:`, `test:`, `refactor:`, `ci:`. Subject ≤ 72 chars, imperative mood, no trailing period.
- Direct prose. No marketing copy, no LLM boilerplate, no performative empathy.

## Architecture rules

The closed six-class effects taxonomy and the precedence ordering are the spec. The five core modules — `effects.py`, `classifier.py`, `posture.py`, `manifest.py`, `receipt.py` — are pure: no I/O, no timestamps, no randomness. I/O lives in `hook.py`, `cli.py`, and tests.

## Tests

`pytest` for everything. `hypothesis` for invariants and determinism. The TypeScript reference fixtures (added in Phase 2) are used as-is — no mocking. No network calls in tests; the runtime is offline by design.

## Reviewing

The build operates under explicit phase gates documented in `CLAUDE.md`. Reviews should focus on:

1. Public API stability (anything in `__all__`).
2. Determinism in the pure modules.
3. Parity with the TypeScript reference where applicable.
4. Test coverage on the modules touched.

## Releases

Versioning is SemVer with explicit phase tags: `0.1.0a0` (Phase 1), `0.2.0a0` (Phase 2), `0.3.0a0` (Phase 3). PyPI publishing is gated on a project-level sign-off, not on CI alone.
Loading
Loading