Skip to content

Commit 9eae115

Browse files
committed
Quality, skill system, adoption workflow, and pyproject best-practices overhaul
A broad improvement pass across the toolkit, its dogfooded config, the bundled skills, and the new-project/adoption flow. Tree is green (pyclawd check) and the dogfood golden baseline is unchanged. Quality tooling & config model - BuildConfig: group compile/dist/clean knobs into Project.build (BuildConfig | None); compile/dist/clean exit 2 when unset. Scaffold, dogfood, reference example, CHANGELOG updated (breaking config change). - doctor: detect deps by distribution name via importlib.metadata (fixes the false WARN for pytest-xdist/pytest-cov); remove the core->commands layering by extracting skill install/drift logic into a new core module, skills_install.py. - check: replace the positional 4-tuple with a StepResult dataclass (JSON unchanged). - test: clean "unknown category" error; argparse for the timings flags. - golden: pyclawd now dogfoods golden (GoldenConfig + a dedicated golden tier) with a seeded baseline; fix golden_dir to an absolute repo-root path so the CLI and plugin agree regardless of pytest rootdir. - backfill tests: junit/reporting, GoldenStore IO, BuildConfig. Skills: 7 -> 4 -> 5 (progressive disclosure) - Collapse the umbrella + tests/quality/docs skills into one `pyclawd` router skill (lean SKILL.md + references/{mental-model,tests,quality,docs,packaging}.md, on-demand). - Keep golden/doctor/upgrade standalone; add a new `pyclawd-adopt` skill (the field-tested red->green adoption playbook, self-contained). - Add `pyclawd skills prune` + `install --prune` + a doctor orphan WARN so skills dropped from the bundle don't linger in ~/.claude/skills. - Consistency/completeness pass: router now routes to adopt; cross-link tables on every standalone skill; document skills-prune, coverage, and the timings flags. Adoption (existing repo -> pyclawd) - `pyclawd new` adopt mode detects flat-vs-src layout, infers root_markers, prints a Phase-0 readiness report, and offers opt-in `--scaffold-pyproject` (appends only missing [tool.*] sections, never clobbers). - Route the "not a project" errors to `pyclawd new` + the pyclawd-adopt skill. - Golden adoption doctrine: existing tests are the primary oracle for mechanical changes; golden is the targeted supplement for outputs tests don't lock. pyproject best practices (researched, .claude/docs/pyproject-toml.md) - PEP 639: drop the deprecated `License ::` classifier; add `license-files`. - PEP 561: ship `py.typed` and include it in the wheel (pyclawd is a typed lib). - mypy: scope `ignore_missing_imports` to jupyter_cache (no global); fix `mypy_path` to `src` (correct for src layout). Stays non-strict by choice. - pin build backend `hatchling>=1.27` (PEP 639); ruff `select` adds `W`. - scaffold: line-length 100, `--import-mode=importlib`, `xfail_strict`, `exclude_also`, and drop `-n auto` from addopts (pyclawd injects parallelism) so bare pytest / the scaffolded CI no longer require pytest-xdist; CI installs the PEP 735 dev group. - Verified by building the wheel: ships py.typed, METADATA carries License-Expression.
1 parent 1a3fa24 commit 9eae115

54 files changed

Lines changed: 3842 additions & 1099 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/CLAUDE.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ all of these and reports OK/WARN/FAIL.
3232
| `pyclawd test …` | **pytest** | `test fast` also wants **pytest-xdist** (`-n auto`) |
3333
| `pyclawd lint` / `format` | **ruff** | argv set in `QualityConfig` |
3434
| `pyclawd typecheck` | **mypy** | argv set in `QualityConfig` |
35-
| `pyclawd compile` / `dist` | the project's build backend | only if `compile_cmd` / `dist_cmd` are set |
35+
| `pyclawd compile` / `dist` | the project's build backend | only if `project.build` (a `BuildConfig`) sets `compile_cmd` / `dist_cmd`; `build=None` → exit 2 |
3636
| `pyclawd docs build/run/render/exec` | a **`./docs` runner** (sphinx + nbsphinx, via uvx or installed) | delegated to `DocsConfig.runner`; **`render` also needs the `pandoc` system binary** |
3737
| `pyclawd docs failures` / `timings` | a **jupyter-cache** backend at `cache_db`; `failures` imports `jupyter_cache` + `nbformat` **in this env** | see the backend note on `DocsConfig` |
3838

