-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathpyproject.toml
More file actions
262 lines (243 loc) · 12.6 KB
/
Copy pathpyproject.toml
File metadata and controls
262 lines (243 loc) · 12.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
[project]
name = "contemplative-agent"
version = "2.12.0"
description = "A Python CLI agent that self-updates skills, rules, ethics, and identity from experience"
requires-python = ">=3.10"
license = {text = "MIT"}
authors = [
{name = "Tatsuya Shimomoto"},
]
keywords = [
"ai-agent", "autonomous-agent", "self-improving", "local-llm",
"ollama", "memory-distillation", "contemplative-ai",
"cli-agent", "ai-safety", "secure-by-design",
]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
]
dependencies = ["requests>=2.33.0", "numpy>=1.24.0"]
[project.urls]
Homepage = "https://github.com/shimo4228/contemplative-agent"
Repository = "https://github.com/shimo4228/contemplative-agent"
"Bug Tracker" = "https://github.com/shimo4228/contemplative-agent/issues"
[project.scripts]
contemplative-agent = "contemplative_agent.cli:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/contemplative_agent"]
[tool.pytest.ini_options]
testpaths = ["tests"]
# The eval group's deepeval plugin prints skipped nodeids as rich markup and dies with
# MarkupError on Linux (CI run 36055176384, 2026-09-24); evals/ never uses the plugin.
addopts = "-p no:deepeval"
markers = [
"unit: Unit tests",
"integration: Integration tests",
# Spawns the real `claude` binary to read a contract no stub can answer for
# (which flags exist; which `--tools` names the CLI actually honours). Runs
# by default so an operator's full Verify sees the drift; deselected inside
# weekly-pipeline.sh's fix loop, where an unrelated CLI hiccup would abort
# `pytest -x` and be attributed to the fix under test. Every test that
# spawns the binary must carry it — the exclusion is by class, not by name.
"live_cli: Spawns the real claude CLI (external binary, no model call)",
]
[tool.pyright]
include = ["src", "tests", "evals"]
venvPath = "."
venv = ".venv"
# Argparse handler signatures take (args, parser) uniformly via dispatcher;
# many handlers do not use parser, so unused-variable and unreachable reports
# are disabled here (the `_` prefix marks intent at the call sites).
reportUnusedVariable = "none"
reportUnreachable = "none"
[dependency-groups]
# Dev tooling lives here (uv auto-syncs the `dev` group on `uv run` / `uv sync`),
# so `uv run pytest` / `uv run ruff` / `uv run pyright` work without extra flags.
dev = [
# Floor is a security floor, not a feature floor: 9.0.2 and below carry
# PYSEC-2026-1845 (pip-audit, 2026-07-31).
"pytest>=9.0.3",
"pytest-cov>=4.0",
"hypothesis>=6.100",
"responses>=0.23.0",
"types-requests>=2.31",
# Floor tracks the version the PostToolUse autofix hook resolves, so
# `uv run ruff` and the hook agree on more than just the rule set.
"ruff>=0.16",
"pyright>=1.1.380",
"import-linter>=2.0",
# 機械ゲート (.claude/verify.sh) が使う。版の正本をここに集約し、ローカルと CI で
# 同じものが走るようにする。選定根拠と再調査トリガーは .claude/verify.md
"bandit>=1.9.4",
"pip-audit>=2.9",
# 週次 dead-code intake (scripts/dead_code_scan.py) が使う。ruff が構造的に
# 検出できない unused function/class/method/attribute を担う(選定 2026-08-07、
# search-first: MIT / stdlib+toml のみ / 2026-03 v2.16)。検出のみ — 削除は
# 土曜ゲートの人間承認(T-DEADCODE-INTAKE)。
"vulture>=2.16",
# scripts/instrument_census.py の週次集計が使う(ADR-0110)。session × category の
# 行列・分バケット・間隔 diff・markdown 表は手書き ~150 行が各 1 行になる。
# wheel には入れない — scripts/ は配布物の外で、依存の重さは wheel の中でだけ
# 却下理由になる(ADR-0109)。統計は既存の numpy 3 行で足り、scipy は入れない。
"pandas>=2.2",
"tabulate>=0.9", # DataFrame.to_markdown() の実装
"pandas-stubs>=2.2", # 型(pyright の include に scripts は無いが、編集時に効く)
]
# LLM behavioral eval 層 (evals/) 専用。uv の既定同期に含めず、eval 実行と
# verify.sh の type ゲートだけが --group eval で取り込む (ADR-0089)。
# pin は pyproject が正本 (uv.lock は gitignore)。
eval = [
"deepeval==4.1.5",
]
[tool.uv]
# Security floors for packages we do not depend on directly (they arrive via
# requests / pytest / pip-audit). uv.lock is gitignored here, so a lock bump
# alone would not survive a clone — these constraints are the durable record
# of the 2026-07-31 pip-audit drain. Drop an entry once the ecosystem floor
# has moved past it on its own.
constraint-dependencies = [
"urllib3>=2.7.0", # PYSEC-2026-141, PYSEC-2026-142
"idna>=3.15", # PYSEC-2026-215
"pygments>=2.20.0", # PYSEC-2026-2987
]
[tool.vulture]
# 週次 dead-code intake (scripts/dead_code_scan.py) と手動 `uv run vulture` の共通正本
# (T-DEADCODE-INTAKE)。tests/ evals/ は参照解決のために scan へ含める — テストだけが
# 使う src コードを偽陽性にしないため。報告の絞り込み (src/ scripts/ のみ) は
# dead_code_scan.py 側の決定論コードが行う。偽陽性の免除は .vulture_whitelist.py
# (追記・削除は土曜ゲートの人間 commit)。検出のみ — 自動削除は構造的に存在しない。
paths = ["src", "scripts", "tests", "evals", ".vulture_whitelist.py"]
[tool.importlinter]
root_packages = ["contemplative_agent"]
# ADR-0001: one-way dependency cli -> adapters -> core.
# On violation, move the shared code down into core/ (or inject it via
# constructor / SessionContext) instead of importing upward.
[[tool.importlinter.contracts]]
name = "Layered architecture: cli -> adapters -> core (ADR-0001)"
type = "layers"
layers = [
"contemplative_agent.cli",
"contemplative_agent.adapters",
"contemplative_agent.core",
]
# ADR-0015: one external adapter per agent — adapters must stay separable.
# On violation, move the shared logic down into core/ rather than importing
# a sibling adapter.
[[tool.importlinter.contracts]]
name = "Adapter independence (ADR-0015): moltbook / meditation / dialogue"
type = "independence"
modules = [
"contemplative_agent.adapters.moltbook",
"contemplative_agent.adapters.meditation",
"contemplative_agent.adapters.dialogue",
]
# ADR-0088: the conformance kit ships in the wheel (siblings import it) but
# is not production code. Two forbidden contracts rather than a fourth
# `layers` entry: layers would state the direction correctly and ALSO permit
# testing -> adapters, which would drag the Moltbook HTTP client into a
# sibling's test dependencies. What is wanted is a narrow ban on each side.
[[tool.importlinter.contracts]]
name = "Testing kit is a leaf (ADR-0088): production layers never import it"
type = "forbidden"
source_modules = [
"contemplative_agent.cli",
"contemplative_agent.adapters",
"contemplative_agent.core",
]
forbidden_modules = ["contemplative_agent.testing"]
[[tool.importlinter.contracts]]
name = "Testing kit depends on core only (ADR-0088)"
type = "forbidden"
source_modules = ["contemplative_agent.testing"]
forbidden_modules = [
"contemplative_agent.cli",
"contemplative_agent.adapters",
]
[tool.ruff]
target-version = "py310"
line-length = 100
# docs/evidence is frozen verbatim records — data, not owned code (same standard
# as the markdown gate's exclusion list in .claude/verify.md). Excluding it here
# covers lint AND format, in both verify.sh modes, and ends the per-rule
# exemption creep (T20 -> C901 -> ... one per-file-ignores line per new rule).
# force-exclude extends this to explicitly-passed paths (the PostToolUse
# autofix hook invokes ruff on the edited file directly).
extend-exclude = ["docs/evidence"]
force-exclude = true
[tool.ruff.lint]
# Pin the rule set explicitly rather than inheriting ruff's defaults. The
# PostToolUse autofix hook resolves ruff from PATH/uvx while this repo locks
# its own version, and the two drifted: a wider default set fired UP/SIM/RUF
# on the files that happened to be edited, and `ruff check src/ tests/` at
# the locked version reported those same files clean — so the tree silently
# split into modernized and legacy halves. An explicit `select` makes the
# answer identical under both binaries (verified: 674 findings either way).
#
# E4/E7/E9/F are ruff's own defaults, restated here so nothing depends on
# what "default" means in a given release. Beyond them: B (bugbear —
# silent-truncation zip, mutable defaults), I (import sorting), T20 (no
# print in library code — use logging; rules/python/hooks.md), UP (pyupgrade
# — PEP 585/604 typing; the floor is py310 so `list[str]` / `X | None` are
# native at runtime). C901 (mccabe) is the complexity budget — a per-function
# ceiling so that "one more branch" has to be argued for rather than merged by
# default; its threshold and exemption ledger are configured below.
# ANN001-003/201 require explicit types at function boundaries and ANN401 bans
# bare `Any` there (LLM-first axis 4, 2026-08-31): a typed boundary is a
# machine-checkable spec — the frozen edge for regeneration and cross-session
# edits. Production code had zero missing annotations at adoption, and the 24
# bare-Any sites were drained the same day with no exemption and no inline
# noqa. ANN202/204-206 (private/dunder return types) stay unselected — the
# explicit-pin policy adds rules when argued for, not by family.
select = [
"E4", "E7", "E9", "F", "B", "I", "T20", "UP", "C901",
"ANN001", "ANN002", "ANN003", "ANN201", "ANN401",
]
[tool.ruff.lint.mccabe]
# Budget rule: drain, do not raise — any change to this number needs a dated
# reason in .claude/verify.md (harness ADR-0056). The exemption ledger in
# per-file-ignores is empty and stays empty: a function over the budget is a
# design question, not a config entry. When one cannot be drained the answer
# is to stop lowering the threshold, not to exempt the function.
# Distribution as-of 2026-08-31 (src/tests/scripts/evals): p50=1 / p90=3 /
# p95=5 / p99=9 / max=10. Introduced at 15 on 2026-08-28 (then p99=10 / max=35,
# 13 functions drained the same day) and walked down 15 -> 13 -> 11 -> 10 on
# 2026-08-31, draining 24 functions and exempting none. The budget now sits at
# p99: 26 functions are at 10, so the next branch added to any of them has to be
# argued for rather than merged by default.
# "10 was rejected" in the 2026-08-28 note meant rejected as an INTRODUCTION
# threshold, where it would have put 24 files on the exemption list and
# blinded the most-edited modules. Reached by draining instead, it exempts
# nothing — which is the road that was taken.
max-complexity = 10
[tool.ruff.lint.isort]
# Keep explicit `X as X` re-export blocks (core/llm/__init__.py) compact
# instead of one import statement per name.
combine-as-imports = true
# Declare the first-party roots instead of letting ruff infer them from what
# happens to exist on disk. `.claude/verify.sh --staged` checks the *index*
# by materializing only the staged files into a tmpdir, so a commit that
# stages tests without src/ would leave `contemplative_agent` unresolvable
# there — inferred as third-party, and every test file then fails I001 on a
# section split that full mode considers correct (2026-07-31, 14 false
# positives). Declaring it makes staged and full mode agree by construction.
known-first-party = ["contemplative_agent", "tests", "evals"]
[tool.ruff.lint.per-file-ignores]
# Console-UI seams where print IS the interface, not stray logging.
"src/contemplative_agent/cli/**" = ["T20"] # CLI user output
"src/contemplative_agent/adapters/moltbook/agent.py" = ["T20"] # ADR-0012 approval-gate TTY display
"src/contemplative_agent/adapters/dialogue/peer.py" = ["T20"] # stderr turn progress
"src/contemplative_agent/testing/__main__.py" = ["T20"] # ADR-0088 conformance CLI
# tests/ and the ANN family: test functions are not consumed boundaries (nothing
# regenerates against their signatures), and the corpus carries 5,810 missing
# annotations — cost with no LLM-first payoff. Permanent, not a drain ledger.
"tests/**" = ["T20", "ANN001", "ANN002", "ANN003", "ANN201", "ANN401"]
"scripts/**" = ["T20"]
"evals/**" = ["T20"] # probe/CLI 系 (benchmark_distill と同じ扱い) — print が UI
# docs/evidence/** の免除行は 2026-08-31 に撤去 — [tool.ruff] の extend-exclude が
# ディレクトリごと検査対象から外した (凍結記録はデータであってコードではない)