M1: Implement normalized capacity contract v1 #5

Merged
creatidy merged 4 commits from m1-capacity-model-validation into main 2026-09-03 10:50:33 +00:00
creatidy commented 2026-09-02 14:04:55 +00:00 (Migrated from github.com)

Resolves #4.

Summary

Implements the frozen v1 normalized capacity contract from docs/capacity-model.md as pure, provider-independent, standard-library-only product code, plus the repository quality gate. Incorporates all fixes from both review rounds.

Contract implementation (scarcity_router/)

  • capacity.py — the four v1 dataclasses (CapacitySnapshot, CapacityWindow, LocalRuntime, CapacityDiagnostic), the _v_* validators as the single source of truth, _v_exact_shape for serialized-shape validation, deterministic JSON-compatible to_dict, SCHEMA_VERSION = 1.
  • errors.py — CapacityError / CapacityValidationError.
  • __init__.py — public API exports.

Review fixes

  • Constructor-time validation: every v1 invariant is enforced in __post_init__ on all four dataclasses, so an invalid value can never exist as a public/serializable object — via from_dict() or a direct constructor call.
  • Missing required keys: from_dict raises CapacityValidationError (never a raw KeyError) and rejects unknown keys; one validation path, shared by both construction routes.

Quality gate (repo-managed basedpyright)

  • pyproject.toml is tooling-only: [dependency-groups] dev = ["basedpyright>=1.0.0"] + [tool.basedpyright] with typeCheckingMode = "recommended" (basedpyright's default; all rules enabled, less severe findings as warnings, and the default CLI policy fails on warnings — so all reported diagnostics are blocking), scoped to scarcity_router/ + tests/.
  • No [project] table: package name/version/publishing metadata intentionally absent; U-008 remains unresolved (__version__ stays 0.0.0).
  • uv.lock pins basedpyright==1.39.10; the gate invokes uv run basedpyright — the repo-managed environment, never a global executable; plain pyright is not a substitute.
  • Makefile: make test (full-suite unittest discovery over tests/), make typecheck, make check (test + typecheck, required exit 0).
  • AGENTS.md additions: one-issue/one-branch/one-PR workflow invariants and type-checking governance (gate findings must be resolved at the default recommended ruleset; no mode downgrades, broad reportXxx = none suppressions, baselines, or product-code exclusions; narrow suppressions require reported justification).

Verification

  • make check: 84 tests OK + basedpyright 0 errors / 0 warnings / 0 notes, exit 0.
  • 0 suppression directives (# pyright: ignore / # type: ignore) in the repo.
  • 8 changed files vs main: AGENTS.md, Makefile (new), pyproject.toml (new), uv.lock (new), scarcity_router/__init__.py, scarcity_router/capacity.py, scarcity_router/errors.py, tests/test_capacity.py.
  • Linear history, no merge commits.

Scope boundaries

  • No provider adapter or acquisition work (the Z.ai collector is a separate task).
  • No runtime dependencies: product code is stdlib-only; basedpyright is development-only.
  • No CLI/REST/MCP surface; no routing or selection logic. The broker recommends; it does not execute.
Resolves #4. ## Summary Implements the frozen v1 normalized capacity contract from `docs/capacity-model.md` as pure, provider-independent, standard-library-only product code, plus the repository quality gate. Incorporates all fixes from both review rounds. ## Contract implementation (`scarcity_router/`) - `capacity.py` — the four v1 dataclasses (`CapacitySnapshot`, `CapacityWindow`, `LocalRuntime`, `CapacityDiagnostic`), the `_v_*` validators as the single source of truth, `_v_exact_shape` for serialized-shape validation, deterministic JSON-compatible `to_dict`, `SCHEMA_VERSION = 1`. - `errors.py` — `CapacityError` / `CapacityValidationError`. - `__init__.py` — public API exports. ## Review fixes - **Constructor-time validation:** every v1 invariant is enforced in `__post_init__` on all four dataclasses, so an invalid value can never exist as a public/serializable object — via `from_dict()` or a direct constructor call. - **Missing required keys:** `from_dict` raises `CapacityValidationError` (never a raw `KeyError`) and rejects unknown keys; one validation path, shared by both construction routes. ## Quality gate (repo-managed basedpyright) - `pyproject.toml` is tooling-only: `[dependency-groups] dev = ["basedpyright>=1.0.0"]` + `[tool.basedpyright]` with `typeCheckingMode = "recommended"` (basedpyright's default; all rules enabled, less severe findings as warnings, and the default CLI policy fails on warnings — so all reported diagnostics are blocking), scoped to `scarcity_router/` + `tests/`. - **No `[project]` table:** package name/version/publishing metadata intentionally absent; **U-008 remains unresolved** (`__version__` stays `0.0.0`). - `uv.lock` pins **basedpyright==1.39.10**; the gate invokes `uv run basedpyright` — the repo-managed environment, never a global executable; plain `pyright` is not a substitute. - `Makefile`: `make test` (full-suite unittest discovery over `tests/`), `make typecheck`, `make check` (test + typecheck, required exit 0). - `AGENTS.md` additions: one-issue/one-branch/one-PR workflow invariants and type-checking governance (gate findings must be resolved at the default `recommended` ruleset; no mode downgrades, broad `reportXxx = none` suppressions, baselines, or product-code exclusions; narrow suppressions require reported justification). ## Verification - `make check`: **84 tests OK** + basedpyright **0 errors / 0 warnings / 0 notes**, exit 0. - 0 suppression directives (`# pyright: ignore` / `# type: ignore`) in the repo. - **8 changed files** vs `main`: `AGENTS.md`, `Makefile` (new), `pyproject.toml` (new), `uv.lock` (new), `scarcity_router/__init__.py`, `scarcity_router/capacity.py`, `scarcity_router/errors.py`, `tests/test_capacity.py`. - Linear history, no merge commits. ## Scope boundaries - No provider adapter or acquisition work (the Z.ai collector is a separate task). - No runtime dependencies: product code is stdlib-only; basedpyright is development-only. - No CLI/REST/MCP surface; no routing or selection logic. The broker recommends; it does not execute.
Sign in to join this conversation.
No description provided.