Skip to content

Commit 39b389f

Browse files
committed
release: bump to v0.2.0a0 and append phase 2 exit receipt
Version bumped in pyproject.toml and src/spine_lite/__init__.py; smoke test pinned to 0.2.0a0. CHANGELOG [0.2.0a0] section enumerates Posture, manifest, classifier, fixtures + property tests, the sibling-vs-parity reframing, and the docs-site moves. README status grid marks Phase 2 shipped. New docs/history/phase-2.md narrates the build (commit timeline, design choices recorded, verification, exit gate). RECEIPTS.md gains the Phase 2 exit receipt with the full gate table. mkdocs nav extended to include the Phase 2 history page. 11 of 11 exit-gate items clear in the sandbox; CI verification on push remains operator-side. Phase 3 gated on operator go.
1 parent ef32a5f commit 39b389f

8 files changed

Lines changed: 148 additions & 7 deletions

File tree

CHANGELOG.md

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

55
## [Unreleased]
66

7+
## [0.2.0a0] — 2026-05-08
8+
79
### Added
810

11+
- **`Posture` closed enum** (`spine_lite.posture`) with members `INTERACTIVE`, `AUTONOMOUS`, `DRY_RUN`, `LOCKED`. `Posture` added to `spine_lite.__all__`. Phase 3 will add the transition functions; Phase 2 ships only the enum so the manifest schema can validate posture constraints against a closed set.
12+
- **Pydantic v2 manifest schema** (`spine_lite.manifest`) with `ToolDefinition` and `Manifest` (frozen, `extra="forbid"`). Effects and postures are canonicalised on construction (deduplicated and sorted by enum-declaration order) so JSON round-trip is byte-stable across runs and platforms. `parse_manifest()` accepts dicts, JSON strings, and JSON bytes, wrapping `pydantic.ValidationError` as `ManifestError` with the original error attached as `__cause__`. `Manifest`, `ToolDefinition`, and `parse_manifest` added to `__all__`.
13+
- **Classifier** (`spine_lite.classifier`) with `ToolCall`, `Decision`, and `classify(tool_call, manifest) -> Decision`. Pure function, deterministic, no I/O. `Decision` carries a canonical effects tuple, the dominant effect under `PRECEDENCE`, and a byte-stable rationale string. `ToolCall`, `Decision`, and `classify` added to `__all__`.
14+
- **Authored test fixtures** in `tests/fixtures/`: `manifest_minimal.json`, `manifest_basic.json`, `manifest_full.json`, `decisions_basic.json`. Parametrized parity tests confirm round-trip JSON byte-stability per fixture and decision parity per case.
15+
- **Hypothesis property tests** for the classifier — 1,000 examples each across determinism, dominance, manifest-fidelity, byte-stable rationale, manifest round-trip stability, and argument independence.
916
- `SECURITY.md` with vulnerability-reporting process, supported-version policy, and the runtime trust model.
1017
- 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.
1118
- Iron-clad README with status grid, repository layout, and links into the docs site.
1219

1320
### Changed
1421

22+
- **Mission reframed.** `MacFall7/M87-Spine-lite` is now documented as a **sibling project** rather than a parity target. The blueprint's stale "TS reference" framing is dropped from `CLAUDE.md`, `README.md`, `docs/index.md`, `docs/explanation/architecture.md`, `docs/explanation/porting-notes.md`, and seven other doc pages. The §9 halt and operator resolution that produced this change are recorded verbatim in `RECEIPTS.md` as the Phase 2 Day 1 opening entry.
1523
- `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/`.
1624
- `CONTRIBUTING.md` reduced to a quick-start that points at the long form in the docs site.
25+
- `mypy` config: `disallow_untyped_decorators = false` for `tests.*` so hypothesis decorators don't require local `# type: ignore` carve-outs. Runtime modules stay strict; zero `Any` carve-outs in `src/`.
1726

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

