Thanks for improving AgentOS. Keep pull requests small, focused, and covered by tests that outside contributors can run without private access.
Before opening a new issue, search the existing open and closed issues. If one already covers your report, comment there instead of opening a duplicate.
Open an issue before opening a pull request for a bug fix or a new feature. Maintainers confirm the change is needed and can assign the issue to you.
Give every issue the labels that match it: one type: label, the relevant
area: label, and a priority: label when you can judge it. List the labels
this repository defines with gh label list (or the Labels page on GitHub)
and pick from those rather than inventing new ones.
To claim an issue, comment on it. A maintainer will assign it to you and add
status: claimed. Check the assignee before you start work — do not fix an
issue that is already assigned to someone else, because it duplicates their
effort.
If an assigned contributor posts no progress within three days, maintainers may unassign the issue so someone else can take it over.
Open pull requests against main. Every pull request must link its issue in
the description with a GitHub keyword: Fixes #123 or Closes #123 when the
pull request fully resolves the issue, or Refs #123 when it does not.
A pull request that closes an issue should solve that issue completely. If it
covers only part of the issue, use Refs #123 and say in the description what
still remains.
You do not have to wait to be assigned. Opening a pull request that links the issue is enough to claim the work — just check the assignee first so you do not duplicate someone else's effort.
When a squash or rebase collapses commits from several people, keep the final
commit attributable with Co-authored-by: trailers for every contributor
whose work is included.
Install development dependencies:
uv sync --extra dev --extra recommendedRun the quality gate before opening a pull request:
python scripts/build_control_ui.py build
npm --prefix frontend run check
uv run ruff check src tests
uv run mypy src/agentos --show-error-codes
uv run pytest -q
uv build --wheelNode.js 22 or newer is required for source installs and release-artifact builds. Python-only inner-loop tests do not need Node, but the final wheel gate does because the wheel must contain a freshly verified Control UI bundle.
Default tests must be offline, deterministic, credential-free, and safe for forks. Do not add network, provider, browser, or channel requirements to the default pull request path.
uv.lock is committed and CI syncs --frozen, so contributor and CI installs
are reproducible. Downstream pip install use-agent-os is not: it resolves
fresh against PyPI and takes whatever each package published most recently,
including a breaking major released after our last release.
So every dependency in [project.dependencies] and in every extra except dev
carries an upper bound at the first release its upstream is free to break
in, measured from the version uv.lock pins:
| upstream version | cap at | example |
|---|---|---|
>=1.0 |
next major | locked rich 14.3.3 → rich>=13.0,<15.0 |
0.x |
next minor | locked typer 0.24.1 → typer>=0.12,<0.25 |
The 0.x row matters more than it looks. Under semver the minor is the breaking
unit below 1.0, so typer<1.0 is not a cap — it waves through every release
upstream cares to make. There is no 0.x exemption: a package numbered below 1.0
gets a minor cap even where we only touch it indirectly.
Exemptions are for >=1.0 packages only, listed in INTENTIONALLY_UNCAPPED in
tests/test_packaging/test_pyproject_invariants.py:
- CalVer projects (
structlog,html2text) — the version tracks the year, so a cap expires by the calendar rather than by an actual break. - Long-stable narrow surfaces (
pyyaml,jinja2,cachetools, and similar) — we callsafe_load,Template.render, aTTLCache. A cap buys nothing and costs co-installability.
Bounds are deliberately not applied uniformly to everything: capping all of
them makes AgentOS painful to install alongside other packages. dev is
contributor tooling pinned by the lockfile, never a consumer surface, and stays
uncapped.
When you add, remove, or re-bound a dependency:
uv lock # commit the result with your change
uv run pytest tests/test_packaging -q # enforces the policy aboveThe test recomputes both boundaries from uv.lock rather than hardcoding them,
so bumping a locked version tells you to move its cap in the same change.
Raising an existing cap is a normal change — bump the locked version, widen the cap, and say in the pull request what you tested against.
Add or update public regression tests for behavior changes and bug fixes.
Prefer focused unit or integration tests unless the behavior crosses the
gateway, browser UI, provider, or channel boundary. Live provider, browser,
and channel smoke tests are maintainer-only opt-in workflows
(Live Release E2E and LLM E2E).
Private test suites, real provider transcripts, real channel identifiers,
local paths, credentials, and AI session artifacts must not be committed.
Local maintainer-only files may live under tests/_private/; it is excluded
from the public tree and default pytest collection.
Declare any third-party origin in the pull request (none if there is none):
inspired-by, adapted/ported, vendored, direct dependency, or
modified upstream. For adapted, vendored, or modified upstream material,
include the upstream URL, license, copyright notice, and any required changes
to THIRD_PARTY_NOTICES.md in the same pull request.
Permissive licenses (Apache-2.0, MIT, BSD, ISC) are usually acceptable. GPL, AGPL, LGPL, SSPL, source-available, or unclear licenses require explicit maintainer approval before merge.
Do not include vulnerability details, exploit steps, credentials, or provider
tokens in public issues. Use the process in SECURITY.md for suspected
vulnerabilities.
Keep discussion technical, specific, and respectful. Expected conduct is
documented in CODE_OF_CONDUCT.md.