obstat 0.1.0__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,3 +9,6 @@ build/
9
9
 
10
10
  # obstat's own working files: a decision record is per-deployment, never committed.
11
11
  .obstat/
12
+
13
+ # IDE config: per-developer, not per-project.
14
+ .idea/
obstat-0.2.0/CLAUDE.md ADDED
@@ -0,0 +1,52 @@
1
+ # obstat
2
+
3
+ An auditable decision record for agent tool calls. The one claim the project
4
+ makes: **the decision is on disk, fsynced, before the tool body runs.** Anything
5
+ that weakens that ordering is a bug, not an optimisation.
6
+
7
+ ## Commands
8
+
9
+ ```bash
10
+ uv run pytest -q # 26 tests, ~0.3s
11
+ uv run ruff check .
12
+ uv run ruff format .
13
+ ```
14
+
15
+ ## Rules that are not negotiable
16
+
17
+ - **No runtime dependencies.** `tomllib`, `sqlite3`, `hashlib`, `json` are
18
+ stdlib. `mcp` is an optional extra for `examples/server.py` only. A governance
19
+ library nobody can try on a laptop is one nobody adopts.
20
+ - **`docs/obstat-spec.md` is normative.** Behaviour changes update the spec in
21
+ the same commit. Where spec and code disagree, one of them is a bug.
22
+ - **Nothing matching the policy is a deny**, and a missing policy file is an
23
+ error — never an implicit allow.
24
+ - **Denials reach the caller with a record id and nothing else.** The reason goes
25
+ to the record. A denial that explains itself teaches a caller what to work
26
+ around.
27
+
28
+ ## Conventions
29
+
30
+ Comments say *why*, not what, and name the alternative that was rejected. A
31
+ `ponytail:` comment marks a deliberate shortcut and its upgrade path — those are
32
+ debts, not decoration.
33
+
34
+ Spec section numbers (`§5.1`) are referenced from docstrings; keep them in step
35
+ when the spec is renumbered.
36
+
37
+ ## Releasing
38
+
39
+ 1. Bump `version` in `pyproject.toml` **and** `__version__` in
40
+ `obstat/__init__.py` — two places, no single source yet.
41
+ 2. `git tag vX.Y.Z && git push --tags`
42
+
43
+ `.github/workflows/publish.yml` runs the tests against the tag, builds, and
44
+ uploads via PyPI trusted publishing (OIDC — there is no token anywhere). **A PyPI
45
+ version is immutable**: a bad build can be superseded, never withdrawn.
46
+
47
+ ## Gotcha
48
+
49
+ `uv run --with /path/to/obstat` serves a **cached wheel** and will happily run
50
+ code you edited minutes ago as if you hadn't. `--reinstall-package` does not
51
+ dislodge it; `--refresh` does. Two verification runs were wasted on this — if a
52
+ local install seems to ignore your change, that is why.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: obstat
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: An auditable decision record for agent tool calls. The clearance is written down before the call runs.
5
5
  Project-URL: Homepage, https://github.com/marcinmarzeta/obstat
6
6
  Project-URL: Issues, https://github.com/marcinmarzeta/obstat/issues
@@ -21,6 +21,11 @@ Description-Content-Type: text/markdown
21
21
 
22
22
  # obstat
23
23
 
