Skip to main content

Contributing workflow

Use the same Poetry-based commands locally that CI runs on pull requests. The goal is to keep the repository readable after the collapsed-file cleanup and to catch formatting, lint, test, docs, and eval regressions before review.

Public installs vs. development installs

End users should install released OTerminus packages from PyPI, preferably with pipx install oterminus so the CLI is isolated from the system Python environment. If pipx is unavailable, the user-facing fallback is python -m pip install oterminus. Keep these public install instructions in README and the user guide aligned with actual package metadata and CLI behavior.

Contributors working from a source checkout should use Poetry instead of a public PyPI install.

Set up development dependencies

poetry install --with dev

Formatting and linting

Ruff is the Python formatter and linter for OTerminus. Do not add Black or another overlapping tool for normal Python formatting or lint enforcement.

Format Python code before opening a PR:

poetry run ruff format .

Verify formatting without changing files:

poetry run ruff format --check .

Run lint checks across the repository:

poetry run ruff check .

Keep non-Python source readable too:

  • Markdown should use normal headings, blank lines, lists, and valid fenced code blocks.
  • YAML and TOML should stay expanded and reviewable, not collapsed into one-line files.
  • Avoid mixing large formatting-only rewrites with feature or behavior changes; split them into a separate PR when practical.

Tests and evals

Run the unit test suite locally:

poetry run pytest

CI reports package coverage with:

poetry run pytest --cov=src/oterminus --cov-report=term-missing

Run deterministic eval fixtures when command support, router/planner behavior, validator/policy behavior, direct-command detection, ambiguity behavior, or structured rendering changes:

poetry run oterminus-evals
poetry run oterminus-evals --fixtures-dir evals/cases

Eval fixtures are JSON arrays organized by capability or behavior under evals/cases/, with a packaged mirror under src/oterminus/eval_fixtures/. Keep fixture IDs unique across all files and prefer readable capability or behavior prefixes such as network-, project-health-, direct-, release-, planner-, or ambiguity-. New command-pack work should include representative eval coverage for accepted behavior plus focused unsafe, unsupported, and ambiguous cases. Use release_smoke.json only for cross-cutting public-install or first-use flows such as direct command entry, deterministic shortcuts, ambiguity blocking, dry-run/explain preview behavior, or a minimal planner-fixture path. Keep capability-specific behavior in its capability file, and cover oterminus --version, oterminus version, and oterminus doctor with CLI tests because the eval harness does not execute console-script commands.

Use planner_proposal for natural-language planner-path cases so the eval remains deterministic. These local test and eval commands should not require an Ollama service, live network access, a real Git repository state, filesystem contents, a real installed wheel, or subprocess execution. CI uses the same deterministic fixture path, so no Ollama service/model/network call is required for the regression gate. See Evals for fixture organization and format details.

Do not add natural-language phrase variants as deterministic shortcut fixtures. Direct-command evals cover local command detection, deterministic-shortcut evals cover only the retained fixed utility shortcuts, and flexible natural-language behavior should be represented with mocked planner_proposal payloads.

For contributor-created candidate files, validate the sanitized JSON before moving it into evals/cases/:

poetry run oterminus-evals --validate-file path/to/candidate.json
poetry run oterminus-evals --validate-file path/to/candidate.json --run

The first command checks JSON shape, EvalCase schema, non-empty content, and duplicate IDs within the candidate file. The --run form additionally runs deterministic evaluation for that candidate file only. Candidate files can live outside evals/cases/; neither mode calls Ollama or executes shell commands.

Dogfooding findings

Use the Dogfooding playbook before turning real unsupported or surprising requests into issues, eval fixtures, docs examples, planner/schema follow-ups, command-spec changes, or bug reports. Dogfooding notes should capture the request pattern and expected behavior, not private logs, file contents, audit records, history files, hostnames, tokens, or project details. Do not add deterministic shortcuts for broad natural-language phrase coverage. Flexible language should be handled through planner prompts, schema-constrained outputs, diagnostics, and mocked planner eval fixtures; the shortcut layer is limited to retained fixed utility requests. Do not add broad regex coverage, argument extraction, or shortcut recipes to work around model schema failures. A new deterministic shortcut needs strong justification: tiny, stable, low-ambiguity, read-only, and no path, count, search-term, manual-page topic, process-name, Git, project-health, or other argument interpretation.

CI coverage

