diff --git a/.bumpversion.cfg b/.bumpversion.cfg new file mode 100644 index 0000000..54b6c97 --- /dev/null +++ b/.bumpversion.cfg @@ -0,0 +1,10 @@ +[bumpversion] +current_version = 0.0.2 +commit = true +tag = false +tag_name = v{new_version} +message = Bump version: {current_version} → {new_version} + +[bumpversion:file:pyproject.toml] +search = version = "{current_version}" +replace = version = "{new_version}" \ No newline at end of file diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md new file mode 100644 index 0000000..f1ef708 --- /dev/null +++ b/.claude/CLAUDE.md @@ -0,0 +1,78 @@ +--- +last_updated: 2026-07-23 +semver: 0.0.1 +author: Nicholas Bergantz +--- + +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +`py-MathTools` is the **Tier 3 math implementation layer** of the foundation +math tiers defined in py-foundationTools' `mathTypeTiers.md`: it subclasses +the Tier-2 `foundation_abc.math.*` ABCs, chooses numpy-backed storage, and +implements arithmetic, composition, interpolation, DSP, and trajectory +generation on top of the Tier-1 `foundationTypes.mathTypes` data carriers. +Unlike `pyFoundationTools` (zero-dependency by policy), this repo depends on +a curated set of mature numeric packages rather than reimplementing them. + +## Packages and layering + +Two top-level snake_case packages under `src/`, one-way dependency: + +``` +math_plot_helpers → math_tools → pyFoundationTools → stdlib + (matplotlib) (numpy, scipy, numpy-quaternion) +``` + +`math_tools` never imports `math_plot_helpers` or `matplotlib`; only +`math_plot_helpers` imports `matplotlib`. This is enforced by +`tests/test_package_layering.py` (static AST scan of `src/`). + +## Commands + +All workflows go through the Makefile (`make help` lists them); **the +Makefile is the source of truth for tooling**, not this file or the README. +The `uv-` prefixed targets are the primary path. Key ones: + +- `make uv-fullCheck` — CI gate: `uv-lint` + `uv-typecheck` + `uv-test`. Run + this before considering work done. +- `make uv-lint` — ruff check +- `make uv-format` — `ruff format` + `ruff check --fix --unsafe-fixes` +- `make uv-typecheck` — strict `mypy` over `src/` + `tests/` +- `make uv-test` — sync deps then run pytest +- `make uv-refresh` — clean cache + reinstall from `requirements.txt` + + upgrade editable dev install (required after a `pyFoundationTools` pin + changes; a stale `.venv` makes the gate meaningless) + +## Specs + +`.claude/specs/` is the authoritative contract for this repo's architecture +and every module's behavior. Consult the relevant spec before extending a +module; if implementation forces a contract change, the spec is updated in +the same change and its `semver` bumped. + +- [mathToolsArchitecture.md](specs/mathToolsArchitecture.md) — umbrella: + layering, package layout, dependency policy, shared conventions, error + semantics +- [templateConformance.md](specs/templateConformance.md) — template + migration: packaging, Makefile/CI parity, rename, governance docs +- [precisionTimeMath.md](specs/precisionTimeMath.md) — `PrecisionTimeInterval`, + `PrecisionTimestamp` +- [spatialMath.md](specs/spatialMath.md) — `Position`, `Quaternion`, + `SpatialPose` +- [waveformCore.md](specs/waveformCore.md) — `Waveform1D` + aggregate + waveform containers +- [waveformDsp.md](specs/waveformDsp.md) — DSP families, scipy mapping, + support types +- [polynomials.md](specs/polynomials.md) — polynomial type + analytic root + solvers +- [otg.md](specs/otg.md) — online trajectory generation (Ruckig port) + +## Tests + +Tests live in `tests/`, `unittest.TestCase` style run under pytest +(`test*.py` files, `test_*` methods), mirroring the package layout under +`src/` (e.g. `tests/spatial/test_position.py`). diff --git a/.claude/archive/math-tools-port/00-overview.md b/.claude/archive/math-tools-port/00-overview.md new file mode 100644 index 0000000..e820701 --- /dev/null +++ b/.claude/archive/math-tools-port/00-overview.md @@ -0,0 +1,195 @@ +--- +plan: math-tools-port +status: complete +last_updated: 2026-07-23 +semver: 0.1.2 +author: Nicholas Bergantz +--- + +# Action Plan — Template Migration + Swift Math Port + +**Goal:** bring py-MathTools onto the py-foundationTools template conventions +and port the Swift `FoundationMathTypes` capability set (spatial SE(3) types, +precision time, waveforms + DSP, polynomials/roots, OTG trajectory +generation) as the Tier-3 math layer, per the accepted specs in +[`../specs/`](../specs/mathToolsArchitecture.md). + +**Usability north star:** a robotics/DSP engineer in a notebook can build a +waveform or pose, do the obvious math, and reach scipy-grade analysis with +minimal ceremony — well-typed numpy-adjacent Python, not translated Swift. + +## Conventions every chunk inherits (do not restate per chunk) + +1. **Spec is authoritative.** Each chunk lists its governing spec section(s). + If implementation forces a contract change, update the spec in the same + chunk and bump its `semver`. +2. **TDD:** write the failing tests first, implement, then run the gate. +3. **Gate:** `make uv-fullCheck` (ruff lint + mypy strict + pytest) must pass + at the end of every chunk. Test layout mirrors the package + (`tests//test_.py`), `unittest.TestCase` style, `test_*` + methods. +4. **Stay in scope:** touch ONLY the files the chunk lists. Adjacent + problems get reported in the chunk's completion notes, not fixed. +5. **Frontmatter:** every chunk carries `status: pending` → set + `in_progress` while working, then `complete` when done (not `done` + — `complete` is the convention actually used across every chunk file + in this repo); bump `last_updated`. +6. **Swift reference roots** (read-only, for faithful-port chunks): + - `SWIFT_MATH` = `/Users/nbergantz/__Workspaces__/spmWorkspaces/spmMathTools/spm/Sources/spmMathTools/FoundationMathTypes` + - `SWIFT_TYPES` = `/Users/nbergantz/__Workspaces__/spmWorkspaces/spmFoundationTools/spm/Sources/FoundationTypes` + - `SWIFT_TESTS` = `/Users/nbergantz/__Workspaces__/spmWorkspaces/spmMathTools/spm/Tests/spmMathToolsTests` +7. **Shared API idioms** (umbrella spec "API idioms"): `normalized()` method + / `normalize()` in-place; `isclose(rtol, atol)`; `__array__`; + `__hash__ = None` on mutable numpy-backed classes; snake_case throughout. +8. Do not commit; the human reviews and commits per track. + +## Dependency graph + +``` +Track A (template) 01 ──► 02 ──► 03 + │ + ┌────────────────┴───────────────────────────────┐ +Track B │ 04 ─► 05 06 ─► 07 ─► 08 09 10 │ (02 before all B) + │ └──────┬──────────┘└───┬────┘ │ │ +Track C │ ▼ │ │ │ + │ 11 ─► {12, 13, 14} │ │ │ + │ │ │ ▼ │ │ + │ │ │ 15(◄06) 16(◄07) ─► 17(◄08,15,16)│ +Track D │ │ ▼ │ │ + │ │ 18..29 (one per mixin; 26 also ◄19) │ │ + │ │ └────────► 30 (compose) │ │ +Track E │ └──────────────────────────────────────────┘ │ + │ 31(◄10) ─► 32 │ + │ 31 ─► 33 ─► 34 ─► {35, 36, 37, 38, 39} │ + │ {32,35..39} ─► 40 ─► 41 ─► 42 │ + └────────────────────────────────────────────────┘ +``` + +Tracks B/C/D/E parallelize after 01–02; within a track, run in numeric +order unless the graph says otherwise. Chunk 43 (public surface) runs last, +after 09, 17, 30, and 42. + +## Chunk index + +| # | Chunk | Track | Depends on | Spec | +|---|---|---|---|---| +| 01 | [template-rename-and-refresh](01-template-rename-and-refresh.md) | A | — | templateConformance | +| 02 | [errors-and-layering](02-errors-and-layering.md) | A | 01 | umbrella, templateConformance §5 | +| 03 | [governance-and-readme](03-governance-and-readme.md) | A | 02 | templateConformance §3–4 | +| 04 | [precision-time-interval](04-precision-time-interval.md) | B | 02 | precisionTimeMath | +| 05 | [precision-timestamp](05-precision-timestamp.md) | B | 04 | precisionTimeMath | +| 06 | [position](06-position.md) | B | 02 | spatialMath | +| 07 | [quaternion-additions](07-quaternion-additions.md) | B | 06 | spatialMath | +| 08 | [spatial-pose](08-spatial-pose.md) | B | 07 | spatialMath | +| 09 | [univariate-polynomial](09-univariate-polynomial.md) | B | 02 | polynomials | +| 10 | [roots-kernel](10-roots-kernel.md) | B | 02 | polynomials | +| 11 | [waveform1d-core](11-waveform1d-core.md) | C | 05 | waveformCore | +| 12 | [waveform1d-operators](12-waveform1d-operators.md) | C | 11 | waveformCore | +| 13 | [waveform1d-generators](13-waveform1d-generators.md) | C | 11 | waveformCore | +| 14 | [dsp-support-and-protocol](14-dsp-support-and-protocol.md) | C | 11 | waveformDsp | +| 15 | [waveform-position](15-waveform-position.md) | C | 11, 06 | waveformCore | +| 16 | [waveform-quaternion](16-waveform-quaternion.md) | C | 11, 07 | waveformCore | +| 17 | [waveform-spatial-pose](17-waveform-spatial-pose.md) | C | 08, 15, 16 | waveformCore | +| 18 | [dsp-calc](18-dsp-calc.md) | D | 14 | waveformDsp | +| 19 | [dsp-correlation](19-dsp-correlation.md) | D | 14 | waveformDsp | +| 20 | [dsp-envelope](20-dsp-envelope.md) | D | 14 | waveformDsp | +| 21 | [dsp-spectral](21-dsp-spectral.md) | D | 14 | waveformDsp | +| 22 | [dsp-filtering](22-dsp-filtering.md) | D | 14 | waveformDsp | +| 23 | [dsp-peaks](23-dsp-peaks.md) | D | 14 | waveformDsp | +| 24 | [dsp-phase](24-dsp-phase.md) | D | 14 | waveformDsp | +| 25 | [dsp-resampling](25-dsp-resampling.md) | D | 14 | waveformDsp | +| 26 | [dsp-time-alignment](26-dsp-time-alignment.md) | D | 14, 19 | waveformDsp | +| 27 | [dsp-triggers](27-dsp-triggers.md) | D | 14 | waveformDsp | +| 28 | [dsp-windowing](28-dsp-windowing.md) | D | 14 | waveformDsp | +| 29 | [dsp-zero-crossings](29-dsp-zero-crossings.md) | D | 14 | waveformDsp | +| 30 | [dsp-compose](30-dsp-compose.md) | D | 18–29 | waveformDsp | +| 31 | [otg-enums-and-errors](31-otg-enums-and-errors.md) | E | 10 | otg | +| 32 | [otg-input-parameter](32-otg-input-parameter.md) | E | 31 | otg | +| 33 | [otg-profile](33-otg-profile.md) | E | 31 | otg | +| 34 | [otg-block-brake-bound](34-otg-block-brake-bound.md) | E | 33 | otg | +| 35 | [otg-trajectory-and-output](35-otg-trajectory-and-output.md) | E | 34 | otg | +| 36 | [otg-velocity-steps](36-otg-velocity-steps.md) | E | 34 | otg | +| 37 | [otg-position-first-second-steps](37-otg-position-first-second-steps.md) | E | 34 | otg | +| 38 | [otg-position-third-step1](38-otg-position-third-step1.md) | E | 34 | otg | +| 39 | [otg-position-third-step2](39-otg-position-third-step2.md) | E | 34 | otg | +| 40 | [otg-calculator-target](40-otg-calculator-target.md) | E | 32, 35–39 | otg | +| 41 | [otg-driver](41-otg-driver.md) | E | 40 | otg | +| 42 | [otg-oracle-suites](42-otg-oracle-suites.md) | E | 41 | otg | +| 43 | [public-surface](43-public-surface.md) | A | 30, 42, 09, 17 | umbrella | + +--- + +# Corrective actions — 2026-07-22 post-audit + +Chunks 01–43 were audited against their governing specs by five parallel +skeptical auditors (one per track), with the top findings independently +reproduced at runtime and source level before being recorded here. + +**Verdict: substantially complete.** `make uv-fullCheck` is green (exit 0) and +the port is faithful in the large — the 31-case OTG numeric truth table was +re-derived from the Swift source with 0 mismatches, no test in the repo is +skipped or xfail'd, and no unsolved algorithm case, stub, or swallowed `None` +was found in the solvers. The confirmed gaps are specific and are chunked below. + +Chunks 44–57 form **Track F**. They are corrective, not new capability. + +## Confirmed defect summary + +| Class | Count | Where | +|---|---|---| +| (c) drift / correctness | 8 | OTG solvers, spatial/time accessors, waveform indexing | +| (a) not implemented | 6 | aggregate API idioms, subpackage exports, roots degeneracy | +| (b) implemented, untested | ~45 | concentrated in DSP mixins and OTG oracles | +| (e) docs/convention drift | 14 | dead workflow refs, stale tolerances, boilerplate | +| (d) out-of-scope violations | **0** | — none found in any track | + +The single most consequential finding is a **transcription defect** at +`position_third_order_step2.py:1731,1752,1763` (`j·tf⁴` where Swift has +`j·tf³`), confirmed by direct comparison against +`PositionThirdOrderStep2.swift:1279,1286,1289`. It changes profile-branch +selection in ~0.57% of prescribed-duration Step2 solves and is invisible to the +current gate — because the entire 1,784-case OTG oracle corpus is 1-DOF, which +takes a fast path that **never invokes Step2 at all** (measured: 0 invocations). + +## Dependency graph + +``` +Track F (corrective) + + OTG code fixes 44 45 46 + └────┴────┴──────► 56 (OTG oracle closure) + + Track B/C code fixes 47 48 49 ─► 50 51 + └────┴────┴─► 54 (B/C test closure) + + Surface / conventions 52 53 ─────────────► 55 (DSP test closure) + + Everything above ───────────────────────────────► 57 (docs sweep, last) +``` + +44–49, 51, 52, 53 are mutually independent and may run in parallel. +50 waits on 49. The three closure chunks wait on their code fixes. 57 runs last +so it reconciles docs against the post-fix repo. + +## Corrective chunk index + +| # | Chunk | Kind | Depends on | Origin | +|---|---|---|---|---| +| 44 | [otg-step2-udud-discriminant](44-otg-step2-udud-discriminant.md) | fix | — | E-1 | +| 45 | [otg-trivial-profile-length](45-otg-trivial-profile-length.md) | fix | — | E-2 | +| 46 | [otg-step1-sqrt-guard](46-otg-step1-sqrt-guard.md) | fix | — | E-3 | +| 47 | [position-tolerance-and-timestamp-accessors](47-position-tolerance-and-timestamp-accessors.md) | fix | — | B-1, B-2 | +| 48 | [roots-degenerate-cases](48-roots-degenerate-cases.md) | fix | — | B-7, B-14 | +| 49 | [waveform-spatial-pose-indexing](49-waveform-spatial-pose-indexing.md) | fix | — | C-4, C-5 | +| 50 | [aggregate-container-api-idioms](50-aggregate-container-api-idioms.md) | fix | 49 | C-1, C-2, C-3, C-7 | +| 51 | [waveform-generator-phase-conventions](51-waveform-generator-phase-conventions.md) | fix | — | C-15 | +| 52 | [subpackage-public-surface](52-subpackage-public-surface.md) | fix | — | A-1, A-2 | +| 53 | [window-convention-reconciliation](53-window-convention-reconciliation.md) | fix | — | D-windowing | +| 54 | [test-closure-tracks-bc](54-test-closure-tracks-bc.md) | tests | 47–51 | 11 (b) findings | +| 55 | [test-closure-dsp](55-test-closure-dsp.md) | tests | 53 | 23 (b) findings | +| 56 | [test-closure-otg-oracles](56-test-closure-otg-oracles.md) | tests | 44, 45, 46 | 8 (b) findings | +| 57 | [docs-and-convention-sweep](57-docs-and-convention-sweep.md) | docs | 44–56 | 14 (e) findings | + +Findings deliberately **not** actioned (B-4, B-5, B-6, A-7, D-cross-cutting, +D-spectral-(c), E-10) are recorded with rationale in chunk 57's +"Recorded — no action" section, so the decisions are not relitigated. diff --git a/.claude/archive/math-tools-port/01-template-rename-and-refresh.md b/.claude/archive/math-tools-port/01-template-rename-and-refresh.md new file mode 100644 index 0000000..2d09849 --- /dev/null +++ b/.claude/archive/math-tools-port/01-template-rename-and-refresh.md @@ -0,0 +1,123 @@ +--- +chunk: 01-template-rename-and-refresh +track: A +status: complete +depends_on: [] +spec: ../specs/templateConformance.md §Gap 1, §Gap 2.4; ../specs/spatialMath.md §Modules (ABC re-parent) +last_updated: 2026-07-11 +semver: 0.0.1 +author: Nicholas Bergantz +--- + +# 01 — Package rename + environment refresh + +**Deliverable:** the repo builds and gates green under the new snake_case +package names against the pinned foundation branch. Mechanical migration — +no behavior changes beyond the required ABC re-parent. + +## Files + +- `git mv src/pyMathTools src/math_tools`; inside it: + `git mv src/math_tools/spatial/Quaternion.py src/math_tools/spatial/quaternion.py`, + `git mv src/math_tools/spherical/sphericalGenerators.py src/math_tools/spherical/spherical_generators.py`, + `git mv src/math_tools/spherical/sphericalTransforms.py src/math_tools/spherical/spherical_transforms.py` + (`constructors.py`, `hints.py` keep their names). +- `git mv src/pyMathToolsPlotHelpers src/math_plot_helpers`; + `git mv src/math_plot_helpers/plotUnitSpherical.py src/math_plot_helpers/plot_unit_spherical.py`. +- Edit: `src/math_tools/spatial/quaternion.py` (imports/base only, see below), + every `__init__.py` touched by moves, `tests/test_quaternion.py`, + `examples/sphericalPlotting/plotArcs.py`, + `examples/sphericalPlotting/plotQuatUnitCircles.py`, `Makefile` (mypy + package list if it names packages), `.env` if it names packages. +- Add: `py.typed` in `src/math_tools/` and `src/math_plot_helpers/` roots + (keep the existing ones in subpackages). + +## Design constraints + +1. **First action:** `make uv-refresh` so `.venv` matches the + `requirements.txt` branch pin (the stale install has the pre-template ABC + layout; nothing imports correctly until this runs). +2. **ABC re-parent (the one semantic edit):** in `quaternion.py`, replace + `from foundationTypes.mathTypes.quaternionABC import QuaternionABC` with + `from foundation_abc.math.spatialABCs import QuaternionABC`. The old ABC + carried `DataModelHelper`; the new one is ABC-only with a concrete + `to_dict`. In `tests/test_quaternion.py`, update the serialization tests' + base-class assertions (`DataModelHelper` inheritance assertion → the new + ABC) — assertions on `to_dict`/`from_dict` *values* stay untouched. If any + other import from the old layout exists (grep `foundationTypes.mathTypes.` + across `src/`), re-point to `foundation_abc.math.*` / + `foundationTypes.mathTypes.MathTypes` equivalents. +3. All other edits are import-path text substitutions + (`pyMathTools` → `math_tools`, `pyMathToolsPlotHelpers` → + `math_plot_helpers`, moved module filenames). +4. `pyproject.toml`: update `description` only if trivially co-located; the + deps list changes in chunk 03, not here. + +## TDD steps + +1. `make uv-refresh`; run `make uv-test` to record the pre-existing pass/fail + baseline (the ABC import may already be broken — note it). +2. Perform moves + edits. +3. `make uv-fullCheck` green. + +## Acceptance criteria + +- [x] `grep -rn "pyMathTools" src/ tests/ examples/ Makefile .env pyproject.toml` → no hits +- [x] `grep -rn "foundationTypes.mathTypes.quaternionABC" src/ tests/` → no hits +- [x] `git log --follow --oneline src/math_tools/spatial/quaternion.py` shows history (moves were `git mv`) — verified via `git status`/`git diff --staged` showing `renamed: src/pyMathTools/spatial/Quaternion.py -> src/math_tools/spatial/quaternion.py`; `--follow` itself needs a commit to walk, which this chunk intentionally leaves to the supervising process +- [x] All ~90 quaternion tests pass (89 collected/passed); diff to `tests/test_quaternion.py` contains only import lines, base-class assertion lines, and their two adjacent docstring lines (see Resolution notes) +- [x] `make uv-fullCheck` passes + +## Out of scope + +`errors.py`, layering test, README, `.claude/CLAUDE.md`, pyproject dependency +list, any new math code, any `__init__.py` re-export curation beyond fixing +broken imports. + +## Resolution notes + +- `make uv-refresh` pulled the pinned foundation branch fresh; pre-move + `make uv-test` baseline reproduced the expected pre-existing break + (`ModuleNotFoundError: foundationTypes.mathTypes.quaternionABC`), confirming + the venv was stale before this chunk and the gate is meaningful after. +- Moves done via `git mv` as specified. `git status`/`git diff --staged` + correctly report `quaternion.py` as a rename from `Quaternion.py`; git's + similarity heuristic cross-matched some of the (byte-identical, empty) + `__init__.py` files to different-but-equivalent old empty `__init__.py` + paths — cosmetic only, every file's on-disk destination was verified + directly with `find`, and it does not affect `--follow` on the files that + matter (confirmed for `quaternion.py`). +- The old-layout ABC repoint (design constraint 2) turned out to reach beyond + `quaternion.py`: `foundationTypes.mathTypes.unitSphericalArcABC` and + `unitSphericalSmallCircleABC` (imported by `spherical_generators.py`, + `spherical_transforms.py`, `constructors.py`, `plot_unit_spherical.py`) + were also deleted from the pinned foundation branch and now live at + `foundation_abc.math.sphericalABCs`. Re-pointed all of them per the chunk's + explicit "grep `foundationTypes.mathTypes.` across `src/`" instruction — + required for `make uv-fullCheck` (mypy strict) to pass, since mypy scans + all of `src/`, not just the quaternion module. `foundationTypes.mathTypes.MathTypes.*` + imports (the codegen `*Type` classes) were left untouched — that module + still exists unchanged in the new layout. + This is a scope note, not a spec deviation: constraint 2's own text + authorized exactly this action; the "Files" list section above just didn't + enumerate every file it touched. +- `examples/sphericalPlotting/plotArcs.py` and `plotQuatUnitCircles.py` got + only the mechanical package-name substitution, per constraint 3 and Gap 4's + explicit ownership of their pre-existing broken `foundationTypes.mathTypes.UnitSphericalArc` + / `UnitSphericalSmallCircle` imports (not part of this chunk's scope, and + not scanned by `make uv-typecheck` since `PY_EXAMPLES` is unset in `.env`). +- `tests/test_quaternion.py`: `test_inheritance_from_data_model_helper` + asserted `isinstance(q, DataModelHelper)`, which is no longer true (the new + `QuaternionABC` is ABC-only). Repointed the import and assertion to + `QuaternionABC` per constraint 2's directive, and updated that test's and + the class's docstrings by one line each so the docstrings don't contradict + the assertion right below them — the only lines in this diff beyond raw + import-path substitution. No `to_dict`/`from_dict` value assertions were + touched. +- Added `py.typed` at `src/math_tools/` and `src/math_plot_helpers/` package + roots (subpackage ones already existed). +- No `Makefile`/`.env` edits were needed — neither names packages explicitly + (both scope quality targets via path variables, not package names). +- `pyproject.toml` `description` left untouched — no trivially co-located + edit was applicable in this chunk; still boilerplate text, tracked as Gap 4 + (chunk 03). diff --git a/.claude/archive/math-tools-port/02-errors-and-layering.md b/.claude/archive/math-tools-port/02-errors-and-layering.md new file mode 100644 index 0000000..78fdbfb --- /dev/null +++ b/.claude/archive/math-tools-port/02-errors-and-layering.md @@ -0,0 +1,74 @@ +--- +chunk: 02-errors-and-layering +track: A +status: complete +depends_on: [01] +spec: ../specs/mathToolsArchitecture.md §Error semantics; ../specs/templateConformance.md §Gap 5 +last_updated: 2026-07-11 +semver: 0.0.1 +author: Nicholas Bergantz +--- + +# 02 — Exception hierarchy + package layering test + +**Deliverable:** `math_tools/errors.py` and the AST-based layering test. + +## Files + +- Create: `src/math_tools/errors.py` +- Create: `tests/test_package_layering.py` +- Create: `tests/test_errors.py` + +## Design constraints + +1. `errors.py` defines exactly (each with a one-line docstring): + `MathToolsError(Exception)`, `WaveformCompatibilityError(MathToolsError)`, + `TimestampComparisonError(MathToolsError)`, + `PolynomialSolveError(MathToolsError)`. +2. Layering test pattern: copy the approach of py-foundationTools + `tests/test_package_layering.py` (AST-walk every module under `src/`, + collect `import`/`from` roots). Assertions per templateConformance §Gap 5: + `math_tools` imports neither `math_plot_helpers` nor `matplotlib`; + only `math_plot_helpers` imports `matplotlib`. +3. Additionally assert `math_tools.otg` (once it exists) does not import + `numpy` — write the rule now, guarded to skip if the package dir is + absent, so OTG chunks inherit enforcement for free. + +## TDD steps + +1. Write `tests/test_errors.py` (hierarchy, catchability as `MathToolsError`) + and `tests/test_package_layering.py`; watch errors test fail. +2. Implement `errors.py`; layering test must pass against the current tree. +3. Temporarily add `import matplotlib` to a `math_tools` module and confirm + the layering test fails; revert. `make uv-fullCheck` green. + +## Acceptance criteria + +- [x] All four exception classes exist and subclass as specified +- [x] Layering test fails on an injected `import matplotlib` in `math_tools` (verified then reverted) +- [x] `make uv-fullCheck` passes + +## Out of scope + +Any consumer of the exceptions; OTG's `OtgError` (lives in `otg/errors.py`, +chunk 31); README/CLAUDE.md. + +## Resolution notes + +- `errors.py` implements exactly the four classes from the spec, each a + one-line docstring, no added behavior. +- `test_package_layering.py` follows the py-foundationTools AST-scan pattern + (`tests/test_package_layering.py` there) rather than executing imports, so + it can't be defeated by import side effects. Four checks: `math_tools` ↛ + `math_plot_helpers`, `math_tools` ↛ `matplotlib`, only `math_plot_helpers` + → `matplotlib` (scans all of `src/` excluding that package), and the + guarded `math_tools.otg` ↛ `numpy` rule (currently a no-op skip since + `otg/` doesn't exist yet — will activate automatically once chunk 31 lands). +- Verification step 3 (inject `import matplotlib` into `math_tools/errors.py`, + confirm two layering assertions fail, revert) was done live against the + actual gate, not simulated; file diffed back to the original after. +- One ruff fix needed: the injected-violation assertion message exceeded the + 100-char line limit in `test_only_math_plot_helpers_imports_matplotlib`'s + sibling test; wrapped the f-string across two lines. +- No spec changes were required — the chunk's design constraints matched the + umbrella spec's Error semantics section exactly. diff --git a/.claude/archive/math-tools-port/03-governance-and-readme.md b/.claude/archive/math-tools-port/03-governance-and-readme.md new file mode 100644 index 0000000..804cdab --- /dev/null +++ b/.claude/archive/math-tools-port/03-governance-and-readme.md @@ -0,0 +1,106 @@ +--- +chunk: 03-governance-and-readme +track: A +status: complete +depends_on: [02] +spec: ../specs/templateConformance.md §Gap 2, §Gap 3, §Gap 4 +last_updated: 2026-07-11 +semver: 0.0.2 +author: Nicholas Bergantz +--- + +# 03 — Governance docs, README, dependency names + +**Deliverable:** `.claude/CLAUDE.md`, real README, pyproject dependency +names, fixed examples. + +## Files + +- Create: `.claude/CLAUDE.md` +- Edit: `README.md`, `pyproject.toml` (deps + description only), + `examples/sphericalPlotting/plotArcs.py`, + `examples/sphericalPlotting/plotQuatUnitCircles.py` + +## Design constraints + +1. `.claude/CLAUDE.md` follows the shape of py-foundationTools + `.claude/CLAUDE.md` but stays short: repo role (Tier 3 of foundation's + `mathTypeTiers.md`), the two-package layering diagram, gate command, + Makefile-is-source-of-truth note, and a linked index of every spec in + `.claude/specs/`. Reference specs; never duplicate their content. +2. `pyproject.toml` `dependencies` = names only: + `pyFoundationTools`, `numpy`, `scipy`, `numpy-quaternion`, `matplotlib`. + Replace the boilerplate `description`. +3. README per templateConformance Gap 4: describe only what exists at + execution time (quaternion, spherical utilities, template workflows); + sections: title → Features → Installation → Quick Start → Development + Workflows → Requirements. Quick Start snippets must actually run. +4. `plotArcs.py`: fix the dead + `foundationTypes.mathTypes.UnitSphericalArc.UnitSphericalArc` import to + the real generated type in `foundationTypes.mathTypes.MathTypes` + (verify the class name by reading that module). + +## TDD steps + +1. Add a test `tests/test_governance.py`: `.claude/CLAUDE.md` exists and its + text links every `*.md` in `.claude/specs/`; `README.md` contains no + "Boilerplate". Watch it fail. +2. Write the docs; run both example scripts manually + (`uv run python examples/sphericalPlotting/plotArcs.py` with a + non-interactive matplotlib backend) to prove imports resolve. +3. `make uv-fullCheck` green. + +## Acceptance criteria + +- [x] `tests/test_governance.py` passes +- [x] `pyproject.toml` deps are exactly the five names, unpinned +- [x] Both example scripts import-run without error +- [x] `make uv-fullCheck` passes + +## Out of scope + +Makefile/CI edits; requirements pin changes; any `src/` code beyond the +example imports. + +## Resolution notes + +- `tests/test_governance.py` added: asserts `.claude/CLAUDE.md` exists and + its text contains the filename of every `.claude/specs/*.md` file, and + that `README.md` contains no `"Boilerplate"` text. Confirmed it failed + before the docs existed, passed after. +- `.claude/CLAUDE.md` written to the shape of py-foundationTools' + `.claude/CLAUDE.md` (Project Overview → layering → Commands → Specs index + → Tests) but scoped to what exists in this repo today: Tier 3 role, the + two-package layering diagram, the gate command, a Makefile-is-source-of- + truth note, and a linked index of all 7 specs in `.claude/specs/`. +- `README.md` rewritten per Gap 4's section shape (title → Features → + Installation → Quick Start → Development Workflows → Requirements), + describing only what exists at execution time: `Quaternion`, the + spherical arc/small-circle utilities, `errors.py`, and the + `math_plot_helpers` plotting package. Both Quick Start snippets + (quaternion arithmetic/conversion, spherical arc construction + + endpoint) were executed directly to confirm they run as written. +- `pyproject.toml`: `dependencies` set to the five names + (`pyFoundationTools`, `numpy`, `scipy`, `numpy-quaternion`, `matplotlib`) + — the prior list was missing `numpy`/`scipy` and had an unrelated stray + order; `description` replaced with a one-line Tier-3 summary. +- `examples/sphericalPlotting/plotArcs.py`: fixed the dead + `foundationTypes.mathTypes.UnitSphericalArc.UnitSphericalArc` import to + `foundationTypes.mathTypes.MathTypes.UnitSphericalArcType` (verified the + real class name by reading the installed `MathTypes.py`) and updated the + two local usages/type hints accordingly. +- `examples/sphericalPlotting/plotQuatUnitCircles.py`: same dead-import + pattern existed for `UnitSphericalSmallCircle` (not called out by name in + the chunk's design constraint 4, but the file was listed for edit and the + acceptance criterion requires *both* scripts to import-run without + error) — fixed to `foundationTypes.mathTypes.MathTypes.UnitSphericalSmallCircleType`. +- Both example scripts verified to run end-to-end with + `MPLBACKEND=Agg PYTHONPATH=src .venv/bin/python examples/sphericalPlotting/