24
+ [![PyPI](https://img.shields.io/pypi/v/obstat)](https://pypi.org/project/obstat/)
25
+ [![Python](https://img.shields.io/pypi/pyversions/obstat)](https://pypi.org/project/obstat/)
26
+ [![CI](https://github.com/marcinmarzeta/obstat/actions/workflows/ci.yml/badge.svg)](https://github.com/marcinmarzeta/obstat/actions/workflows/ci.yml)
27
+ [![License](https://img.shields.io/pypi/l/obstat)](LICENSE)
28
+
24
29
  **An auditable decision record for agent tool calls.** The clearance is written
25
30
  down before the call runs — not reconstructed from logs afterwards.
26
31
 
@@ -37,6 +42,9 @@ pip install obstat
37
42
  No dependencies. Not AWS, not an identity provider, not a policy service — the
38
43
  decorator, `tomllib`, `sqlite3` and a file.
39
44
 
45
+ [`docs/obstat-spec.md`](docs/obstat-spec.md) is normative, and its §8 lists what
46
+ is still weak.
47
+
40
48
  ## 60 seconds
41
49
 
42
50
  `obstat.toml`:
@@ -87,7 +95,7 @@ the whole story.
87
95
  ## What it is not
88
96
 
89
97
  There are several good libraries that gate MCP tool calls. This one is built
90
- around a narrower claim: **the record is the product.** Three things follow from
98
+ around a narrower claim: **the record is the product.** Four things follow from
91
99
  that, and they are the reason to pick this over a permission wrapper.
92
100
 
93
101
  **The decision is durable before the body runs.** Not flushed after, not written
@@ -117,6 +125,19 @@ q3-report" cannot be spent on deleting something else, and cannot be spent twice
117
125
  This is enforced in one `BEGIN IMMEDIATE` transaction, so two concurrent retries
118
126
  cannot both win.
119
127
 
128
+ **The record says when it has been edited.** Every record carries the hash of the
129
+ one before it, and `obstat verify` recomputes the chain:
130
+
131
+ ```console
132
+ $ obstat verify
133
+ line 3: record cd53f9db… follows a record that is no longer in the log
134
+ ```
135
+
136
+ An edited line and a deleted line both show up. A truncated tail does not, and
137
+ anyone who can write the file can recompute the whole chain — this is
138
+ tamper-evidence, not non-repudiation, and
139
+ [§8](docs/obstat-spec.md#8-still-open) says so in those words.
140
+
120
141
  ## Identity is optional
121
142
 
122
143
  Most MCP servers today have no token at all: stdio, one local user, or a gateway
@@ -180,6 +201,7 @@ obstat pending # approvals waiting on a human
180
201
  obstat approve <id> [--by] # decide
181
202
  obstat deny <id> [--by]
182
203
  obstat log -n 50 # the decision record
204
+ obstat verify # recompute the chain; exit 1 if anything was edited
183
205
  obstat stop # deny every guarded call
184
206
  obstat resume
185
207
  ```
@@ -213,4 +235,4 @@ you cannot try.
213
235
 
214
236
  ## License
215
237
 
216
- Apache-2.0.
238
+ Apache-2.0. Copyright 2026 Marcin Marzęta.
@@ -1,5 +1,10 @@
1
1
  # obstat
2
2
 
3
+ [![PyPI](https://img.shields.io/pypi/v/obstat)](https://pypi.org/project/obstat/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/obstat)](https://pypi.org/project/obstat/)
5
+ [![CI](https://github.com/marcinmarzeta/obstat/actions/workflows/ci.yml/badge.svg)](https://github.com/marcinmarzeta/obstat/actions/workflows/ci.yml)
6
+ [![License](https://img.shields.io/pypi/l/obstat)](LICENSE)
7
+
3
8
  **An auditable decision record for agent tool calls.** The clearance is written
4
9
  down before the call runs — not reconstructed from logs afterwards.
5
10
 
@@ -16,6 +21,9 @@ pip install obstat
16
21
  No dependencies. Not AWS, not an identity provider, not a policy service — the
17
22
  decorator, `tomllib`, `sqlite3` and a file.
18
23
 
24
+ [`docs/obstat-spec.md`](docs/obstat-spec.md) is normative, and its §8 lists what
25
+ is still weak.
26
+
19
27
  ## 60 seconds
20
28
 
21
29
  `obstat.toml`:
@@ -66,7 +74,7 @@ the whole story.
66
74
  ## What it is not
67
75
 
68
76
  There are several good libraries that gate MCP tool calls. This one is built
69
- around a narrower claim: **the record is the product.** Three things follow from
77
+ around a narrower claim: **the record is the product.** Four things follow from
70
78
  that, and they are the reason to pick this over a permission wrapper.
71
79
 
72
80
  **The decision is durable before the body runs.** Not flushed after, not written
@@ -96,6 +104,19 @@ q3-report" cannot be spent on deleting something else, and cannot be spent twice
96
104
  This is enforced in one `BEGIN IMMEDIATE` transaction, so two concurrent retries
97
105
  cannot both win.
98
106
 
107
+ **The record says when it has been edited.** Every record carries the hash of the
108
+ one before it, and `obstat verify` recomputes the chain:
109
+
110
+ ```console
111
+ $ obstat verify
112
+ line 3: record cd53f9db… follows a record that is no longer in the log
113
+ ```
114
+
115
+ An edited line and a deleted line both show up. A truncated tail does not, and
116
+ anyone who can write the file can recompute the whole chain — this is
117
+ tamper-evidence, not non-repudiation, and
118
+ [§8](docs/obstat-spec.md#8-still-open) says so in those words.
119
+
99
120
  ## Identity is optional
100
121
 
101
122
  Most MCP servers today have no token at all: stdio, one local user, or a gateway
@@ -159,6 +180,7 @@ obstat pending # approvals waiting on a human
159
180
  obstat approve <id> [--by] # decide
160
181
  obstat deny <id> [--by]
161
182
  obstat log -n 50 # the decision record
183
+ obstat verify # recompute the chain; exit 1 if anything was edited
162
184
  obstat stop # deny every guarded call
163
185
  obstat resume
164
186
  ```
@@ -192,4 +214,4 @@ you cannot try.
192
214
 
193
215
  ## License
194
216
 
195
- Apache-2.0.
217
+ Apache-2.0. Copyright 2026 Marcin Marzęta.