Skip to content

Commit e5e37bf

Browse files
authored
Merge pull request #3 from MacFall7/claude/setup-project-structure-3YeiT
Claude/setup project structure 3 yei t
2 parents bcfa478 + b2dd152 commit e5e37bf

31 files changed

Lines changed: 1545 additions & 92 deletions

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

CLAUDE.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Operating manual for Claude Code sessions in this repo.
44

55
## Mission
66

7-
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.
7+
Deterministic policy and effects runtime for LLM tool calls. Public API and observable semantics are defined by the architectural invariants below; the sibling project at [MacFall7/M87-Spine-lite](https://github.com/MacFall7/M87-Spine-lite) is informational and not a parity target. See `docs/explanation/porting-notes.md` for the relationship.
88

99
## Authority
1010

@@ -19,7 +19,6 @@ Mac decides — halt and ask:
1919
- Anything in `src/spine_lite/__init__.py`'s `__all__`.
2020
- New dependencies beyond `pyproject.toml`.
2121
- Phase boundary transitions (1→2, 2→3).
22-
- Semantic divergence from the TypeScript reference.
2322
- PyPI publish, repo visibility, GitHub Pages enablement.
2423

2524
## Architectural invariants
@@ -56,7 +55,7 @@ All three green or no commit. Before any push, also `coverage` and `docs`. Cover
5655
Halt and report when:
5756

5857
- A phase exit gate item is unclear.
59-
- Python and TS reference diverge semantically and you can't tell which is right.
58+
- The architectural invariants above conflict with new code or new decisions.
6059
- A test fails you can't explain in 15 minutes.
6160
- You're about to add a dependency.
6261
- You're about to modify `__all__`.
@@ -75,14 +74,14 @@ Awaiting: <decision needed>
7574
## Phase plan
7675

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

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

8382
## Scope
8483

85-
- In-repo only. Don't touch the TypeScript reference (read-only spec).
84+
- In-repo only. Don't touch the sibling project at MacFall7/M87-Spine-lite (informational, not a parity target).
8685
- Don't invoke Patronus, Braintrust, or Arize SDKs (operator-decision pending).
8786
- No network calls in tests. No LLM calls anywhere in the runtime.
8887

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
Deterministic policy and effects runtime for LLM tool calls.
99

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.
10+
Six-class effects taxonomy on state × boundary × reversibility axes. Ordinal precedence. Content-addressable receipts. Wires into Claude Code as a PreToolUse hook (Phase 3); usable anywhere you can shell out to a subprocess. Sibling project to [M87-Spine-lite](https://github.com/MacFall7/M87-Spine-lite) — see [Porting Notes](docs/explanation/porting-notes.md) for the relationship.
1111

1212
## What it does
1313

@@ -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: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,176 @@ All 10 items clear. **Phase 1 closed.**
112112
- None. Phase 2 (manifest + classifier, ~5 working days, target `v0.2.0a0`) is gated on operator go per `CLAUDE.md`.
113113

114114
**Next:** Halt for Mac at the Phase 1 → Phase 2 transition. Per blueprint §11, completion of Phase 1 unblocks the Braintrust application thread on the operator's side.
115+
116+
---
117+
118+
### Phase 2 Day 1 Opening — 2026-05-08
119+
120+
Phase 2 opened with a §9 halt. The halt and the operator's resolution are recorded here verbatim for the run-registry trail.
121+
122+
**§9 HALT (verbatim):**
123+
124+
```
125+
HALT: Reference repo's spec diverges from Phase 1's invariants in ways that make
126+
a straight parity port mechanically impossible.
127+
128+
Context:
129+
- The blueprint and CLAUDE.md describe a "TS reference" at MacFall7/M87-Spine-lite.
130+
- WebFetch confirms the repo exists and is public, but it's implemented in Python
131+
(hooks/guard.py, hooks/governor.py), not TypeScript. The "TS reference" framing
132+
in the blueprint is stale.
133+
- Bigger issue: the reference's six-class taxonomy is *categorically different*
134+
from what shipped in Phase 1.
135+
136+
Reference (M87-Spine-lite, README verbatim): │ Phase 1 (spine-lite-python):
137+
138+
SAFE_READ (risk 0.00, auto-approve) │ READ
139+
SHELL_SAFE (risk 0.01, auto-approve) │ WRITE
140+
SHELL_MUTATING (risk 0.04, auto-approve) │ NETWORK
141+
SCOPED_WRITE (risk 0.02, auto-approve) │ EXECUTE
142+
RESTRICTED_WRITE (risk 0.08, manual approval) │ SPAWN
143+
SHELL_DANGEROUS (risk 0.10, blocked) │ DESTRUCTIVE
144+
145+
+ numeric risk scores │ + ordinal precedence
146+
+ per-class default disposition │ + most_restrictive collapse
147+
+ 5-step deny→network→safe→mutating→default pipeline │ + classifier returns Decision
148+
+ 7 invariants about governance philosophy │ + 7 invariants about implementation
149+
150+
- These are not "the same six things with different labels." The reference draws axes
151+
on shell-vs-file and risk-magnitude; Phase 1 draws axes on state-vs-boundary-vs-
152+
reversibility. There's no NETWORK in the reference; there's no SAFE_READ vs
153+
SHELL_SAFE distinction in Phase 1.
154+
- Phase 1 shipped 2026-05-08 as v0.1.0a0, merged via PR #1, repo public, Pages live.
155+
Mac signed off on the action-centric taxonomy and the 7 implementation invariants
156+
in CLAUDE.md.
157+
- This is exactly the §9 stop condition: "TS reference and Python implementation
158+
diverge semantically and you cannot determine which is correct." Except now
159+
it's clear the divergence isn't fixable by translation — it's a different design.
160+
161+
Options:
162+
1. Treat the reference as a sibling project, not a parity target. Phase 2 implements
163+
manifest + classifier from the contract document already drafted in
164+
docs/concepts/posture-and-hooks.md and docs/how-to/wire-claude-code.md (author-
165+
from-spec, same pattern as Phase 1). Add a porting-notes entry recording that
166+
the reference informed the design but is not the byte-equality target — its
167+
taxonomy and ours are categorically different. Parity tests become "round-trip
168+
our own fixtures" rather than "byte-equal against external fixtures."
169+
170+
2. Retract Phase 1's taxonomy. Re-tag v0.1.0a0 as deprecated, bump to v0.1.1a0
171+
with the reference's SAFE_READ/SHELL_*/SCOPED_WRITE/RESTRICTED_WRITE/SHELL_DANGEROUS,
172+
then resume Phase 2 against that baseline. Brings byte-for-byte parity back
173+
on the table.
174+
175+
3. Both taxonomies, with a translation layer. Phase 1 names stay public; manifest
176+
schema accepts reference-style names and maps them. Mapping is judgment-heavy
177+
(does SHELL_SAFE map to EXECUTE or to READ depending on the call?), so parity
178+
becomes case-by-case rather than mechanical.
179+
180+
4. The reference repo isn't actually the spec — CLAUDE.md is, and you confirmed
181+
that in Phase 1 by directing "FLIP TO OPTION 1: author scaffold from blueprint
182+
spec." Phase 2 follows the same pattern. The reference is consulted as
183+
inspiration, cited in porting-notes, but isn't a parity gate.
184+
185+
Recommendation: Option 1 (or equivalently Option 4 — they're the same path with
186+
different labels).
187+
188+
Awaiting:
189+
- Pick 1, 2, 3, or 4 for the parity question.
190+
- (a) or (b) for the Posture enum question.
191+
- Anything else worth logging in porting-notes before Phase 2 starts.
192+
```
193+
194+
**Operator resolution (verbatim):**
195+
196+
```
197+
DECISION ON HALT:
198+
199+
Parity question: Option 1. Reference (MacFall7/M87-Spine-lite) is a sibling project,
200+
not a parity target. Phase 1's taxonomy is correct and final. spine-lite-python
201+
authors from spec, same pattern as Phase 1.
202+
203+
Posture question: sub-option (a). Ship Posture enum in Phase 2 with manifest
204+
validation. Closed-enum + one-line __all__ addition.
205+
206+
BLUEPRINT CORRECTION (do this in commit 1 of Phase 2 before code lands):
207+
208+
Update CLAUDE.md and docs/explanation/porting-notes.md:
209+
- Reference repo is a Python sibling, not "TS reference"
210+
- Reference is informational, not a parity target
211+
- spine-lite-python's design rationale: broader taxonomy (state × boundary ×
212+
reversibility axes), ordinal precedence over risk scores, suitable for any LLM
213+
tool call not just bash
214+
- Record that the reference's taxonomy (SAFE_READ / SHELL_SAFE / SHELL_MUTATING /
215+
SCOPED_WRITE / RESTRICTED_WRITE / SHELL_DANGEROUS + numeric risk scores +
216+
5-step pipeline) was reviewed and explicitly not adopted
217+
218+
Coverage 100% on manifest.py, classifier.py, posture.py (enum scope only).
219+
Six-commit Conventional Commits shape preserved (the blueprint correction is
220+
commit 1, then 5 functional commits).
221+
222+
Begin from blueprint correction commit. Halt at exit gate.
223+
```
224+
225+
**What landed in commit 1 of Phase 2:**
226+
227+
- `CLAUDE.md` mission rewritten to drop "TS reference" framing; sibling project recorded as informational, not a parity target.
228+
- `docs/explanation/porting-notes.md` reframed from "translation log" to "design history" with a Sibling Project section, a Phase 2 opening entry recording this halt and resolution, and a Phase 1 entry pinning the taxonomy as `spine-lite-python`'s spec.
229+
- `docs/concepts/posture-and-hooks.md` updated to remove the "subject to refinement" caveat from the posture table and pin the four members (`INTERACTIVE`, `AUTONOMOUS`, `DRY_RUN`, `LOCKED`) with their string values.
230+
- `docs/explanation/architecture.md` reference-implementation paragraph rewritten as a sibling-project note.
231+
- `docs/explanation/faq.md` "Why Python after TypeScript?" question replaced with "How does this relate to M87-Spine-lite?", plus three other in-place corrections.
232+
- `docs/concepts/effects-taxonomy.md`, `docs/concepts/overview.md`, `docs/how-to/contribute.md`, `docs/how-to/use-the-api.md`, `docs/reference/glossary.md`: surgical edits to drop TS-reference framing.
233+
- `README.md` and `docs/index.md` headlines reworded: `spine-lite-python` is described directly, sibling project credited but not framed as a port target.
234+
- This receipt entry.
235+
236+
**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).

0 commit comments

Comments
 (0)