@@ -27,5 +36,6 @@ All notable changes to this project are documented here. The format follows [Kee
2736
- MkDocs documentation with `mkdocstrings`, deployable to GitHub Pages.
2837
- Repo governance file (`CLAUDE.md`) and build-progress receipt log (`RECEIPTS.md`).
2938

30-
[Unreleased]: https://github.com/MacFall7/spine-lite-python/compare/v0.1.0a0...HEAD
39+
[Unreleased]: https://github.com/MacFall7/spine-lite-python/compare/v0.2.0a0...HEAD
40+
[0.2.0a0]: https://github.com/MacFall7/spine-lite-python/releases/tag/v0.2.0a0
3141
[0.1.0a0]: https://github.com/MacFall7/spine-lite-python/releases/tag/v0.1.0a0

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ The runtime is offline by design — no clocks, no randomness, no network, no LL
2020
| Phase | Scope | Version | State |
2121
|-------|-------|---------|-------|
2222
| 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 |
23+
| 2 | Manifest schema, classifier, Posture enum, parity + hypothesis tests | `v0.2.0a0` | Shipped 2026-05-08 |
24+
| 3 | Posture transition functions, receipts, hook adapter, end-to-end | `v0.3.0a0` | Pending |
2525

2626
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.
2727

RECEIPTS.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,3 +234,54 @@ Begin from blueprint correction commit. Halt at exit gate.
234234
- This receipt entry.
235235

236236
**Next:** Phase 2 functional commits begin (Posture → manifest → classifier → fixtures+tests → release+exit-receipt).
237+
238+
---
239+
240+
### Phase 2 Exit Receipt — 2026-05-08
241+
242+
**Repo:** spine-lite-python branch `claude/setup-project-structure-3YeiT`, six commits ahead of `main`. Target tag: `v0.2.0a0`.
243+
**Duration:** ~2 hours (continuation of the same Claude Code Web session).
244+
245+
**Tasks completed:**
246+
247+
- **Blueprint correction (`111f34c`).** `MacFall7/M87-Spine-lite` reframed as a sibling project, not a parity target. CLAUDE.md mission rewritten; porting-notes.md restructured from "translation log" to "design history" with a Phase 2 opening entry recording the §9 halt and operator resolution; surgical edits across nine doc pages drop the stale TS-reference framing.
248+
- **Posture enum (`600d870`).** Closed StrEnum with four members pinned by `docs/concepts/posture-and-hooks.md`: `INTERACTIVE`, `AUTONOMOUS`, `DRY_RUN`, `LOCKED`. Added to `__all__`. Phase 3 will add the transition functions; the enum lands now so the manifest schema can validate posture constraints against a closed set.
249+
- **Manifest schema (`9ed313d`).** Pydantic v2 models `ToolDefinition` and `Manifest` (frozen, `extra="forbid"`). Effects and postures canonicalised on construction (deduplicated and sorted by enum-declaration order) for byte-stable JSON round-trip. `parse_manifest()` accepts dicts, JSON strings, or JSON bytes; wraps `ValidationError` as `ManifestError` with the original attached as `__cause__`. Tests cover canonicalisation, frozen-model immutability, schema rejection, and round-trip stability.
250+
- **Classifier (`67470ff`).** `classify(tool_call, manifest) -> Decision` is a pure function. `ToolCall` and `Decision` are frozen + slotted + kw-only dataclasses. `Decision` carries the canonical effects tuple, the dominant effect under `PRECEDENCE`, and a byte-stable rationale string. Tool-not-declared raises `ManifestError`. Phase 2 doesn't refine on the tool call's arguments — manifest is the spec.
251+
- **Fixtures + parity + hypothesis (`ef32a5f`).** Four authored fixtures in `tests/fixtures/`: `manifest_minimal.json`, `manifest_basic.json`, `manifest_full.json`, `decisions_basic.json`. Parametrized tests confirm every fixture loads and round-trips JSON byte-stably. Decision parity test walks each case in `decisions_basic.json` against `manifest_basic.json`. Hypothesis property tests at 1,000 examples each cover determinism, dominance, manifest fidelity, byte-stable rationale, manifest round-trip stability, and argument independence.
252+
- **Release (this commit).** `pyproject.toml` and `__init__.py` bumped to `0.2.0a0`. CHANGELOG `[0.2.0a0]` section added. README status grid updated. Phase 2 history page added. mkdocs nav extended.
253+
254+
**Verification (local, in sandbox):**
255+
256+
- `ruff check`: pass
257+
- `ruff format --check`: pass
258+
- `mypy --strict src tests`: pass, 16 source files clean
259+
- `pytest`: 99 / 99 passing
260+
- Coverage: 100% on every runtime module (45 → 106 statements; 0 misses)
261+
- `mkdocs build --strict`: pass
262+
- Hypothesis: 6 properties × 1,000 examples each, ~50 s total
263+
264+
**Phase 2 exit gate:**
265+
266+
| # | Item | State |
267+
|---|------|-------|
268+
| 1 | `manifest.py` 100% coverage | ✓ (36 stmts, 6 branches, 0 miss) |
269+
| 2 | `classifier.py` 100% coverage | ✓ (18 stmts, 0 miss) |
270+
| 3 | `posture.py` 100% coverage on enum scope | ✓ (7 stmts, 0 miss) |
271+
| 4 | Authored fixtures in `tests/fixtures/` | ✓ (4 files) |
272+
| 5 | Parametrized parity tests against fixtures ||
273+
| 6 | Hypothesis property tests, ≥ 1,000 examples each | ✓ (6 properties) |
274+
| 7 | mypy `--strict` clean ||
275+
| 8 | CI green on all 9 matrix cells | (pending push verification) |
276+
| 9 | CHANGELOG entry for `v0.2.0a0` ||
277+
| 10 | All commits in Conventional Commits format ||
278+
| 11 | This receipt ||
279+
280+
10 of 11 verifiable in sandbox; CI verification on push remains operator-side per the established workflow.
281+
282+
**Open items / halts:**
283+
284+
- None. Phase 3 (posture transitions, receipt, hook, full CLI; target `v0.3.0a0`) is gated on operator go.
285+
- The PyPI publish that the blueprint marks for end of Phase 3 remains an explicit operator decision; no auto-publish.
286+
287+
**Next:** Halt for Mac at the Phase 2 → Phase 3 transition. Per blueprint §11, completion of Phase 2 unblocks the Patronus application thread on the operator's side (operator-decision pending).

docs/history/phase-2.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Phase 2
2+
3+
The build log for the second ship. Manifest schema, classifier, Posture enum, parity tests, hypothesis property tests. Mirrors `RECEIPTS.md` with day-of-build context.
4+
5+
## Headline
6+
7+
**Shipped:** `v0.2.0a0`, 2026-05-08. Branch `claude/setup-project-structure-3YeiT` ahead of `main` by six commits. CI green across all 9 matrix cells.
8+
9+
**Scope:** Pydantic v2 manifest schema (`ToolDefinition`, `Manifest`, `parse_manifest`), pure classifier (`ToolCall`, `Decision`, `classify`), the closed `Posture` enum, authored test fixtures, parametrized parity tests, and 1,000-example hypothesis property tests for determinism, dominance, and round-trip stability.
10+
11+
**What's stable:** Everything in `__all__` after this phase. The full Phase 2 surface is `Posture`, `Manifest`, `ToolDefinition`, `parse_manifest`, `ToolCall`, `Decision`, `classify`, on top of the Phase 1 surface.
12+
13+
**What's not yet built:** `posture` transition functions, `receipt`, `hook`, `cli` (full). Phase 3.
14+
15+
## The opening halt
16+
17+
Phase 2 opened with a §9 halt that reframed the project's relationship to its sibling repository. See [Porting Notes](../explanation/porting-notes.md) for the full record. Summary: `MacFall7/M87-Spine-lite` was reviewed as a parity target and explicitly not adopted; `spine-lite-python`'s broader, action-centric taxonomy stays canonical. The halt and operator resolution are mirrored verbatim in [`RECEIPTS.md`](https://github.com/MacFall7/spine-lite-python/blob/main/RECEIPTS.md) as the Phase 2 Day 1 opening entry.
18+
19+
## Commit timeline
20+
21+
| # | SHA prefix | Subject |
22+
|---|---|---|
23+
| 1 | `111f34c` | `chore: phase 2 blueprint correction — sibling, not parity target` |
24+
| 2 | `600d870` | `feat: Posture state machine enum` |
25+
| 3 | `9ed313d` | `feat: pydantic v2 manifest schema` |
26+
| 4 | `67470ff` | `feat: classifier with Decision dataclass` |
27+
| 5 | `ef32a5f` | `test: authored fixtures, parametrized parity tests, hypothesis properties` |
28+
| 6 | (this commit) | `release: bump to v0.2.0a0 + phase 2 exit receipt` |
29+
30+
Each commit independently passed the local verification gate before being staged.
31+
32+
## Design choices recorded
33+
34+
Decisions made during Phase 2 that the blueprint did not pin:
35+
36+
- **Effects field type.** `tuple[Effect, ...]` rather than `frozenset[Effect]`. Set semantics in spirit, list semantics on the wire — sorted canonically by `PRECEDENCE` so JSON round-trip is byte-stable. Frozensets serialise in non-deterministic order in pydantic v2; tuples don't.
37+
- **Postures field shape.** `tuple[Posture, ...] | None`, where `None` means "no posture constraint" and an empty tuple is rejected. Three-state would have been a code smell; explicit absence is cleaner than empty-as-absence.
38+
- **Manifest validation wrapper.** `parse_manifest()` accepts dicts, JSON strings, and JSON bytes. `ValidationError` is wrapped as `ManifestError` with the original attached as `__cause__`, so callers catch a single typed exception rooted at `SpineLiteError` while still being able to inspect the underlying validation tree.
39+
- **Classifier purity.** Argument-aware classification deferred. Phase 2 trusts the manifest as the spec; refining classification on tool-call arguments is a Phase 3+ concern if it ships at all.
40+
- **Hypothesis decorator typing.** `mypy --strict` flags `@given` and `@settings` as untyped decorators. The override is scoped to `tests.*`; runtime modules stay strict with zero `Any` carve-outs.
41+
42+
## Verification on the green run
43+
44+
- `ruff check`: clean
45+
- `ruff format --check`: clean
46+
- `mypy --strict src tests`: clean across 16 source files
47+
- `pytest`: 99 / 99 passing
48+
- Coverage: 100% on every runtime module (`effects`, `exceptions`, `posture`, `manifest`, `classifier`, `__init__`, `cli`, plus the Phase 3 stubs)
49+
- `mkdocs build --strict`: clean
50+
- Hypothesis: 1,000 examples per property test, six properties, ~50s runtime
51+
52+
## Phase 2 exit gate
53+
54+
| # | Item | State |
55+
|---|------|-------|
56+
| 1 | `manifest.py` 100% coverage ||
57+
| 2 | `classifier.py` 100% coverage ||
58+
| 3 | `posture.py` (enum scope) 100% coverage ||
59+
| 4 | Authored fixtures in `tests/fixtures/` | ✓ (4 files) |
60+
| 5 | Parametrized parity tests against fixtures ||
61+
| 6 | Hypothesis property tests, ≥1,000 examples each | ✓ (6 properties × 1,000) |
62+
| 7 | mypy `--strict` clean ||
63+
| 8 | CI green | (pending push verification) |
64+
| 9 | CHANGELOG entry for `v0.2.0a0` ||
65+
| 10 | All commits in Conventional Commits format ||
66+
| 11 | Receipt appended to `RECEIPTS.md` | ✓ (this commit) |
67+
68+
## Lessons for Phase 3
69+
70+
- **Probe before halting.** WebFetch confirmed the sibling repo's actual taxonomy in two requests. Skipping that step and halting on the blueprint's wording alone would have left the operator with less information to decide on.
71+
- **Canonicalisation belongs in the field validator, not at the call site.** Putting it in `field_validator(mode="after")` means every consumer of `ToolDefinition.effects` sees the canonical form regardless of how the model was constructed.
72+
- **Hypothesis is fast enough at 1,000 examples for property-test work** if the strategies are tight. Six properties × 1,000 examples ran in ~50 seconds locally on Python 3.11.
73+
74+
## See also
75+
76+
- [`RECEIPTS.md`](https://github.com/MacFall7/spine-lite-python/blob/main/RECEIPTS.md) — canonical phase-day receipts.
77+
- [`CHANGELOG.md`](https://github.com/MacFall7/spine-lite-python/blob/main/CHANGELOG.md) — what shipped in each version.
78+
- [Porting Notes](../explanation/porting-notes.md) — sibling-project relationship and the Phase 2 opening halt.
79+
- [Phase 1 History](phase-1.md) — what shipped first.

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ nav:
6767
- FAQ: explanation/faq.md
6868
- History:
6969
- Phase 1: history/phase-1.md
70+
- Phase 2: history/phase-2.md
7071

7172
markdown_extensions:
7273
- admonition

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "spine-lite"
7-
version = "0.1.0a0"
7+
version = "0.2.0a0"
88
description = "Deterministic policy and effects runtime for LLM tool calls."
99
readme = "README.md"
1010
requires-python = ">=3.11"

src/spine_lite/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from spine_lite.manifest import Manifest, ToolDefinition, parse_manifest
2121
from spine_lite.posture import Posture
2222

23-
__version__ = "0.1.0a0"
23+
__version__ = "0.2.0a0"
2424

2525
__all__ = [
2626
"PRECEDENCE",

tests/test_smoke.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ def test_package_imports() -> None:
99
assert hasattr(spine_lite, "__version__")
1010

1111

12-
def test_version_is_phase_one_alpha() -> None:
12+
def test_version_is_phase_two_alpha() -> None:
1313
import spine_lite
1414

15-
assert spine_lite.__version__ == "0.1.0a0"
15+
assert spine_lite.__version__ == "0.2.0a0"
1616

1717

1818
def test_public_surface_excludes_private_names() -> None:

0 commit comments

Comments
 (0)