Skip to content

Commit 31104d6

Browse files
committed
docs: rewrite README, add SECURITY, point CONTRIBUTING at the docs site
README: status grid with phase + version + ship date, repository layout, copy-paste install, today-vs-later capability matrix, deep links into the published docs site, badges that resolve. SECURITY.md: vulnerability-reporting process, supported-version policy, trust model (what the runtime guarantees vs. what it doesn't), threat model in scope vs. out of scope, dependency policy. CONTRIBUTING.md: trimmed to a quick-start that points at the long-form guide in docs/how-to/contribute.md. CHANGELOG: [Unreleased] entry for the documentation expansion.
1 parent d5e36c7 commit 31104d6

4 files changed

Lines changed: 219 additions & 48 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ All notable changes to this project are documented here. The format follows [Kee
44

55
## [Unreleased]
66

7+
### Added
8+
9+
- `SECURITY.md` with vulnerability-reporting process, supported-version policy, and the runtime trust model.
10+
- Documentation site restructured into Diátaxis quadrants (Tutorial / How-To / Reference / Explanation) plus a History section. New pages: getting-started, concepts/{overview,effects-taxonomy,posture-and-hooks}, how-to/{use-the-api,wire-claude-code,contribute,release}, reference/{cli,exceptions,glossary}, explanation/{invariants,faq}, history/phase-1.
11+
- Iron-clad README with status grid, repository layout, and links into the docs site.
12+
13+
### Changed
14+
15+
- `docs/architecture.md`, `docs/design-rationale.md`, `docs/porting-notes.md`, `docs/integration-claude-code.md`, and `docs/api.md` moved under `docs/explanation/`, `docs/how-to/`, and `docs/reference/`.
16+
- `CONTRIBUTING.md` reduced to a quick-start that points at the long form in the docs site.
17+
718
## [0.1.0a0] — 2026-05-08
819

920
### Added

‎CONTRIBUTING.md‎

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
# Contributing
22

3+
The quick-start. The full contributor guide lives at [docs/how-to/contribute.md](https://macfall7.github.io/spine-lite-python/how-to/contribute/).
4+
35
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.
46

5-
## Local setup
7+
## Setup
68

79
```bash
810
git clone https://github.com/MacFall7/spine-lite-python
@@ -11,7 +13,7 @@ uv venv
1113
uv sync --all-extras --dev
1214
```
1315

14-
## Verification
16+
## Verify
1517

1618
Before every commit:
1719

@@ -25,34 +27,37 @@ Before every push, also:
2527
nox -s coverage docs
2628
```
2729

28-
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`.
29-
30-
## Style
30+
Coverage stays ≥ 95%; docs build with `--strict`.
3131

32-
- Python 3.11+. `from __future__ import annotations` at the top of every module.
33-
- `mypy --strict` clean. No `Any` without a comment explaining why.
34-
- Google-style docstrings on every public symbol.
35-
- Frozen, slotted, kw-only dataclasses by default. Mutable only with explicit justification.
36-
- Conventional Commits: `feat:`, `fix:`, `docs:`, `chore:`, `test:`, `refactor:`, `ci:`. Subject ≤ 72 chars, imperative mood, no trailing period.
37-
- Direct prose. No marketing copy, no LLM boilerplate, no performative empathy.
38-
39-
## Architecture rules
32+
If you don't have nox, the underlying tools work directly:
4033

41-
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.
34+
```bash
35+
uv run ruff check .
36+
uv run ruff format --check .
37+
uv run mypy src tests
38+
uv run pytest --cov=spine_lite --cov-fail-under=95
39+
uv run mkdocs build --strict
40+
```
4241

43-
## Tests
42+
## Style at a glance
4443

45-
`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.
44+
- Python 3.11+. `from __future__ import annotations` everywhere.
45+
- `mypy --strict` clean. No `Any` without a justifying comment.
46+
- Google-style docstrings on every public symbol.
47+
- Conventional Commits, subject ≤ 72 chars, imperative mood, no trailing period.
48+
- Direct prose. No marketing tone, no LLM boilerplate.
4649

47-
## Reviewing
50+
## Architecture rules
4851

49-
The build operates under explicit phase gates documented in `CLAUDE.md`. Reviews should focus on:
52+
The closed six-class effects taxonomy and the precedence ordering are the spec. The five pure modules (`effects.py`, `manifest.py`, `classifier.py`, `posture.py`, `receipt.py`) contain no I/O, no clocks, no randomness. I/O lives in `hook.py`, `cli.py`, and tests.
5053

51-
1. Public API stability (anything in `__all__`).
52-
2. Determinism in the pure modules.
53-
3. Parity with the TypeScript reference where applicable.
54-
4. Test coverage on the modules touched.
54+
If your change cannot be made within those rules, that's an issue for discussion — not a PR.
5555

56-
## Releases
56+
## Where to read more
5757

58-
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.
58+
- [Full contributor guide](https://macfall7.github.io/spine-lite-python/how-to/contribute/)
59+
- [Architecture](https://macfall7.github.io/spine-lite-python/explanation/architecture/)
60+
- [Invariants](https://macfall7.github.io/spine-lite-python/explanation/invariants/)
61+
- [Design Rationale](https://macfall7.github.io/spine-lite-python/explanation/design-rationale/)
62+
- [Release process](https://macfall7.github.io/spine-lite-python/how-to/release/)
63+
- [`CLAUDE.md`](CLAUDE.md) — repo governance for Claude Code sessions.

‎README.md‎

Lines changed: 89 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,34 @@
11
# spine-lite
22

3+
[![CI](https://github.com/MacFall7/spine-lite-python/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/MacFall7/spine-lite-python/actions/workflows/ci.yml)
4+
[![docs](https://img.shields.io/badge/docs-mkdocs--material-blue)](https://macfall7.github.io/spine-lite-python/)
5+
[![python](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13-blue)](https://github.com/MacFall7/spine-lite-python)
6+
[![license](https://img.shields.io/badge/license-MIT-green)](LICENSE)
7+
38
Deterministic policy and effects runtime for LLM tool calls.
49

5-
A Python port of [M87-Spine-lite](https://github.com/MacFall7/M87-Spine-lite). Same closed effects taxonomy, same precedence rules, same posture state machine — typed, tested, and shipped as a `pip`-installable package.
10+
A Python port of [M87-Spine-lite](https://github.com/MacFall7/M87-Spine-lite). Same closed effects taxonomy, same precedence rules, same posture state machine — typed, tested, packaged.
11+
12+
## What it does
13+
14+
You give it a tool call and a manifest of declared effects. It returns a deterministic decision: which effects fire, what the dominant class is, and a structured receipt you can replay by SHA. Built to wire into Claude Code as a PreToolUse hook; usable anywhere you can shell out to a subprocess.
15+
16+
The runtime is offline by design — no clocks, no randomness, no network, no LLM calls inside the runtime itself.
617

718
## Status
819

9-
Alpha. Phase 1 ships the scaffold, the closed effects taxonomy, and the public exception hierarchy. The classifier, posture state machine, hook adapter, and the rest of the CLI land in Phases 2 and 3. See `RECEIPTS.md` for build progress and `docs/architecture.md` for the plan.
20+
| Phase | Scope | Version | State |
21+
|-------|-------|---------|-------|
22+
| 1 | Scaffold, taxonomy, exceptions, CLI surface, CI matrix, docs | `v0.1.0a0` | Shipped 2026-05-08 |
23+
| 2 | Manifest schema, classifier with parity tests | `v0.2.0a0` | Pending |
24+
| 3 | Posture state machine, receipts, hook adapter, end-to-end | `v0.3.0a0` | Pending |
1025

11-
> PyPI release lands at the end of Phase 3. Until then, install from source.
26+
See [`RECEIPTS.md`](RECEIPTS.md) for build progress and [docs/history/phase-1.md](https://macfall7.github.io/spine-lite-python/history/phase-1/) for the Phase 1 narrative.
1227

1328
## Install
1429

30+
PyPI publish lands at the end of Phase 3. Until then, install from source:
31+
1532
```bash
1633
git clone https://github.com/MacFall7/spine-lite-python
1734
cd spine-lite-python
@@ -26,46 +43,94 @@ uv run spine-lite version
2643
# 0.1.0a0
2744
```
2845

29-
## Quickstart
46+
Need uv? `curl -LsSf https://astral.sh/uv/install.sh | sh` (Linux/macOS) or `winget install astral-sh.uv` (Windows).
47+
48+
## Use today (Phase 1)
3049

3150
```python
32-
from spine_lite import Effect, most_restrictive
51+
from spine_lite import Effect, most_restrictive, SpineLiteError
3352

53+
# Collapse any non-empty effect set to its dominant class
3454
most_restrictive({Effect.READ, Effect.NETWORK, Effect.WRITE})
3555
# <Effect.NETWORK: 'network'>
56+
57+
# Precedence is total: DESTRUCTIVE > SPAWN > EXECUTE > NETWORK > WRITE > READ
58+
most_restrictive({Effect.READ, Effect.WRITE, Effect.DESTRUCTIVE})
59+
# <Effect.DESTRUCTIVE: 'destructive'>
60+
61+
# Every error in the package descends from SpineLiteError
62+
issubclass(SpineLiteError, Exception) # True
3663
```
3764

38-
The classifier and PreToolUse hook arrive in later phases. The taxonomy is the contract; everything else builds on top of it.
65+
The classifier and PreToolUse hook arrive in Phases 2 and 3. The taxonomy is the contract; everything else builds on it without changing it.
66+
67+
## What this gives you
3968

40-
## What this is
69+
The closed six-class effects taxonomy:
4170

42-
A six-class taxonomy of side effects every tool call falls into:
71+
| Effect | Meaning | Examples |
72+
|--------|---------|----------|
73+
| `READ` | observation only | reading a file, listing a dir, querying without writing |
74+
| `WRITE` | persistent state change to caller-owned storage | writing a file, updating a row, appending a log |
75+
| `NETWORK` | outbound network call | HTTP request, DNS lookup, Slack message |
76+
| `EXECUTE` | subprocess invocation, no fork | `subprocess.run`, `os.system`, shell pipeline |
77+
| `SPAWN` | subprocess that may fork-and-detach | `Popen` with no `wait`, daemon launch, `nohup` |
78+
| `DESTRUCTIVE` | irreversible state change | `rm -rf`, `git push --force`, dropping a table |
4379

44-
| Effect | Meaning |
45-
| --- | --- |
46-
| `READ` | observation only |
47-
| `WRITE` | persistent state change to caller-owned storage |
48-
| `NETWORK` | outbound network call |
49-
| `EXECUTE` | subprocess invocation |
50-
| `SPAWN` | fork-and-detach subprocess |
51-
| `DESTRUCTIVE` | irreversible change |
80+
Ordered by precedence: `DESTRUCTIVE > SPAWN > EXECUTE > NETWORK > WRITE > READ`. `most_restrictive()` collapses any non-empty set to the highest class.
5281

53-
Ordered by precedence: `DESTRUCTIVE > SPAWN > EXECUTE > NETWORK > WRITE > READ`. `most_restrictive()` collapses any non-empty set to the highest class. Same input produces the same output every time. No clocks, no randomness, no I/O in the core.
82+
Same input → same output, every time. No clocks, no randomness, no I/O in the core.
5483

55-
## What this is not
84+
## What this isn't
5685

57-
- Not a model. No LLM calls happen inside spine-lite.
58-
- Not a sandbox. It classifies and decides; enforcement happens in the hook.
59-
- Not extensible at runtime. The taxonomy is closed by design — extending it is a project-level decision.
86+
- **Not a model.** No LLM calls happen inside the runtime.
87+
- **Not a sandbox.** It classifies and decides; enforcement happens in the hook adapter.
88+
- **Not extensible at runtime.** The taxonomy is closed by design — extending it is a project-level decision.
89+
- **Not a network library.** There are zero network calls in the runtime, ever.
6090

6191
## Documentation
6292

63-
Full docs publish to GitHub Pages once Phase 1 is signed off.
93+
Full docs at <https://macfall7.github.io/spine-lite-python/>. Quick links:
94+
95+
- [**Getting Started**](https://macfall7.github.io/spine-lite-python/getting-started/) — five-minute install + first decision.
96+
- [**Concepts / Overview**](https://macfall7.github.io/spine-lite-python/concepts/overview/) — mental model end to end.
97+
- [**How-To / Use the API**](https://macfall7.github.io/spine-lite-python/how-to/use-the-api/) — practical patterns, today.
98+
- [**How-To / Wire into Claude Code**](https://macfall7.github.io/spine-lite-python/how-to/wire-claude-code/) — operator runbook.
99+
- [**Reference / API**](https://macfall7.github.io/spine-lite-python/reference/api/) — auto-generated from docstrings.
100+
- [**Reference / Glossary**](https://macfall7.github.io/spine-lite-python/reference/glossary/) — vocabulary.
101+
- [**Explanation / Architecture**](https://macfall7.github.io/spine-lite-python/explanation/architecture/) — why it's shaped this way.
102+
- [**Explanation / Invariants**](https://macfall7.github.io/spine-lite-python/explanation/invariants/) — the rules nothing in this repo gets to break.
103+
- [**Explanation / FAQ**](https://macfall7.github.io/spine-lite-python/explanation/faq/) — common questions about the design.
104+
105+
## Repository layout
106+
107+
```
108+
spine-lite-python/
109+
├── src/spine_lite/ # the runtime (5 pure modules + hook + cli + exceptions)
110+
├── tests/ # unit + smoke tests, all offline
111+
├── docs/ # mkdocs source — Diátaxis-structured
112+
├── .github/workflows/ # CI matrix + Pages deploy
113+
├── CLAUDE.md # repo governance for Claude Code sessions (≤ 150 lines)
114+
├── CONTRIBUTING.md # contributor quick-start
115+
├── CHANGELOG.md # what shipped in each version
116+
├── RECEIPTS.md # append-only phase-day completion log
117+
├── SECURITY.md # vulnerability reporting + trust model
118+
├── pyproject.toml # hatchling build, deps pinned via uv.lock
119+
└── uv.lock # tracked; CI cache keys against it
120+
```
64121

65122
## Contributing
66123

67-
See `CONTRIBUTING.md`. The closed effects taxonomy and the public API are non-negotiable; proposals to change either need a written rationale on an issue first.
124+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the quick-start and [docs/how-to/contribute.md](https://macfall7.github.io/spine-lite-python/how-to/contribute/) for the long form. The closed effects taxonomy and the public API are non-negotiable; proposals to change either need a written rationale on an issue first.
125+
126+
## Security
127+
128+
See [SECURITY.md](SECURITY.md). The runtime is deterministic and offline; trust questions live in your manifest.
129+
130+
## Governance
131+
132+
This repo runs under M87 Studio with explicit phase boundaries. Authority split, halt conditions, and verification gates are in [`CLAUDE.md`](CLAUDE.md). Phase exit gates and receipts are in [`RECEIPTS.md`](RECEIPTS.md).
68133

69134
## License
70135

71-
MIT. See `LICENSE`.
136+
[MIT](LICENSE). Maintained by Mac McFall.

‎SECURITY.md‎

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Security
2+
3+
## Reporting a vulnerability
4+
5+
Email `security@m87.studio` with details. Don't open a public issue for a vulnerability; security issues stay private until a fix is shipped and disclosed.
6+
7+
Include in the report:
8+
9+
- A description of the vulnerability and its impact.
10+
- Steps to reproduce, ideally with a minimal example.
11+
- The version(s) affected (commit SHA is fine).
12+
- Your preferred disclosure timeline (the default is 90 days).
13+
14+
Expect an acknowledgement within 5 business days. We'll coordinate disclosure and CVE assignment if applicable.
15+
16+
## Supported versions
17+
18+
Alpha. Phase 1 is the only shipped version.
19+
20+
| Version | Status | Security fixes |
21+
|---------|--------|----------------|
22+
| `0.1.0aN` (Phase 1) | Current | Yes |
23+
| `0.2.0aN` (Phase 2) | Pending | Will be supported when shipped |
24+
| `0.3.0aN` (Phase 3) | Pending | Will be supported when shipped |
25+
26+
Once `v1.0.0` ships, we support the latest two minor lines (`v1.N` and `v1.N-1`) for security fixes.
27+
28+
## Trust model
29+
30+
The runtime is deterministic and offline. It contains:
31+
32+
- No LLM calls.
33+
- No network calls.
34+
- No filesystem access in the pure modules.
35+
- No subprocess invocation in the pure modules.
36+
37+
Trust questions live entirely in your **manifest**. The manifest declares which tools the LLM can invoke, what effects each tool produces, and which postures permit which calls. If your manifest is wrong, the runtime will faithfully classify a wrong call as a wrong call — the runtime is not the safety net for a bad manifest.
38+
39+
### What the hook does and doesn't do
40+
41+
When `v0.3.0a0` ships:
42+
43+
- `spine-lite hook` reads stdin, classifies, decides, writes stdout. It does not execute the tool itself; that's Claude Code's job.
44+
- An exit code of 0 from the hook is a permission, not an action. Whether the tool actually runs is the host's decision.
45+
- A non-zero exit deny is final on the host's side. spine-lite has no way to override its own deny.
46+
47+
### What you trust the runtime for
48+
49+
- Correctly classifying a tool call against a manifest.
50+
- Producing a deterministic, content-addressable receipt.
51+
- Never silently widening permissions (e.g. classifying a `WRITE` as a `READ`).
52+
53+
### What you don't trust the runtime for
54+
55+
- Validating that the tool itself behaves as the manifest claims. The manifest is a contract, not a proof.
56+
- Sandboxing the tool's execution. The hook decides; the host enforces.
57+
- Detecting model jailbreaks or prompt injection. That's a separate layer.
58+
59+
### Threat model
60+
61+
In scope:
62+
63+
- A misconfigured manifest that leaks more permission than intended.
64+
- A tool call that classifies differently in TypeScript and Python (parity bug).
65+
- A receipt that doesn't reproduce on replay (determinism bug).
66+
- A path-handling bug on Windows that doesn't surface on Linux.
67+
68+
Out of scope:
69+
70+
- Adversarial LLM output that the manifest does not constrain.
71+
- Compromise of the operator's machine (the hook reads files; if those files are tampered with, classification follows the tampered input).
72+
- Side-channel attacks via posture transitions in shared environments.
73+
74+
## Dependencies
75+
76+
We run `pip-audit` (manually for now, automated in CI for Phase 2+) against the `uv.lock` and respond to CVEs in dependencies within 30 days. The dependency surface is intentionally small: `pydantic`, `typer`, and the dev/docs tooling.
77+
78+
## Cryptographic guarantees
79+
80+
Receipts are content-addressable via `hashlib.sha256` (Phase 3). Two receipts with the same SHA-256 represent the same decision over the same inputs. We rely on the standard-library implementation; no custom crypto.
81+
82+
## Disclosure history
83+
84+
None yet.
85+
86+
## See also
87+
88+
- [Explanation / Invariants](docs/explanation/invariants.md) — what the runtime guarantees.
89+
- [Explanation / FAQ](docs/explanation/faq.md) — common questions about the design.
90+
- [How-To / Wire into Claude Code](docs/how-to/wire-claude-code.md) — the hook contract.

0 commit comments

Comments
 (0)