Skip to content

Commit 9b97684

Browse files
committed
Harden color contracts and repo legibility
1 parent f4651ae commit 9b97684

17 files changed

Lines changed: 939 additions & 139 deletions

.github/workflows/python-package.yml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,22 @@ jobs:
2525
with:
2626
python-version: ${{ matrix.python-version }}
2727
- name: Install PDM
28-
run: |
29-
python -m pip install --upgrade pip
30-
python -m pip install pdm
28+
uses: pdm-project/setup-pdm@v4
3129
- name: Install dependencies
3230
run: |
3331
pdm install --group dev
3432
- name: Lint with ruff
3533
run: |
3634
pdm run ruff check src tests --statistics
35+
- name: Type check with mypy
36+
run: |
37+
pdm run mypy src tests
38+
- name: Check repo legibility
39+
run: |
40+
pdm run check:repo
3741
- name: Test with pytest
3842
run: |
3943
pdm run pytest
44+
- name: Build package
45+
run: |
46+
pdm build

AGENTS.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Agent Map
2+
3+
Start with [docs/index.md](docs/index.md). It is the map for architecture,
4+
API contracts, quality gates, release flow, and current legibility debt.
5+
6+
## Working Rules
7+
8+
- Use PDM for all Python environment and dependency work.
9+
- When adding package dependencies, resolve the latest available version and
10+
keep constraints as `>=...` unless a precise pin is required for function.
11+
- Do not add tests that only prove removed functionality is gone. Removals
12+
should delete and simplify unless a positive replacement contract needs tests.
13+
14+
## Main Commands
15+
16+
- `pdm run lint`
17+
- `pdm run typecheck`
18+
- `pdm run check:repo`
19+
- `pdm run test`
20+
- `pdm run check`
21+
- `pdm build`
22+
23+
## Code Map
24+
25+
- `src/paintcan/hsba_color.py`: immutable HSBA value object and component bounds.
26+
- `src/paintcan/color_scheme.py`: immutable sequence of generated colors.
27+
- `src/paintcan/__main__.py`: terminal demo only; keep library behavior in the
28+
domain modules.
29+
- `tests/`: public contract tests plus repo-legibility checks.

README.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@
99
## Features
1010

