Contributing
Tests, the live-marker rule, PR conventions, and the honesty rule for docs.
What this guide covers
The file covers the three things easiest to get wrong in this repo: how to run the tests, the rule that keeps live tests from spending your money, and the standard the project holds documentation to.
Setup
The toolchain is uv. uv.lock is committed, so everyone resolves to the same versions. --all-extras matters, because several tests skip themselves when an optional dependency is missing โ syncing only the dev group silently shrinks the suite instead of failing.
git clone https://github.com/CodeGraphContext/GraphARC cd GraphARC uv sync --all-extras --group dev
Running the tests
Standard pytest invocations for the whole suite, a single file, a pattern, or first-failure. Lint and autofix with ruff. Both are what CI runs (.github/workflows/ci.yml), across Python 3.12, 3.13 and 3.14.
uv run pytest # the whole suite uv run pytest tests/test_server.py # one file uv run pytest -k retrieval # one pattern uv run pytest -x -q # stop at the first failure
uv run ruff check . --fix
The live-marker rule
A test marked live calls a real model backend and spends real money. Never run pytest -m live casually, never in CI, and never because a failing test looked like it might pass against a real model. The mechanics, so you can tell when they are broken: addopts in pyproject.toml carries -m 'not live' and must stay there; addopts also carries --strict-markers, which is part of the same guarantee rather than a style preference, since without it a misspelled @pytest.mark.lvie is only a warning and the test calls the API on a plain pytest; every marker must be registered in the markers table before use, and tests/test_packaging.py fails if the tree uses one that is not; required_plugins names pytest-asyncio and pytest-timeout, because registering their markers locally would make a missing plugin silent with no timeout enforced; and CI has a live-marker-guard job comparing the default selection against the -m live selection, failing if anything is in both.
pytest # 'not live' โ every paid test is deselected pytest -m live # opt in, deliberately, with your own key
@pytest.mark.live
def test_something_against_a_real_backend():
...Writing a change (PR conventions
One concern per PR โ a fix and the refactor around it are two PRs. A bug fix comes with a test that fails without it: prove it by reverting the fix, watching the new test go red, and putting the fix back; a test that passes either way is documentation, not a regression guard. Never weaken an existing test to make a change pass โ if an assertion is genuinely wrong, say so in the PR and explain why in the same breath as changing it. Keep ruff check . clean; line length is 100. Note behaviour changes in the commit message when you change behaviour, the public API, or what ships.
The honesty rule for documentation
Enforced hardest, because this repo has broken it before and the ROADMAP still carries a ! legend entry for claims that shipped while being false. A docstring, a README line, or a comment must not claim a guarantee the code does not provide โ not "aspirationally", not "once the TODO lands", not because the happy path happens to hold. Concretely: if a function is confined, sandboxed, budgeted, atomic, ordered or durable only under conditions, name the conditions in the docstring (render_context documents the single case where it overshoots max_tokens; LocalExecutor is named for what it does not do). If something is defense in depth rather than a boundary, say which (SandboxedExecutor is an audit-hook sandbox, not a kernel boundary, and its docstring says so). If a name oversells the thing โ the way an exact-string match was once labelled "GraphRAG" โ rename it or write down precisely what it does (HashingEmbedder's docstring states it is lexical, not semantic). If an extra, config key or parameter exists but nothing implements it yet, say that where a reader will hit it โ the optional-dependency table in pyproject.toml states, per extra, whether anything under grapharc/ imports it today. When you fix something, fix the prose in the same commit: a stale docstring that used to be true is exactly as harmful as one that was never true.
Packaging changes
pyproject.toml is the authority for what ships. [tool.hatch.build.targets.sdist].include is an allowlist, so a new top-level file that should ship has to be added there; MANIFEST.in mirrors that list for readers and tools, hatchling never reads it, and tests/test_packaging.py fails if the two drift. [tool.hatch.build].ignore-vcs is on deliberately, because with hatchling's default a .gitignore entry doubles as a build exclusion โ verified to drop the whole grapharc/tools subpackage out of the wheel with no error and a successful build; junk you want kept out belongs in [tool.hatch.build].exclude. After a packaging change, build and install into a throwaway environment rather than trusting the build's exit code. CI does the same on every PR, for both the wheel and the sdist.
uv build uv venv /tmp/check && uv pip install --python /tmp/check/bin/python "$(echo dist/*.whl)[all]" cd /tmp && /tmp/check/bin/python -c "import grapharc, pkgutil, importlib [importlib.import_module(m.name) for m in pkgutil.walk_packages(grapharc.__path__, 'grapharc.')]" /tmp/check/bin/grapharc --version
Releasing
Tag-driven. .github/workflows/release.yml refuses a tag that disagrees with the version in pyproject.toml, builds, verifies the artifacts in clean environments, and publishes through PyPI Trusted Publishing. There is no API token in this repository or in its secrets. Steps: bump version in pyproject.toml and __version__ in grapharc/__init__.py (CI fails if the two disagree); tag the release, with the commit log as the record of what changed; tag vX.Y.Z and push it.
Source files read in full: /home/shashank/Desktop/GraphARC/README.md, /home/shashank/Desktop/GraphARC/ROADMAP.md, /home/shashank/Desktop/GraphARC/CONTRIBUTING.md. Referenced but not read: /home/shashank/Desktop/GraphARC/docs/diagrams/ (architecture.png, 03-agent-node.png, grapharc-architecture.drawio, architecture.py), /home/shashank/Desktop/GraphARC/docs/cookbook/, /home/shashank/Desktop/GraphARC/grapharc/examples/plan_incident.py, /home/shashank/Desktop/GraphARC/grapharc/policy/example.toml, /home/shashank/Desktop/GraphARC/grapharc/stdlib.py.