The role of this file is to describe common mistakes and confusion points that agents might encounter as they work in this project. If you ever encounter something in the project that surprises you, please alert the developer working with you and then document it in this file to help prevent future agents from having the same issue.
- Work on the
devbranch. Do not create feature branches. - Use Conventional Commits:
feat:,fix:,docs:,test:,ci:,chore:,refactor:,release:. - Split commits per logical change (one commit per feature, fix, or refactor).
- Match existing code style. Lint rules are in
pyproject.toml(ruff + pyright). - Python ≥ 3.11. Dependencies:
httpx,pydantic v2.
The workflow is mandatory — do not skip any step, even for small changes.
- Make changes.
- Run
make test(unit + contract tests) andmake check(ruff + pyright). Fix any failures before committing. - Commit with a conventional commit message.
- Repeat steps 1–3 for each logical change.
- When finished, add entries to
CHANGELOG.mdunder## Unreleasedgrouped by sub-header (### New features,### Bug fixes,### Internal, etc.) matching the existing style. - Commit the changelog update:
docs: update changelog.
Past failure: An agent completed a multi-file fix but skipped steps 3–6 entirely — no commits were made and the changelog was not updated. The workflow applies to every task, no matter how small.
-
make testruns unit + contract tests (not just unit). Thetesttarget includestests/contract— the contract tests validate the public API surface. Don't skip them. -
The
Assetmodel overrides bothsave()and_apply_server_data(). If you're modifying the baseApiObjectpersistence logic, you must also checksnipeit/resources/assets/model.py— it has non-trivial overrides that handle Snipe-IT's custom field PATCH quirks (nullcustom_fieldsin responses, column-name echo at top level). Breaking the base class contract will silently break asset custom field workflows. -
Pydantic v2 internals are used directly in
_apply_server_data. The base class writes to__pydantic_extra__and__dict__directly. On any pydantic version bump, run the full test suite — there are regression tests specifically for this (test_apply_server_data_*). -
Integration tests require Docker and a two-stage wait.
make test-integrationhandles this automatically. Don't try to runpytest tests/integrationdirectly — it needsSNIPEIT_TEST_URLandSNIPEIT_TEST_TOKENenv vars, which are set by the Makefile's wait loop after Docker is ready. -
The
docker/api_key.txtfile must be a regular file (not a directory). Docker will auto-create it as a directory if it doesn't exist beforedocker compose up. The Makefile handles this, but if you manually run docker compose, you'll hit a confusing bind-mount error. -
Every test file must have
pytestmark = pytest.mark.unitorpytest.mark.integration. The Makefile runs tests with-m unitor-m integration. An unmarked test will be silently skipped (not fail), which is worse — you'll think your new test passes when it never ran. -
The retry transport's sleep is patched to a no-op in the
snipeit_clientfixture. Without this, tests that trigger retries (429/5xx paths) would sleep for real backoff delays. If you need to verify timing/backoff behavior, construct your ownRetryTransportwith an explicitsleep=callable (seetest_retries.py).
- Run
make test-all. unit + contract + integration tests must pass. Fix any failures before proceeding. - Run
make mut— review mutation results (advisory, non-blocking). - Decide the version bump following pre-1.0 semver:
- MINOR — breaking changes or significant new features.
- PATCH — bug fixes and non-breaking additions.
- Update
versioninpyproject.toml. - Move
## Unreleasedentries inCHANGELOG.mdunder## X.Y.Z (YYYY-MM-DD). - Commit:
release: vX.Y.Z. - Tag:
git tag vX.Y.Z. - Push:
git push -u origin dev --tags. - Create PR to
main:gh pr create --base main --head dev.