Developer Guide#

Installation From Source (Development)#

Clone the repository and install in editable mode with the dev dependency group (which provides pytest, pre-commit, and ipython):

pip install -e . --group dev

Project Layout#

historia/
├── src/historia/         # Main package source
│   ├── _cli.py           # CLI entry points
│   └── data/
│       └── github/       # GitHub data fetch/dump/update APIs
├── tests/                # Pytest test suite
├── docs/                 # Sphinx documentation source
├── AGENTS.md             # Agent/AI contributor instructions
├── CHANGELOG.md          # Release changelog
└── pyproject.toml        # Project metadata and tool configuration

Testing#

This project uses pytest for its test suite.

python -m pytest

Assertion Style#

Always place the actual (observed) value on the left and the expected value on the right:

# Correct
assert actual_value == expected_value

# Incorrect
assert expected_value == actual_value

AI-Generated Tests#

Tests written or substantially assisted by AI must be marked with the ai_generated marker:

import pytest

@pytest.mark.ai_generated
def test_something():
    ...

Use pytest.mark.parametrize wherever appropriate to reduce test duplication.

Code Quality (Pre-commit Hooks)#

This project uses pre-commit to enforce code quality automatically before each commit.

The configured hooks are:

  • black — code formatting

  • ruff — linting and import sorting (with auto-fix)

  • codespell — spell checking

  • mypy — static type checking

Install the hooks once after cloning:

pre-commit install

Run all hooks manually against all files (recommended before opening a PR):

pre-commit run --all-files

Pre-commit will auto-fix most formatting and lint issues. Re-run the command afterward to confirm everything passes.

CLI#

The Historia package exposes a single entry point:

  • historia — primary entrypoint

Commands are organized into sub-groups:

  • historia update — data management

    • historia update github — fetch raw GitHub activity data for a user

  • historia project — GitHub Project management

    • historia project create — create a new GitHub Project page

    • historia project populate — populate a project from fetched data

    • historia project update dates — update item date fields in a project

    • historia project transition — transition project items between statuses

Since every command requires at least one option, running any command or sub-group without arguments automatically prints its help text:

historia
historia update
historia project

Changelog#

Add a short description of any user-facing change to the ## Upcoming section of CHANGELOG.md under the appropriate subsection:

  • ### 🚀 Enhancement — new features

  • ### 🐛 Bug Fix — bug fixes

  • ### 📝 Documentation — documentation-only changes

  • ### 🏠 Internal — refactors, tooling, CI, and test infrastructure

  • ### 🔩 Dependency Updates — dependency-only changes

Include the GitHub PR link at the end of each entry:

- Brief description of the change. ([#N](https://github.com/CodyCBakerPhD/historia/pull/N))

Building Documentation#

Building Locally#

Install the docs dependencies:

pip install -e . --group docs

Build the HTML output:

python -m sphinx -b html docs docs/_build/html

Open docs/_build/html/index.html in your browser to preview the result.

AI Usage Addendum#

Materials across repositories in this organization have been prepared with the assistance of agentic AI coding tools and large language models (LLMs), alongside direct human authoring and supervision.

Our working practice is: 👤 Human review and accountability. We strive to have final versions of all work — prose, code, figures, and specifications — to be reviewed and/or edited by human authors, who remain accountable for the content. 🪪 Transparent annotation. Where AI tools materially contributed to a commit, we strive to annotate that commit adequately and trustworthily (for example, via a Co-Authored-By trailer identifying the tool and model version), so that the provenance of individual changes can be inspected in the git history. 🔍 Inspectable provenance. Because all work is developed in public git repositories with pull-request review, readers can trace portions of the materials which involved AI assistance.

AI tools are used as assistive instruments; they do not substitute for the scientific judgment, domain expertise, or editorial responsibility of the human authors.

Commit Co-Authorship#

When an AI tool (agent, coding assistant, IDE integration) materially contributes to a commit, the commit MUST include a Co-Authored-By trailer identifying both the tool and its version and the model and its version. This makes the git history the inspectable provenance record so anyone reading the repo can tell which tool produced which change.

Format (replace all <…> placeholders with actual values):

Co-Authored-By: <Tool> <tool-version> / <Model> <model-version> <noreply@<vendor-domain>>

Discover the tool version from the tool itself (commonly --version) and use the model identifier the tool reports. Don’t guess.

Example: Claude Code#

Co-Authored-By: Claude Code 2.1.63 / Claude Opus 4.6 <noreply@anthropic.com>

claude --version reports the Claude Code CLI version; the active model is reported by the CLI in its status / via /model.

Agent / AI Contributor Instructions#

Agent instructions#

Commits and PRs#

  • Always run pre-commit before committing and pushing changes

  • Always link PRs to issues when possible

  • PR titles should be human-readable and in the past tense. They should NOT use conventional commit style.

  • Every commit must include a Co-Authored-By trailer identifying your tool name and version and your underlying model and version. Format (replace all <…> placeholders with actual values): Co-Authored-By: <Tool> <tool-version> / <Model> <model-version> <noreply@vendor-domain>

Versioning and changelog#

  • Bump the version in pyproject.toml once per PR when any file under src/ or pyproject.toml itself changes. Do NOT bump for changes that are purely CI/workflow, documentation, or configuration (e.g., GitHub Actions workflows, AGENTS.md, README.md badges).

  • Add a short entry to the ## Upcoming section of CHANGELOG.md under the appropriate subsection (### 🚀 Enhancement, ### 🐛 Bug Fix, ### 📝 Documentation, ### 🔩 Dependency Updates, ### 🏠 Internal). Include the PR link at the end of each entry in the format ([#N](https://github.com/<org>/<repo>/pull/N)). Create the ## Upcoming section or subsection if it does not yet exist.

Code style#

  • Require keyword-only arguments (*, ...) for multi-input functions. For any function with exactly one caller-supplied parameter (excluding self and cls), require positional-only usage with the / designator.

  • Always add new imports at the top of the file. The only exception is when a local import is needed to avoid a circular dependency.

  • For external dependencies, use the full module import style (e.g., import xyz; xyz.abc) rather than from xyz import abc.

  • For internal imports, always use relative style (e.g., from .foo import bar).

  • Prefer assigning return values to named locals before return when this improves readability and debugger breakpoint placement.

  • Avoid excessive em-dashes, colons, and semicolons in written text such as documentation. Prefer breaking into separate, shorter sentences instead.

  • Favor defining one-word names for CLI flags, then map those onto longer, more explicit keyword arguments at the API level.

  • Keep inline comments sparse. Only explain non-obvious “why”, not “what” the code does. Prefer self-documenting code and clear names over narration; do not annotate routine logic.

Tests#

  • To the best of your ability, ensure tests are passing before pushing.

  • Follow assertion style: actual on left, expected on right.

  • Always mark AI-generated tests with the ai_generated pytest marker.

  • Use pytest.mark.parametrize wherever appropriate to reduce duplication in test cases.

  • Avoid importing private API (names with a leading underscore) in tests. Always import from what is publicly exposed through __init__.py files.

  • When monkeypatching internal imports in tests, target the importing module’s binding (e.g., foo.baz), not the original definition module (e.g., foo._bar.baz).

Module and API conventions#

  • Never expose private names (those with a leading underscore) in any module’s __all__.

  • Never include code other than imports, __all__, simple import errors, or magic __dir__ overrides in any __init__.py file.

  • Do not add compatibility aliases when renaming functions. Update all call sites to the canonical name instead.