@@ -79,7 +79,11 @@ toolchain is the dogfooded one: ruff + mypy + pytest, all configured in
7979
- **Commands degrade, never crash.** An unconfigured group (e.g. `docs`, `quality`)
8080
self-reports and exits `2`; an undefined test tier applies no `-m` filter. Keep
8181
that contract — prefer `dict.get(...)` / clean `typer.Exit` over raw `KeyError`s.
82-
- **Exit-code contract:** `0` ok · `2` not-configured · otherwise the tool's own code.
82+
- **Exit-code contract:** `0` ok · `2` = not-configured **or** a CLI usage error
83+
(Typer's own convention) · otherwise the underlying tool's own code. Note that
84+
`pyclawd docs` preflight uses distinct codes — `3` for a missing `pandoc` binary,
85+
`1` for "no built docs yet" — so don't assume every nonzero from docs is the
86+
runner's code.
8387
- **Keep the dogfood honest.** If you add a command or config field, update
8488
`.pyclawd/config.py`, `AGENTS.md`, `README.md`, and the scaffold templates so a
8589
freshly-`pyclawd new`-ed project still matches reality.

.claude/docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ with concrete examples and cited sources. These findings are used to shape the p
77

88
- [BEST_PRACTICES.md](BEST_PRACTICES.md) — Opinionated consolidated rules for agent-driven development
99
- [PYCLAWD_ROADMAP.md](PYCLAWD_ROADMAP.md) — Concrete change/add recommendations for pyclawd, grouped by priority
10+
- [pyproject-toml.md](pyproject-toml.md) — Best-practice `pyproject.toml` defaults (build/metadata/license/deps/ruff/mypy/pytest/coverage), with the line-length verdict (June 2026)
1011

1112
## Source Research
1213

.claude/docs/migration.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,6 @@ they still run.
212212
| Need | Reference |
213213
|---|---|
214214
| Prove behavior unchanged (the oracle) | `pyclawd-golden` skill · `examples/golden_demo/` |
215-
| Lint / format / typecheck / `check` gate | `pyclawd-quality` skill |
216-
| Tiered tests, the fix-loop | `pyclawd-tests` skill |
217-
| Mental model + full doctrine | `pyclawd` (umbrella) · `AGENTS.md` |
215+
| Lint / format / typecheck / `check` gate | the `pyclawd` skill's `references/quality.md` |
216+
| Tiered tests, the fix-loop | the `pyclawd` skill's `references/tests.md` |
217+
| Mental model + full doctrine | `pyclawd` (router) · `AGENTS.md` |

.claude/docs/pyproject-toml.md

Lines changed: 460 additions & 0 deletions
Large diffs are not rendered by default.

.pyclawd/config.py

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,25 @@
66
pure-Python project with no compile step and no docs.
77
"""
88

9-
from pyclawd import CoverageConfig, DocsConfig, DoctorConfig, Project, QualityConfig, TestConfig
9+
from pyclawd import (
10+
BuildConfig,
11+
CoverageConfig,
12+
DocsConfig,
13+
DoctorConfig,
14+
GoldenConfig,
15+
Project,
16+
QualityConfig,
17+
TestConfig,
18+
)
1019

1120
project = Project(
1221
name="pyclawd",
1322
conda_env=None, # env-agnostic — runs in whatever env it's installed into
1423
root_markers=["pyproject.toml", "src/pyclawd/__init__.py"],
1524
# The pyclawd this config targets; `pyclawd doctor` WARNs on a minor mismatch.
1625
pyclawd_version="0.1.0",
17-
# Pure-python: no compile or dist step, nothing project-specific to clean.
18-
compile_cmd=[],
19-
dist_cmd=[],
20-
clean_targets=["build", "dist"],
21-
clean_ext_dir="",
22-
clean_ext_globs=[],
26+
# Pure-python: no compile or dist step, just the build/dist dirs to clean.
27+
build=BuildConfig(clean_targets=["build", "dist"]),
2328
# Default directory `pyclawd ls` lists (the code/source root) — src-layout repo.
2429
src_dir="src",
2530
# Dogfood the docs pipeline on pyclawd itself: the worked ./docs runner builds
@@ -38,10 +43,16 @@
3843
tests_dir="tests/",
3944
classname_prefix="tests.",
4045
integration_files=[],
41-
# No slow/integration tests here, so every tier collects nearly the same suite.
42-
markers={"fast": "not slow and not integration", "default": "not slow", "all": ""},
46+
# golden is its own gate (`pyclawd golden`), excluded from the unit tiers.
47+
markers={
48+
"fast": "not slow and not integration and not golden",
49+
"default": "not slow and not golden",
50+
"all": "not golden",
51+
},
4352
),
4453
coverage=CoverageConfig(source=["src/pyclawd"]),
54+
# Dogfood the behavior oracle: `pyclawd golden` gates `@pytest.mark.golden` tests.
55+
golden=GoldenConfig(),
4556
doctor=DoctorConfig(
4657
core_deps=["typer", "rich"],
4758
dev_deps=["pytest", "pytest-xdist", "pytest-cov"],

AGENTS.md

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ walks up from cwd to find `.pyclawd/config.py`.
8989

9090
Mark slow tests `@pytest.mark.slow`, tests needing live services
9191
`@pytest.mark.integration`. Unmarked tests run in every tier — never mark a test
92-
`fast`. The fix-loop and failure taxonomy live in the `pyclawd-tests` skill.
92+
`fast`. The fix-loop and failure taxonomy live in the `pyclawd` skill's `references/tests.md`.
9393

9494
## Behavior oracle (golden)
9595

@@ -124,8 +124,10 @@ in one file:
124124
root.
125125
- The `Project` model groups config: `QualityConfig` (lint/format/typecheck/check
126126
argv), `TestConfig` (tests dir + tier markers), `DocsConfig` (docs toolchain, or
127-
`None`), `DoctorConfig` (deps/binaries/tool-files to probe), `CoverageConfig`,
128-
`DescriptionConfig`. Unset optional groups disable their commands cleanly.
127+
`None`), `DoctorConfig` (deps/binaries/tool-files to probe), `BuildConfig`
128+
(compile/dist/clean argv, via `project.build`, or `None`), `CoverageConfig`,
129+
`DescriptionConfig`, and `GoldenConfig` (the behavior-oracle wrapper, or `None`).
130+
Unset optional groups disable their commands cleanly.
129131
- To inspect a project's setup, read its `.pyclawd/config.py` — it is the single
130132
source of truth for env, paths, markers, and checks.
131133

@@ -170,18 +172,33 @@ the convention when adopting an existing repo.
170172

171173
## How you know you're done
172174

173-
- `pyclawd check` is green (format-check, lint, typecheck, and tests all ✓).
175+
- `pyclawd check` is green (format-check, lint, typecheck, descriptions, and tests
176+
all ✓). The `descriptions` step is the enforced gate — it passes when every
177+
`DescriptionConfig`-included file (default `.py`/`.pyx`) has a one-line
178+
description.
174179
- `pyclawd doctor` exits 0 — no FAILs.
175-
- `pyclawd ls --missing` is empty — every file has a one-line description.
176180
- Behavior is verified by tests, not just by inspection.
177181

182+
`pyclawd ls --missing` is the broader **exploratory** view: it lists *every* repo
183+
file lacking a one-liner, including templates and Markdown the `descriptions` gate
184+
deliberately ignores. It may legitimately be non-empty even when `pyclawd check` is
185+
green — the gate is `descriptions`, not `ls --missing`.
186+
178187
---
179188

180189
**Going deeper.** This file is the contract. For the doctrine behind it, invoke the
181-
Claude Code skills: **`pyclawd`** (mental model + best practices) is the umbrella;
182-
**`pyclawd-tests`**, **`pyclawd-quality`**, **`pyclawd-doctor`**, **`pyclawd-docs`**,
183-
and **`pyclawd-golden`** are the focused deep-dives; **`pyclawd-upgrade`** migrates a
184-
project after pyclawd itself is updated (when `pyclawd version` shows config drift).
185-
They are generic (not specific to any one repo) and update centrally when pyclawd is
186-
upgraded — which is exactly why the deep doctrine lives there and is not duplicated
187-
into this file.
190+
Claude Code skills: **`pyclawd`** is the umbrella router — a lean overview plus
191+
on-demand reference docs (`references/mental-model.md`, `references/tests.md`,
192+
`references/quality.md`, `references/docs.md`, `references/packaging.md`) that carry
193+
the testing, quality, docs, and packaging doctrine. Four focused standalone skills
194+
sit alongside it: **`pyclawd-adopt`** (adopt pyclawd into an existing repo —
195+
red-to-green with zero behavior regression; `pyclawd new` adopt mode now detects the
196+
flat-vs-`src` layout, infers `root_markers`, and prints a Phase-0 readiness report of
197+
what is missing, with `--scaffold-pyproject` to drop a starter ruff/mypy/pytest config),
198+
**`pyclawd-golden`** (the behavior
199+
oracle), **`pyclawd-doctor`** (diagnose a broken env), and **`pyclawd-upgrade`**
200+
(migrate a project *after* a pyclawd version bump, when `pyclawd version` shows config
201+
drift — the upgrade counterpart to `pyclawd-adopt`'s first-time onboarding). They are
202+
generic (not
203+
specific to any one repo) and update centrally when pyclawd is upgraded — which is
204+
exactly why the deep doctrine lives there and is not duplicated into this file.

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,19 @@ the running pyclawd has a different `major.minor`, pointing here for what change
5050
an `(editable)` marker) so you can tell which pyclawd is driving a project.
5151

5252
### Changed
53+
- **Build fields grouped into a new `BuildConfig` (breaking config change).** The
54+
five loose top-level build knobs — `compile_cmd`, `dist_cmd`, `clean_targets`,
55+
`clean_ext_dir`, `clean_ext_globs` — have moved off `Project` into a new frozen
56+
`BuildConfig` dataclass, reached via `Project.build` (a `BuildConfig | None`,
57+
default `None`). Migrate `Project(compile_cmd=…, clean_targets=…, …)` to
58+
`Project(build=BuildConfig(compile_cmd=…, clean_targets=…, …))`; `BuildConfig` is
59+
exported from `pyclawd` alongside the other `*Config`s. When `Project.build` is
60+
`None` the `compile` / `dist` / `clean` commands self-report and exit 2 (the same
61+
0/2 contract as before). This is a clean break — there is no compatibility shim.
62+
- **Scaffolded `check_sequence` now includes the `descriptions` step** — `pyclawd
63+
new` writes `["format-check", "lint", "typecheck", "descriptions", "test"]`, so a
64+
freshly scaffolded project enforces the file-description code-map doctrine out of
65+
the box, matching pyclawd's own dogfood config.
5366
- **Test taxonomy aligned to two marker axes***speed* (`slow`) and *scope*
5467
(`integration`) — with a canonical tier ladder (`fast = not slow and not
5568
integration`, `default = not slow`, `all =` everything). The `long` marker is

README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ pyclawd new myproj # scaffold a fresh best-practice project
4141
cd myproj
4242

4343
pyclawd new # …or, inside an existing repo, ADOPT pyclawd
44-
# (writes ./.pyclawd/config.py, inferring sensible defaults)
44+
# (detects flat-vs-src layout, infers root_markers, writes
45+
# ./.pyclawd/config.py, and prints a Phase-0 readiness report)
46+
pyclawd new --scaffold-pyproject # also drop a starter ruff/mypy/pytest config
4547

4648
pyclawd doctor # health-check the dev env
4749
pyclawd test fast # <30s smoke tier
@@ -62,7 +64,7 @@ pyclawd check # the full quality gate
6264
| Aggregate quality gate | `pyclawd check` |
6365
| Build / dist / clean | `pyclawd compile` · `pyclawd dist` · `pyclawd clean [--ext]` |
6466
| Docs (if configured) | `pyclawd docs build\|run\|render\|serve\|status\|failures\|exec <page>` |
65-
| Scaffold / adopt | `pyclawd new <name>` · `pyclawd new` |
67+
| Scaffold / adopt | `pyclawd new <name>` · `pyclawd new` (adopt: detects layout + Phase-0 readiness) · `pyclawd new --scaffold-pyproject` |
6668
| Code map (file → description) | `pyclawd ls [DIR]` · `pyclawd ls --missing` · `pyclawd ls --py` |
6769
| Repo root / version | `pyclawd root` · `pyclawd version` |
6870

@@ -103,7 +105,7 @@ project = Project(
103105
format_cmd=["ruff", "format"],
104106
format_check_cmd=["ruff", "format", "--check"],
105107
typecheck_cmd=["mypy", "src"],
106-
check_sequence=["format-check", "lint", "typecheck", "test"],
108+
check_sequence=["format-check", "lint", "typecheck", "descriptions", "test"],
107109
),
108110
test=TestConfig( # tests dir + tier marker expressions
109111
tests_dir="tests/",
@@ -121,7 +123,7 @@ project = Project(
121123
)
122124
```
123125

124-
The nested configs (`QualityConfig`, `TestConfig`, `DocsConfig`, `DoctorConfig`) each gate their command group: leave one out and those commands self-report as unconfigured instead of crashing.
126+
The nested configs (`QualityConfig`, `TestConfig`, `DocsConfig`, `DoctorConfig`, plus the optional `BuildConfig` via `project.build` and `GoldenConfig`) each gate their command group: leave one out and those commands self-report as unconfigured instead of crashing (e.g. `build=None``compile`/`dist`/`clean` exit 2).
125127

126128
Two worked examples bracket the range:
127129

@@ -132,7 +134,7 @@ Copy whichever is closer and delete what you don't use.
132134

133135
## Skills
134136

135-
pyclawd ships agent-facing slash-command skills under [`skills/`](skills/): the start-here umbrella `pyclawd` skill plus the focused `pyclawd-doctor`, `pyclawd-tests`, `pyclawd-quality`, `pyclawd-golden`, `pyclawd-docs`, and `pyclawd-upgrade`. They are thin wrappers over the CLI. Copy or symlink the `pyclawd-*` directories into a project's `.claude/skills/` — see [`skills/README.md`](skills/README.md). Agent doctrine for any pyclawd project lives in [`AGENTS.md`](AGENTS.md).
137+
pyclawd ships agent-facing slash-command skills under [`skills/`](skills/): the start-here umbrella `pyclawd` router skill (a lean overview plus on-demand `references/{mental-model,tests,quality,docs,packaging}.md` carrying the testing/quality/docs/packaging doctrine) plus the four focused standalone skills `pyclawd-adopt` (adopt pyclawd into an existing repo — red-to-green with zero behavior regression), `pyclawd-golden`, `pyclawd-doctor`, and `pyclawd-upgrade` (migrate *after* a pyclawd version bump — the upgrade counterpart to first-time adoption). They are thin wrappers over the CLI. `pyclawd skills install` copies (or symlinks) the `pyclawd-*` directories into your **user-scope** `~/.claude/skills/` by default — they are generic, so they are shared across every project rather than committed per-repo — see [`skills/README.md`](skills/README.md). Agent doctrine for any pyclawd project lives in [`AGENTS.md`](AGENTS.md).
136138

137139
## Status
138140

examples/config.reference.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
FAIL,
2121
OK,
2222
WARN,
23+
BuildConfig,
2324
Check,
2425
DocsConfig,
2526
DoctorConfig,
@@ -79,15 +80,19 @@ def acme_doctor_checks() -> list[Check]:
7980
# ["conda", "run", "-n", "acme-dev", "python"] -> one pyclawd, many conda envs
8081
# ["uv", "run", "python"] -> uv-managed env
8182
python_cmd=[],
82-
# --- build / dist / clean (omit any for a pure-Python project) --------- #
83-
# Args handed to the dev Python: `pyclawd compile` → `python setup.py build_ext ...`.
84-
compile_cmd=["setup.py", "build_ext", "--inplace"],
85-
dist_cmd=["setup.py", "sdist"],
86-
# `pyclawd clean` removes these root-relative paths (guarded to stay in-repo).
87-
clean_targets=["build", "dist", "acme.egg-info"],
88-
# `pyclawd clean --ext` removes these globs under this dir (forces a recompile).
89-
clean_ext_dir="acme/functions/compiled",
90-
clean_ext_globs=["*.c", "*.cpp", "*.so", "*.html"],
83+
# --- build / dist / clean (omit `build` entirely for a pure-Python project) #
84+
# All build knobs live in one nested BuildConfig, reached via `project.build`.
85+
# When `build` is None the compile/dist/clean commands self-report and exit 2.
86+
build=BuildConfig(
87+
# Args handed to the dev Python: `pyclawd compile` → `python setup.py build_ext ...`.
88+
compile_cmd=["setup.py", "build_ext", "--inplace"],
89+
dist_cmd=["setup.py", "sdist"],
90+
# `pyclawd clean` removes these root-relative paths (guarded to stay in-repo).
91+
clean_targets=["build", "dist", "acme.egg-info"],
92+
# `pyclawd clean --ext` removes these globs under this dir (forces a recompile).
93+
clean_ext_dir="acme/functions/compiled",
94+
clean_ext_globs=["*.c", "*.cpp", "*.so", "*.html"],
95+
),
9196
# Default directory `pyclawd ls` lists (the code/source root), relative to root.
9297
src_dir="acme",
9398
# --- code quality (lint / format / typecheck / check) ----------------- #

pyproject.toml

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[build-system]
2-
requires = ["hatchling"]
2+
requires = ["hatchling>=1.27"] # >=1.27 for PEP 639 license-expression support
33
build-backend = "hatchling.build"
44

55
[project]
@@ -11,12 +11,12 @@ description = "A project-generic, config-driven dev-task runner for Python proje
1111
authors = [{ name = "Julian Blank", email = "blankjul@outlook.com" }]
1212
readme = "README.md"
1313
license = "Apache-2.0"
14+
license-files = ["LICENSE*"]
1415
requires-python = ">=3.10"
1516
keywords = ["developer-tools", "task-runner", "cli", "testing", "documentation"]
1617
classifiers = [
1718
"Development Status :: 4 - Beta",
1819
"Intended Audience :: Developers",
19-
"License :: OSI Approved :: Apache Software License",
2020
"Programming Language :: Python :: 3.10",
2121
"Programming Language :: Python :: 3.11",
2222
"Programming Language :: Python :: 3.12",
@@ -61,6 +61,7 @@ path = "src/pyclawd/__init__.py"
6161

6262
[tool.hatch.build.targets.wheel]
6363
packages = ["src/pyclawd"]
64+
include = ["src/pyclawd/py.typed"] # PEP 561 — ship the typed marker (we're a typed lib)
6465

6566
# Ship the CHANGELOG inside the wheel so `pyclawd changelog` works from any
6667
# pip-installed pyclawd (PyPI included) — the agent-driven upgrade flow reads it to
@@ -88,7 +89,7 @@ extend-exclude = ["docs"]
8889
# under ruff's unstable `preview`, so selecting them is a silent no-op (and emits a
8990
# warning). The Google docstring *convention* below + the `D` rules are the gate;
9091
# the no-types / Args-Returns style is upheld by review and the `pyclawd ls` map.
91-
select = ["E", "F", "I", "B", "UP", "SIM", "C4", "RUF", "PGH", "D"]
92+
select = ["E", "W", "F", "I", "B", "UP", "SIM", "C4", "RUF", "PGH", "D"]
9293
# `compile` and `format` shadow builtins by design — they are intentional
9394
# command names (`pyclawd compile`, `pyclawd format`).
9495

@@ -109,13 +110,12 @@ extend-immutable-calls = ["typer.Option", "typer.Argument"]
109110
# Non-strict to start: wire up type-checking without drowning in noise.
110111
python_version = "3.10"
111112
files = ["src"]
112-
ignore_missing_imports = true
113113
warn_unused_configs = true
114114
check_untyped_defs = true
115115
# Needed for `pyclawd typecheck/check <file>` — prevents "Duplicate module"
116116
# errors when mypy resolves a single source file by path instead of by name.
117117
explicit_package_bases = true
118-
mypy_path = "."
118+
mypy_path = "src"
119119

120120
# numpy is an optional dep used only via `Any` (lazy import in pyclawd.golden).
121121
# Don't follow into its stubs: with python_version pinned to 3.10, mypy rejects the
@@ -125,3 +125,10 @@ mypy_path = "."
125125
module = ["numpy", "numpy.*"]
126126
follow_imports = "skip"
127127
follow_imports_for_stubs = true
128+
129+
# jupyter-cache ships no type stubs / py.typed; docs.py imports it lazily for the
130+
# `pyclawd docs failures` path. Scope the missing-import allowance to it (rather
131+
# than a global ignore_missing_imports) so every other unstubbed import still errors.
132+
[[tool.mypy.overrides]]
133+
module = ["jupyter_cache.*"]
134+
ignore_missing_imports = true

0 commit comments

Comments
 (0)