The main CI workflow keeps Ubuntu as the full regression gate. It runs the complete pytest suite, Ruff lint and format checks, deterministic evals, generated command-reference checks, docs link checks, a strict MkDocs build, and scripts/validate_package_install.py.

CI also runs a separate macOS Python 3.13 platform smoke lane. That job is intentionally narrower: it exercises platform-aware command registry behavior, direct-command detection, validator behavior, shell completion/config tests, Ruff linting, and installed CLI smoke validation on a real macOS runner. The macOS smoke lane does not require Ollama, local models, or natural-language planning, and it does not publish packages or run release workflows.

Documentation rules

Update /docs in the same PR when you change behavior, architecture, command support, configuration, policy, validation, evals, or user-facing behavior. Documentation is part of the change, not follow-up work.

Keep documentation organized this way:

  • Keep README.md as the landing page and quick orientation.
  • Put detailed product, architecture, reference, eval, and contributor material under website/docs/.
  • Update website/sidebars.ts when adding, moving, or deleting docs pages.
  • Keep public install docs aligned with PyPI/pipx behavior, development docs aligned with Poetry, and release/package-validation docs aligned with the package validation script.
  • Update the root CHANGELOG.md for user-facing changes.
  • Release PRs must update pyproject.toml and CHANGELOG.md together.
  • Do not include secrets, real tokens, real audit logs, persisted history files, or personal local paths in docs or fixtures.
  • When audit, history, failure-explanation, output, install behavior, packaging metadata, or env privacy behavior changes, update the relevant docs in the same PR.

Validate docs before review:

poetry run python scripts/check_docs_links.py
poetry run python scripts/generate_command_reference.py --check
cd website
npm ci
npm run build
npm run typecheck

The CI workflow validates the Docusaurus site and Docusaurus docs links. The docs workflow deploys the Docusaurus website/build artifact only after a push to main or a manual workflow dispatch. Confirm the repository Pages setting is Settings → Pages → Build and deployment → Source → GitHub Actions.

Pre-PR quality commands

Run the checks that match your change before opening a PR:

poetry run ruff format .
poetry run ruff format --check .
poetry run ruff check .
poetry run pytest
poetry run pytest --cov=src/oterminus --cov-report=term-missing
poetry run python scripts/check_docs_links.py
poetry run python scripts/generate_command_reference.py --check
cd website
npm ci
npm run build
npm run typecheck
poetry run oterminus-evals
poetry run python scripts/validate_package_install.py

Local package build + wheel install validation

Validate local artifacts before publishing or changing packaging behavior. This script is for release/development validation, not the primary user install path:

poetry run python scripts/validate_package_install.py

The script will:

  1. remove stale local dist/ artifacts, then build sdist and wheel with poetry build
  2. create a temporary virtual environment
  3. install the local wheel
  4. verify import oterminus
  5. run CLI smoke checks: oterminus --help, oterminus --version, oterminus version, oterminus doctor, config path/init/get/set/validate/show against a temporary config path, oterminus completion zsh, oterminus completion bash, oterminus completion fish, and oterminus-evals

Notes:

  • oterminus doctor may exit non-zero or report Ollama readiness issues in clean or CI environments; this does not block packaging validation because the script still confirms CLI installability.
  • oterminus-evals uses packaged fixture data from src/oterminus/eval_fixtures/ so it works after wheel install.
  • CI and the production PyPI workflow run the same package validation command before the production publish boundary.
  • Publishing to TestPyPI and production PyPI is documented in website/docs/release.md and uses GitHub OIDC Trusted Publishing with protected deployment environments. The TestPyPI workflow still verifies the exact published version by installing it back from TestPyPI after publish.
  • End-user installs should use pipx install oterminus after PyPI release; contributors should not add install-time or runtime behavior that automatically edits user shell startup files for completion.

Pull request template and checklist

Every pull request should use .github/pull_request_template.md and keep every checklist item in place. If an item is not applicable, mark it as N/A in the PR description instead of deleting it.

In addition to formatting, lint, test, docs, and eval checks, the PR template requires explicit confirmation that core architecture invariants still hold for behavior-affecting changes (for example capability-first command support, structured-first behavior, ambiguity gates, validator and policy separation, and local-only audit logging expectations).

When command registry/spec files change, refresh generated command reference docs in the same PR. When behavior, architecture, command support, config, evals, policy, validation, or user-facing behavior changes, docs updates are mandatory in the same PR (not follow-up work).