statutor 0.4.0__tar.gz → 0.5.1__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.
- {statutor-0.4.0/core/statutor.egg-info → statutor-0.5.1}/PKG-INFO +11 -2
- {statutor-0.4.0 → statutor-0.5.1}/README.md +10 -1
- {statutor-0.4.0 → statutor-0.5.1/core/statutor.egg-info}/PKG-INFO +11 -2
- {statutor-0.4.0 → statutor-0.5.1}/core/statutor.egg-info/SOURCES.txt +1 -0
- {statutor-0.4.0 → statutor-0.5.1}/core/statutor.egg-info/top_level.txt +1 -0
- {statutor-0.4.0 → statutor-0.5.1}/core/statutor_core.py +25 -1
- {statutor-0.4.0 → statutor-0.5.1}/core/statutor_doctor.py +14 -0
- statutor-0.5.1/core/statutor_worker.py +1297 -0
- {statutor-0.4.0 → statutor-0.5.1}/pyproject.toml +2 -2
- {statutor-0.4.0 → statutor-0.5.1}/LICENSE +0 -0
- {statutor-0.4.0 → statutor-0.5.1}/MANIFEST.in +0 -0
- {statutor-0.4.0 → statutor-0.5.1}/core/statutor.egg-info/dependency_links.txt +0 -0
- {statutor-0.4.0 → statutor-0.5.1}/core/statutor.egg-info/entry_points.txt +0 -0
- {statutor-0.4.0 → statutor-0.5.1}/core/statutor_global.py +0 -0
- {statutor-0.4.0 → statutor-0.5.1}/core/statutor_global_cli.py +0 -0
- {statutor-0.4.0 → statutor-0.5.1}/core/statutor_global_status.py +0 -0
- {statutor-0.4.0 → statutor-0.5.1}/core/statutor_skills.py +0 -0
- {statutor-0.4.0 → statutor-0.5.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: statutor
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: Typed project-ledger framework for agentic repos: four planes, single-writer files, enforced mutation policies.
|
|
5
5
|
Author: Hoo
|
|
6
6
|
License-Expression: MIT
|
|
@@ -92,6 +92,15 @@ append-only/cap checks; see adapters/codex/). Residual gaps: MCP tools
|
|
|
92
92
|
and Update-File section checks — so the git floor remains
|
|
93
93
|
mandatory there.
|
|
94
94
|
|
|
95
|
+
Worker provenance (v0.5, `statutor machine`/`statutor worker`): automatic
|
|
96
|
+
hooks on every host prove an **attempt** only and record nothing by
|
|
97
|
+
themselves — the hook path stays fail-open. Ingress is executor- or
|
|
98
|
+
custom-run (`begin` → `record --event attempt` → attributed rewrite →
|
|
99
|
+
`complete --session`); `--event mutation` under an automatic harness is
|
|
100
|
+
denied. `worker capabilities` reports proven surfaces and gaps per host
|
|
101
|
+
(verified against Claude Code 2.1.258, Codex CLI 0.152.1, OpenCode 1.18.20
|
|
102
|
+
baselines); `worker compare <ref>` explains sibling handoffs read-only.
|
|
103
|
+
|
|
95
104
|
## Install
|
|
96
105
|
|
|
97
106
|
pipx install statutor # or: pip install -e .
|
|
@@ -105,7 +114,7 @@ mandatory there.
|
|
|
105
114
|
# .pre-commit-config.yaml
|
|
106
115
|
repos:
|
|
107
116
|
- repo: https://github.com/hoohugokim/statutor
|
|
108
|
-
rev: v0.
|
|
117
|
+
rev: v0.5.1
|
|
109
118
|
hooks: [{id: statutor}]
|
|
110
119
|
|
|
111
120
|
Per-repo policy lives in `.statutor.yaml`. In-loop checks use the committed
|
|
@@ -67,6 +67,15 @@ append-only/cap checks; see adapters/codex/). Residual gaps: MCP tools
|
|
|
67
67
|
and Update-File section checks — so the git floor remains
|
|
68
68
|
mandatory there.
|
|
69
69
|
|
|
70
|
+
Worker provenance (v0.5, `statutor machine`/`statutor worker`): automatic
|
|
71
|
+
hooks on every host prove an **attempt** only and record nothing by
|
|
72
|
+
themselves — the hook path stays fail-open. Ingress is executor- or
|
|
73
|
+
custom-run (`begin` → `record --event attempt` → attributed rewrite →
|
|
74
|
+
`complete --session`); `--event mutation` under an automatic harness is
|
|
75
|
+
denied. `worker capabilities` reports proven surfaces and gaps per host
|
|
76
|
+
(verified against Claude Code 2.1.258, Codex CLI 0.152.1, OpenCode 1.18.20
|
|
77
|
+
baselines); `worker compare <ref>` explains sibling handoffs read-only.
|
|
78
|
+
|
|
70
79
|
## Install
|
|
71
80
|
|
|
72
81
|
pipx install statutor # or: pip install -e .
|
|
@@ -80,7 +89,7 @@ mandatory there.
|
|
|
80
89
|
# .pre-commit-config.yaml
|
|
81
90
|
repos:
|
|
82
91
|
- repo: https://github.com/hoohugokim/statutor
|
|
83
|
-
rev: v0.
|
|
92
|
+
rev: v0.5.1
|
|
84
93
|
hooks: [{id: statutor}]
|
|
85
94
|
|
|
86
95
|
Per-repo policy lives in `.statutor.yaml`. In-loop checks use the committed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: statutor
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: Typed project-ledger framework for agentic repos: four planes, single-writer files, enforced mutation policies.
|
|
5
5
|
Author: Hoo
|
|
6
6
|
License-Expression: MIT
|
|
@@ -92,6 +92,15 @@ append-only/cap checks; see adapters/codex/). Residual gaps: MCP tools
|
|
|
92
92
|
and Update-File section checks — so the git floor remains
|
|
93
93
|
mandatory there.
|
|
94
94
|
|
|
95
|
+
Worker provenance (v0.5, `statutor machine`/`statutor worker`): automatic
|
|
96
|
+
hooks on every host prove an **attempt** only and record nothing by
|
|
97
|
+
themselves — the hook path stays fail-open. Ingress is executor- or
|
|
98
|
+
custom-run (`begin` → `record --event attempt` → attributed rewrite →
|
|
99
|
+
`complete --session`); `--event mutation` under an automatic harness is
|
|
100
|
+
denied. `worker capabilities` reports proven surfaces and gaps per host
|
|
101
|
+
(verified against Claude Code 2.1.258, Codex CLI 0.152.1, OpenCode 1.18.20
|
|
102
|
+
baselines); `worker compare <ref>` explains sibling handoffs read-only.
|
|
103
|
+
|
|
95
104
|
## Install
|
|
96
105
|
|
|
97
106
|
pipx install statutor # or: pip install -e .
|
|
@@ -105,7 +114,7 @@ mandatory there.
|
|
|
105
114
|
# .pre-commit-config.yaml
|
|
106
115
|
repos:
|
|
107
116
|
- repo: https://github.com/hoohugokim/statutor
|
|
108
|
-
rev: v0.
|
|
117
|
+
rev: v0.5.1
|
|
109
118
|
hooks: [{id: statutor}]
|
|
110
119
|
|
|
111
120
|
Per-repo policy lives in `.statutor.yaml`. In-loop checks use the committed
|
|
@@ -27,6 +27,8 @@ Entry modes (all share the same validate() core):
|
|
|
27
27
|
exit 1 on violations.
|
|
28
28
|
statutor init [DIR] Scaffold governed files from embedded templates.
|
|
29
29
|
statutor global ... Opt-in global instruction and skill projection manager.
|
|
30
|
+
statutor machine ... Random machine identity (mode 0600, rotation).
|
|
31
|
+
statutor worker ... Machine-local worker provenance (begin/record/show/active/complete/compare).
|
|
30
32
|
|
|
31
33
|
No third-party dependencies. The policy format is a deterministic YAML subset;
|
|
32
34
|
PyYAML is optional for ancillary tooling, never required by the trust floor.
|
|
@@ -785,6 +787,13 @@ def _automatic_reason(tool: str, payload: dict, cwd: str) -> str | None:
|
|
|
785
787
|
return None
|
|
786
788
|
|
|
787
789
|
def run_hook() -> int:
|
|
790
|
+
if sys.stdin.isatty():
|
|
791
|
+
# A TTY means a human ran the hook with no piped event (harnesses
|
|
792
|
+
# always pipe JSON). Fail fast with usage instead of blocking on
|
|
793
|
+
# stdin forever; fail-open applies to hook *events*, not misuse.
|
|
794
|
+
print("statutor hook expects piped hook JSON on stdin; "
|
|
795
|
+
"run `statutor --help` for modes.", file=sys.stderr)
|
|
796
|
+
return 64
|
|
788
797
|
try:
|
|
789
798
|
event = json.load(sys.stdin)
|
|
790
799
|
tool = event.get("tool_name", "")
|
|
@@ -1314,6 +1323,10 @@ TEMPLATES: dict[str, str] = {
|
|
|
1314
1323
|
# HANDOFF
|
|
1315
1324
|
|
|
1316
1325
|
last_verified: 1970-01-01 by `<command that proved the state below>`
|
|
1326
|
+
last_worker: unknown
|
|
1327
|
+
last_machine: unknown
|
|
1328
|
+
handoff_id: none
|
|
1329
|
+
supersedes: none
|
|
1317
1330
|
|
|
1318
1331
|
## Goal
|
|
1319
1332
|
<the single objective of the current work stream>
|
|
@@ -1406,7 +1419,12 @@ def run_init(target: str) -> int:
|
|
|
1406
1419
|
|
|
1407
1420
|
def main() -> None:
|
|
1408
1421
|
argv = sys.argv[1:]
|
|
1409
|
-
|
|
1422
|
+
if not argv or argv[0] in ("--help", "-h"):
|
|
1423
|
+
# Bare invocation must never fall through to hook mode (which blocks
|
|
1424
|
+
# on stdin); print usage instead. --help exits clean, bare exits 64.
|
|
1425
|
+
print(__doc__)
|
|
1426
|
+
sys.exit(0 if argv else 64)
|
|
1427
|
+
mode = argv[0]
|
|
1410
1428
|
if mode in ("hook", "--claude-hook"):
|
|
1411
1429
|
sys.exit(run_hook())
|
|
1412
1430
|
if mode == "check":
|
|
@@ -1420,6 +1438,12 @@ def main() -> None:
|
|
|
1420
1438
|
if mode == "global":
|
|
1421
1439
|
import statutor_global_cli
|
|
1422
1440
|
sys.exit(statutor_global_cli.main(argv[1:]))
|
|
1441
|
+
if mode == "machine":
|
|
1442
|
+
import statutor_worker
|
|
1443
|
+
sys.exit(statutor_worker.machine_cli(argv[1:]))
|
|
1444
|
+
if mode == "worker":
|
|
1445
|
+
import statutor_worker
|
|
1446
|
+
sys.exit(statutor_worker.worker_cli(argv[1:]))
|
|
1423
1447
|
print(__doc__)
|
|
1424
1448
|
sys.exit(64)
|
|
1425
1449
|
|
|
@@ -42,6 +42,7 @@ import sys
|
|
|
42
42
|
from datetime import date, datetime
|
|
43
43
|
|
|
44
44
|
import statutor_core
|
|
45
|
+
import statutor_worker
|
|
45
46
|
|
|
46
47
|
SOFT_AGENTS_LINES = 120
|
|
47
48
|
HANDOFF_STALE_DAYS = 3
|
|
@@ -141,6 +142,19 @@ def check(root: str) -> None:
|
|
|
141
142
|
"bypassed the hook/floor (that's drift)."
|
|
142
143
|
)
|
|
143
144
|
|
|
145
|
+
fields = statutor_worker.parse_handoff_fields(text)
|
|
146
|
+
if all(fields[name] is None
|
|
147
|
+
for name in statutor_worker.ATTRIBUTION_FIELDS):
|
|
148
|
+
warnings.append(
|
|
149
|
+
f"{handoff_filename} has no v0.5 worker attribution block "
|
|
150
|
+
"(last_worker/last_machine/handoff_id/supersedes) — pre-v0.5 "
|
|
151
|
+
"ledger, still valid; add it on the next rewrite and record "
|
|
152
|
+
"completion with `statutor worker complete --session <id>`."
|
|
153
|
+
)
|
|
154
|
+
else:
|
|
155
|
+
for problem in statutor_worker.validate_handoff_metadata(text):
|
|
156
|
+
errors.append(f"{handoff_filename}: {problem}")
|
|
157
|
+
|
|
144
158
|
done_ids: set[str] = set()
|
|
145
159
|
if os.path.isfile(p(tasks_filename)):
|
|
146
160
|
state_body = open(p(tasks_filename), encoding="utf-8").read()
|