Local CI foundation — uv workspace + ruff + mypy + pytest + poe
Header
Use the pencil to edit title, status, priority, and owner. Changing status auto-prepends a changelog entry.
Why
Every later spec in the substrate inherits this layer. CI runs
locally — not on GitHub Actions — so contributors can replicate the
exact same checks pre-push, no cloud minutes burnt. The single entry
point is uv run poe ci: lint + format-check + typecheck + unit +
contract + spec-validate, all in <30s on a warm cache. Without this,
every new spec's "success determiner" runs in an undocumented,
drift-prone environment.
User stories
As a contributor, I want one command that runs every check before push so that I don't push code that fails verification.
As a future-spec author, I want shared lint + typecheck + test config inherited from the root so that my new code is held to the same bar without re-configuring.
Acceptance criteria (EARS)
- When a developer runs `uv run poe ci` from a clean working tree, the system shall lint, format-check, typecheck, run unit + contract tests, and validate spec YAMLs — exiting 0 on success.
- Where pre-existing modules fail strict linting (B/SIM/RUF rules) or strict typing, the substrate shall exclude them via documented per-path overrides rather than touch their code.
- Where new code lives under `josh_substrate.embedding.*`, `josh_embedder.*`, `app.embedding`, or `app.routes.embed`/`health_embedding`, the substrate shall enforce strict mypy.
- When pre-commit hooks are installed, the system shall run fast checks on staged files at commit time and the full `poe ci` at push time.
- When CI runs, the total wall time shall be under 30 seconds on a warm cache.
Success determiner
Command
set -euo pipefail
uv run poe ci
Expect
From repo root, on a clean working tree.
Clarifications needed
None.
Out of scope
None.
Dependencies
None.
Plan
Top-level pyproject.toml declares the uv workspace, dev dep group,
and tool config (ruff, mypy, pytest). Tasks live in [tool.poe.tasks].
Pre-commit config in .pre-commit-config.yaml wires both stages.
Pre-existing modules grandfathered out of ruff via extend-exclude
and out of strict mypy by being absent from the per-module override
block. New code opts in via the override.
Tasks
7 of 7 done.
- t1 Top-level pyproject.toml with uv workspace + dev deps
- t2 Per-service pyproject.toml for josh-core, josh-embedder, josh-substrate
- t3 ruff + mypy + pytest config inherited from root
- t4 poe tasks: lint, format, typecheck, test, test-contract, test-live, smoke, ci, ci-full, spec-validate
- t5 .pre-commit-config.yaml with pre-commit + pre-push stages
- t6 uv 0.11+ workspace verified to sync all 4 packages cleanly
- t7 Conservative ruff rule set (E/F/I/UP), with cleanup spec deferred for B/SIM/RUF
Changelog
-
2026-05-10T11:00:00Z
planned→verifiedFoundation in place; uv run poe ci exits 0 with 73 tests passing.