1111
- **HSBAColor**: A value-type representation of color using Hue, Saturation, Brightness, and Alpha (0.0 - 1.0).
12+
- Validates all components on construction.
1213
- Cyclic Hue adjustment (wrapping).
13-
- Clamping and Overflow support for Saturation and Brightness.
14+
- Clamping and full-interval Overflow support for Saturation and Brightness.
1415
- Pythonic unpacking: `h, s, b, a = color`.
1516
- **ColorScheme**: A collection of colors generated by color theory rules.
16-
- Supports the Sequence protocol (`len(scheme)`, `scheme[0]`, iteration).
17+
- Immutable and supports the Sequence protocol (`len(scheme)`, `scheme[0]`, slicing, iteration).
1718
- 8 factory methods for generating harmonious schemes:
1819
- Analogous
1920
- Accented Analogous
@@ -88,6 +89,16 @@ pdm run python -m paintcan
8889
python -m paintcan
8990
```
9091

92+
## Development
93+
94+
The maintainer and agent docs live in [`docs/index.md`](docs/index.md). For local validation:
95+
96+
```bash
97+
pdm install --group dev
98+
pdm run check
99+
pdm build
100+
```
101+
91102
## License
92103

93104
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

docs/api-contracts.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# API Contracts
2+
3+
This document records behavior that should remain stable unless the package is
4+
intentionally changing its public contract.
5+
6+
## `HSBAColor`
7+
8+
`HSBAColor(hue, saturation, brightness, alpha)` is an immutable value object.
9+
Each component must be in `0.0 <= value <= 1.0`; invalid construction raises
10+
`ValueError`.
11+
12+
### Adjustment Methods
13+
14+
- `adjust_hue(change)` accepts `-1.0 <= change <= 1.0` and wraps the result.
15+
- `complement()` is equivalent to a half-turn hue adjustment.
16+
- `adjust_saturation(change, floor=0.0, ceiling=1.0, overflow=False)` clamps by
17+
default and wraps by full intervals when `overflow=True`.
18+
- `adjust_brightness(change, floor=0.0, ceiling=1.0, overflow=False)` follows
19+
the same bounds behavior as saturation.
20+
- `adjust_alpha(change, floor=0.0, ceiling=1.0)` clamps only.
21+
22+
Custom floors and ceilings must also be inside `0.0..1.0`, and `floor` must be
23+
less than or equal to `ceiling`.
24+
25+
### Random Colors
26+
27+
`HSBAColor.random()` always creates a color with alpha `1.0`. Saturation and
28+
brightness ranges are validated before sampling.
29+
30+
## `ColorScheme`
31+
32+
`ColorScheme(colors)` is a read-only sequence of one or more `HSBAColor` values.
33+
The constructor normalizes iterable input to a tuple. Empty schemes raise
34+
`ValueError`.
35+
36+
Every factory method returns five colors:
37+
38+
- `from_analogous`
39+
- `from_accented_analogous`
40+
- `from_complementary`
41+
- `from_compound`
42+
- `from_monochromatic`
43+
- `from_shades`
44+
- `from_split_complementary`
45+
- `from_triadic`
46+
47+
The first color is always the theme color and is also available through
48+
`scheme.theme_color`.

docs/architecture.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Architecture
2+
3+
PaintCan is a small, typed Python package for HSBA color values and deterministic
4+
color-scheme generation. The intended shape is deliberately simple:
5+
6+
1. `HSBAColor` is the core immutable value object.
7+
2. `ColorScheme` is an immutable sequence wrapper around generated colors.
8+
3. The terminal demo renders colors for human inspection but does not own domain
9+
behavior.
10+
11+
## Layers
12+
13+
### Domain Values
14+
15+
`src/paintcan/hsba_color.py` owns all HSBA component invariants:
16+
17+
- hue, saturation, brightness, and alpha are floats in `0.0 <= value <= 1.0`;
18+
- hue adjustment wraps cyclically;
19+
- saturation and brightness can clamp or overflow within caller-provided bounds;
20+
- alpha always clamps and never overflows;
21+
- random color ranges are validated before any color is constructed.
22+
23+
Code outside this module should not reimplement component bounds. It should rely
24+
on `HSBAColor` construction and adjustment methods.
25+
26+
### Scheme Generation
27+
28+
`src/paintcan/color_scheme.py` owns all palette factory methods. Every factory:
29+
30+
- returns exactly five colors;
31+
- preserves the first color as `theme_color`;
32+
- returns a read-only `ColorScheme`;
33+
- keeps every generated HSBA component inside `0.0..1.0`.
34+
35+
Scheme generation is intentionally algorithmic and local. If more schemes are
36+
added, keep the scheme rules readable in this module until repeated operations
37+
make a small internal helper clearly worthwhile.
38+
39+
### Demo Entrypoint
40+
41+
`src/paintcan/__main__.py` is a visual terminal demo. It may convert HSBA to RGB
42+
for ANSI output, but library callers should import from `paintcan` instead of
43+
depending on demo helpers.
44+
45+
## Dependency Direction
46+
47+
- `paintcan.__init__` re-exports the public API.
48+
- `color_scheme` may depend on `hsba_color`.
49+
- `hsba_color` must not depend on scheme or demo code.
50+
- `__main__` may depend on both public domain modules.
51+
- Tests may import the public package and inspect internals only for repo
52+
structure checks.

docs/index.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# PaintCan Docs
2+
3+
This directory is the repo-local system of record for maintainers and coding
4+
agents. Keep the README focused on package users, and keep durable maintenance
5+
truth here.
6+
7+
## Read By Task
8+
9+
- Understand the library shape: [architecture.md](architecture.md)
10+
- Check exact public behavior: [api-contracts.md](api-contracts.md)
11+
- Run or update validation: [quality.md](quality.md)
12+
- Prepare a release: [releasing.md](releasing.md)
13+
- Improve repo legibility: [legibility-audit.md](legibility-audit.md)
14+
15+
## Source Map
16+
17+
- `src/paintcan/hsba_color.py` owns HSBA component validation and adjustments.
18+
- `src/paintcan/color_scheme.py` owns generated scheme collections.
19+
- `src/paintcan/__main__.py` owns the terminal demo.
20+
- `tests/test_hsba_color.py` and `tests/test_color_scheme.py` encode the public
21+
behavior contracts.
22+
- `tests/test_repo_legibility.py` keeps the docs and validation surface wired.
23+
24+
## Planning Policy
25+
26+
Planning docs should stay forward-looking. Once work is implemented, move durable
27+
truth into the relevant current-state document instead of leaving completed task
28+
history in a plan.

docs/legibility-audit.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Legibility Audit
2+
3+
Last updated: 2026-05-08
4+
5+
## Current Strengths
6+
7+
- The package has a small public API with clear module ownership.
8+
- PDM owns dependency and build workflows.
9+
- CI already validates the package across the supported Python versions.
10+
- The code has focused pytest coverage for the two domain types.
11+
12+
## Gaps Closed In This Pass
13+
14+
- Added `AGENTS.md` as a short routing map instead of relying on chat-provided
15+
instructions.
16+
- Added this indexed `docs/` spine so architecture, API contracts, quality, and
17+
release flow are discoverable in the repo.
18+
- Added strict mypy type checking as a local and CI validation lane.
19+
- Added repo-legibility tests so docs routes and validation commands cannot drift
20+
silently.
21+
- Tightened tests around HSBA invariants, overflow behavior, immutable schemes,
22+
and generated scheme bounds.
23+
24+
## Remaining Pressure Points
25+
26+
- Scheme factory definitions are still hand-written chains. That is acceptable
27+
at the current size, but the next family of schemes should revisit whether a
28+
small internal operation helper would make the palette rules easier to audit.
29+
- There is no generated public API inventory. If the package grows beyond the two
30+
current public classes, add a generated or test-enforced inventory under
31+
`docs/generated/`.
32+
- The terminal demo is intentionally simple. If it grows into a richer CLI, split
33+
CLI formatting from demo orchestration and document that boundary here.
34+
35+
## Entropy Control
36+
37+
Keep `tests/test_repo_legibility.py` cheap and focused. When a future review
38+
finds drift that can be expressed mechanically, prefer adding one targeted
39+
assertion there over expanding `AGENTS.md`.

docs/quality.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Quality
2+
3+
PaintCan is a tiny library, so quality gates should stay fast and contract
4+
oriented. The full local validation loop is:
5+
6+
```bash
7+
pdm run lint
8+
pdm run typecheck
9+
pdm run check:repo
10+
pdm run test
11+
pdm build
12+
```
13+
14+
For a single command before handing work back, run:
15+
16+
```bash
17+
pdm run check
18+
```
19+
20+
`pdm run check` covers linting, strict mypy type checking, repo-legibility
21+
checks, and the pytest suite. Run `pdm build` as well before release or packaging
22+
changes.
23+
24+
## Test Contracts
25+
26+
Tests should protect user-facing behavior rather than mirror implementation
27+
coincidence:
28+
29+
- HSBA components stay inside `0.0..1.0`.
30+
- Invalid construction and invalid adjustment bounds raise clear `ValueError`s.
31+
- Overflow wraps by full intervals, not by a single correction step.
32+
- `ColorScheme` is immutable and sequence-like.
33+
- Every scheme factory returns five in-range colors with the theme first.
34+
- Documentation and CI keep pointing at the current validation surface.
35+
36+
## Static Checks
37+
38+
The package ships `py.typed`, so type checking is part of the public maintenance
39+
contract. Keep `pdm run typecheck` green when public signatures change.
40+
41+
## Dependency Policy
42+
43+
Use PDM for dependency management. When adding dependencies, resolve the latest
44+
available version and store constraints with `>=...` unless an exact pin is
45+
necessary for runtime correctness.

docs/releasing.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Releasing
2+
3+
PaintCan uses SCM-derived versions through PDM. Release work should preserve the
4+
local validation contract before publishing.
5+
6+
## Local Release Checklist
7+
8+
1. Run the full validation loop:
9+
10+
```bash
11+
pdm run lint
12+
pdm run typecheck
13+
pdm run check:repo
14+
pdm run test
15+
pdm build
16+
```
17+
18+
2. Confirm the built artifacts under `dist/` have the expected version.
19+
3. Tag the release with a `vX.Y.Z` tag.
20+
4. Publish a GitHub release from that tag.
21+
22+
## GitHub Automation
23+
24+
- `.github/workflows/python-package.yml` validates pushes and pull requests
25+
across supported Python versions.
26+
- `.github/workflows/draft-release-notes.yml` drafts release notes for
27+
`v*.*.*` tags.
28+
- `.github/workflows/python-publish.yml` publishes to PyPI when a GitHub release
29+
is published.
30+
31+
The publish workflow uses PyPI trusted publishing. Keep package metadata in
32+
`pyproject.toml` aligned with the README and supported Python classifiers.

0 commit comments

Comments
 (0)