Skip to content

Tier 2: 0.2.0 priorities — API contract hardening + GitHub front door #81

Description

@cnicholas

Findings from a three-agent deep audit (public API, internal code health, release readiness). These move the library from "ships" to "polished" — about a week of work distributed across post-release follow-ups before 0.2.0.

These are not blockers for 0.1.0. File Tier 1 (#80) first.

API contract hardening

Highest-ROI bucket: every item below caught the maintainer or audit by surprise at least once.

  • `get_statistics` shape inconsistency. `Histogram` returns `{mean, std, n}`; `X/mR/Xbar/S` return `{N, center, lpl, upl}`. `init.py`'s Quick Start says `stats['center']`. A user copy-pasting against a histogram gets `KeyError`. Fix: align Histogram to `{N, center, ...}` (center=mean, limits=None) OR document the shape difference and add a contract test enumerating shape per chart type.

    • Where: `processbehavior/analysis.py:2544-2548, 2501-2505` (Histogram), `analysis.py:861-870` (others).
  • `'N'`/`'lpl'`/`'upl'` become the string `'Varies'` when subgroup sizes differ. Type pollution: dict claims numeric shape but returns `Union[int|float, str]`. Any `stats['upl'] > x` crashes with `TypeError`.

    • Where: `processbehavior/analysis.py:866-870, 1404-1407`.
    • Fix: keep numeric fields as `None` (or `float('nan')`) when variable; add a separate `'limits_vary': True` flag.
  • Stringly-typed enum parameters. `n_mode: str = 'actual'` accepts only `'actual'` or `'average'`, validated at runtime. Same pattern for `value` (response/R1-R6), `chart` (Xbar/S/X/mR/Histogram), `theme`, `plot(view=...)`. No IDE autocomplete.

    • Where: `processbehavior/study.py:1663, 2296-2299` (`n_mode`).
    • Fix: `Literal["actual", "average"]` for `n_mode`; same treatment for `value`, `chart`, `view`. Pit of success.
  • `observed_design_state` typed as `SDSResult` but field is `SDSResult | None`. Type lies about the optional.

    • Where: `processbehavior/study.py:928, 1032-1043`.
    • Fix: narrow the field type (drop `None`), assert non-None in the property, or admit `Optional` in the signature.
  • `SDSResult` is a public return type but not in `all`. Users wanting `from processbehavior import SDSResult` for type hints must reach into `processbehavior.sds_detector` (also not declared public).

    • Where: `processbehavior/init.py:62-89` vs `study.py:1018, 1032, 1046`.
    • Fix: add `SDSResult` to top-level `all`; add `all` to `exceptions.py` and `sds_detector.py` to lock the public stability promise.
  • `name collision`: `study.factors` is `list[str] | None` while `DesignReport.factors` is `pd.DataFrame`. Same attribute name, different types depending on the object.

    • Where: `processbehavior/study.py:945-955` vs `study.py:151-183`.
    • Fix: rename `DesignReport.factors` to `factors_table` (or method `to_dataframe()`).

chart_info TypedDict-ification

The `chart_info` dict (`{data, statistics, metadata, strata}`) is the single biggest source of class-of-bug: the strata fix (f938fdf) and the Xbar-center dead branch (1444b63) were both producer/consumer disagreements about this contract.

  • Introduce `ChartPayload(TypedDict)` + `ChartMetadata(TypedDict, total=False)` in a shared `analysis_types.py` (or extend `processbehavior/plotting/contracts.py`).
  • Build payloads via the typed dict literal at every `return` site in `processbehavior/analysis.py` (~4 sites: `_calculate_mr_chart_from_precomputed`, `_calculate_mr_chart_stratified`, `_calculate_xbar`, etc.).
  • mypy then catches missing keys before runtime.

Pattern to copy: `plotting/contracts.py:ChartRenderSpec` is already done right (frozen dataclass with `post_init` validation).

GitHub front-door fix

The methodology validation is the marketing centerpiece and currently invisible.

  • Add a Validation section to `README.md` pointing at `validation/e2e_bishop_report.html` and the 280 numerical assertions passing for SDS 1-3. Show the SDS-1 capability table.
  • Set GitHub repo metadata:
    ```bash
    gh repo edit cnicholas/processbehavior \
    --description "Differentiated Python SPC implementing Bishop's Variance Analysis System (VAS)" \
    --homepage "https://cnicholas.github.io/processbehavior/\" \
    --add-topic spc --add-topic control-charts --add-topic statistics \
    --add-topic bishop-vas --add-topic shewhart --add-topic process-behavior
    ```
  • README quickstart should showcase the differentiator — SDS detection + a residual call (`study.execute(chart='Xbar', value='R5')`) — currently it reads like every other SPC wrapper.
    • Where: `processbehavior/init.py:11-26` (also rendered as PyPI landing).
  • Add Bishop-related keywords to pyproject: `"variance-analysis"`, `"bishop-vas"`, `"wheeler"`, `"design-of-experiments"`.
    • Where: `pyproject.toml:14`.

Documentation pruning

  • Remove or move six stale planning docs at `docs/release_gate_0_1_0.md`, `docs/pre_release_audit.md`, `docs/api_ux_review.md`, `docs/chart_statistics.md`, `docs/sampling_plan_design.md`, `docs/sds-detection.md`. They're not in `myst.yml` but MyST may publish them as orphan pages. Per CLAUDE.md anti-patterns: "Don't track planning docs at repo root."
  • Add missing tutorial notebooks to `docs/myst.yml:27-41`: `docs/tutorials/process-capability.ipynb` and `docs/tutorials/loss-function.ipynb` exist on disk but aren't in the TOC — they won't appear on the deployed site.
  • `docs/myst.yml:18` copyright = "2025" while date metadata is 2026 — cosmetic but visible on every doc page footer. Bump to `"2025-2026"`.

CI hardening

  • Notebook execution in CI — `pytest --nbmake docs/tutorials docs/getting-started` would have caught the `chart='Imr'` typo (Tier 1 item) months ago. Add the `nbmake` plugin to `[test]` extra and run notebooks in the test job.
  • publish.yml has no test gate — a tag triggers `build → twine check → publish` with no test step. A regression on `main` that `ci.yml` didn't catch publishes a broken wheel.
    • Where: `.github/workflows/publish.yml`.
    • Fix: `needs: [test, smoke-test]` from a triggered CI run, OR duplicate the smoke-test job into `publish.yml` before the upload step.
  • `pip-audit --skip-editable` silently tolerates CVEs attributed to the editable install path. After publishing 0.1.0, switch to installing the published wheel into a fresh venv and run `pip-audit --strict` against it. (CLAUDE.md anti-pattern flags this for revisit post-publish.)
  • Pre-commit pinned to ruff 0.6.4 while local is 0.14.9. New contributors get drift from their editors.
    • Where: `.pre-commit-config.yaml:3`.
    • Fix: bump rev; add `pre-commit` ecosystem entry to `dependabot.yml`.

Related


Filed from a multi-agent audit synthesizing public-API, code-health, and release-readiness findings against the current library state.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions