Skip to content

Latest commit

 

History

History
327 lines (253 loc) · 12.7 KB

File metadata and controls

327 lines (253 loc) · 12.7 KB

Contributing to codex-lb

Thanks for thinking about contributing to codex-lb! This document covers everything you need to get from "clone" to "merged PR" without re-discovering the conventions yourself.

If anything below is unclear or out of date, please open a small docs: PR — that's the easiest first contribution.


Table of contents

  1. Code of conduct
  2. Ways to contribute
  3. Development setup
  4. Project layout
  5. Workflow: OpenSpec-first
  6. Coding conventions
  7. Commit & PR conventions
  8. Merge gates and collaborator rules
  9. Tests
  10. Release process
  11. Security issues

Code of conduct

Participation in this project is governed by the Contributor Covenant Code of Conduct. By participating, you agree to uphold it.

Ways to contribute

You don't need to write code to help:

  • Triage: reproduce open issues, ask for missing details, add labels.
  • Docs: improve the README, fix typos, document a deployment recipe.
  • Discussions: answer "how do I…?" questions in GitHub Discussions.
  • Bug reports: file a structured report using the Bug Report issue form.
  • Feature ideas: propose them in Discussions first if open-ended; use the Feature Request form when the idea is concrete.
  • Code: fix a bug, implement a feature, refactor a hot spot.

Look for issues labelled good first issue or help wanted if you're looking for a starter task.

Development setup

codex-lb is a Python project managed with uv.

# 1. Fork & clone
git clone https://github.com/<your-user>/codex-lb.git
cd codex-lb

# 2. Install Python + deps via uv
uv sync --all-extras --dev

# 3. Activate the venv (optional — `uv run` works without activating)
source .venv/bin/activate

# 4. Install pre-commit hooks
uv run pre-commit install

# 5. Run the proxy locally
uv run codex-lb --help

Frontend (dashboard) lives under frontend/. Use the project's standard package manager (see frontend/package.json).

Project layout

.
├── app/                  # Python application (proxy, accounts, dashboard backend)
├── frontend/             # Dashboard SPA
├── deploy/helm/codex-lb/ # Helm chart
├── tests/
│   ├── unit/             # Fast, isolated tests
│   └── integration/      # End-to-end / network-touching tests
├── openspec/             # SSOT — specs, change proposals, archived changes
│   ├── specs/<capability>/
│   └── changes/<change>/
└── .github/              # Issue forms, PR template, workflows

Workflow: OpenSpec-first

codex-lb uses OpenSpec as the source of truth for change-driven development. You don't have to author OpenSpec entries for trivial fixes, but for anything that changes observable behavior, requirements, contracts, or schema, an OpenSpec change is expected.

The typical flow:

  1. Find the relevant spec in openspec/specs/<capability>/spec.md.
  2. If your change alters behavior: create openspec/changes/<kebab-case-change>/ with a proposal + tasks.
  3. Implement the tasks; keep spec.md in sync.
  4. Validate: openspec validate --specs.
  5. Verify and archive when done.

If you're unsure whether your PR needs an OpenSpec change, open it as draft and ask — a maintainer will tell you.

Do not hand-edit CHANGELOG.md. Release notes are generated by release-please from your commit titles.

Coding conventions

  • Python: 3.13+. Code is formatted with ruff format, linted with ruff check, and type-checked with ty (Astral's type checker).
  • Imports / style: follow what you see in nearby files; the linter is authoritative — if ruff and your style guide disagree, ruff wins.
  • Type hints: required on new public functions and new modules. Existing code is being typed incrementally.
  • Logging: prefer the project's structured logger over print.
  • Secrets: never commit API keys, tokens, account credentials, or .env files. The CI redaction is best-effort, not a safety net.

Run the full lint/test gate locally before pushing:

uv run pre-commit run local-ci --hook-stage manual --all-files

The local-ci hook runs make ci, which is the local version of the GitHub Actions CI gate. You can also run a single CI job while iterating, for example:

make lint
make test-unit
make package

Commit & PR conventions

codex-lb uses Conventional Commits because release-please derives the next version + changelog from them.

Format:

<type>(<scope>)?: <subject>

[optional body]

[optional footer(s)]

Common types:

Type When to use it
fix Bug fix → patch version bump
feat New user-facing feature → minor version bump
refactor Internal change, no behavior change
perf Performance improvement
docs Documentation only
test Test-only change
build Build system / packaging
ci CI configuration
chore Tooling / maintenance with no user-visible effect

Common scopes used in this repo: proxy, accounts, auth, oauth, quota, chat, proxy-responses, dashboard-auth, ui, db, types, openspec, main (release).

Breaking changes: append ! after the type/scope and/or add a BREAKING CHANGE: footer.

Examples (from the actual repo history):

fix(proxy): handle model fetch timeouts
fix(accounts): recover quota status from usage refresh
feat(proxy): add OpenAI-compatible /v1/images API (gpt-image-2 via image_generation tool)
fix(dashboard-auth): reject passwords longer than bcrypt's 72-byte limit

PR titles must follow the same format — that's the title release-please reads.

Pull request flow

  1. Create a branch from main: git checkout -b <type>/<short-name>.
  2. Make atomic commits with Conventional Commit titles.
  3. Run the lint/test gate locally (see above).
  4. Open a PR using the template. Link the relevant issue.
  5. Codex Review (and a human maintainer) will review. Address feedback by pushing follow-up commits — no force-pushing during active review.
  6. Once approved and CI is green, a maintainer squash-merges with a clean Conventional Commits title.

Merge gates and collaborator rules

These rules apply to every PR, regardless of author (external contributor, project owner, or collaborator). They formalize the review bar that has produced the current proxy / continuity-recovery / OpenSpec quality so far.

Merge gates

Before a PR is squash-merged into main:

  1. CI must be all-green on the merge-target head. "UNSTABLE, looks fine" is not a green CI; rerun, fix, or wait. The Helm / migration / PostgreSQL test jobs are part of the gate, not optional. The CI Required check is the branch-protection check to require: it depends on every CI job and also runs for merge queue synthetic merge groups, so a stale PR head cannot bypass a broken merge result.
  2. @codex review must be clean — or its findings addressed — on the merge-target head. Every PR triggers @codex review at least once against the head that's about to be merged. Local codex review --base origin/main runs are encouraged but don't substitute for the cloud review (the cloud @codex review reliably catches things the local run misses). The 🤖 codex: ok label is maintained by the trusted Codex review labels workflow from current-head CI and current-head Codex review evidence. Treat the label as an audit aid, not as a substitute for branch protection or merge queue checks.
    • P1 findings: fix in the PR, or justify in-thread with a short write-up of why the finding doesn't apply. No silent skipping.
    • P2 findings: fix in the PR, or open a follow-up issue and link it in the PR thread before merging.
  3. mergeable must be CLEAN in the GitHub API — no merge conflicts, no requested-changes review still outstanding, no missing required status check.
  4. OpenSpec change folder for behavior changes (see Workflow: OpenSpec-first). Pure refactors, docs-only edits, dev-tool changes, and test stabilization PRs are exempt; everything else needs an openspec/changes/<slug>/ entry.
  5. Fixes #N / Closes #N in the PR body for anything that resolves an issue, so the issue close stays automatic and the merge stays traceable. Use Refs #N / Related to #N for partial cover.

Collaborator rules

Collaborators (write-access contributors) follow two additional rules on top of the merge gates above:

  1. No self-merge by default. A collaborator's own PR is merged by another maintainer (or, until the project has more collaborators, by the project owner). Review independence matters more than turnaround.
  2. Large PRs get split. Roughly:
    • If a PR is a stack tip pulling in unrelated commits from sibling branches, split it so each merged PR is a single scoped change.
    • If a single PR is over ~800 net lines and spans multiple concerns, split it into reviewable pieces. A single 1500-line change scoped to one capability is fine; a 400-line change that touches the proxy hot path and the dashboard and the OAuth flow is not.

Bus factor escape hatch

To keep the project unblocked if the owner is unavailable, the following self-merge escape hatch applies:

  • If a collaborator's PR has been waiting on a maintainer merge for more than 14 days with all merge gates met (CI green, @codex review clean or findings addressed, mergeable=CLEAN, no outstanding requested-changes review, no objection from any other active collaborator in the thread), the PR author may self-merge.
  • Self-merge under this clause must include a comment on the PR explicitly invoking the clause and linking to the date the merge gates first went green. Audit trail must stay clean.
  • The clause is a safety valve, not a default path. If you're tempted to invoke it on a PR you opened less than two weeks ago, the merge gates probably aren't actually all green yet.

What this is not

These rules are intentionally lightweight. They don't require:

  • A second human reviewer in addition to @codex review for every PR. Codex review + the PR author + a maintainer merge is the baseline.
  • Squash-merge commit message rewriting beyond the Conventional Commits title. The PR description ends up in the body; that's enough.
  • A formal escalation process for disagreements. If a P1 finding is disputed, work it out in-thread; if it can't be resolved, leave the PR open and ping the owner.

Tests

  • Unit tests live under tests/unit/ and should be fast and hermetic.
  • Integration tests live under tests/integration/ and may spin up the app, hit local sockets, or use fixtures that mimic upstream.
  • New behavior needs at least one unit test. New endpoints need an integration test.
  • Run a focused subset with:
uv run pytest tests/unit/test_proxy_api_responses_contract.py -q

Release process

Releases are automated via release-please:

  1. Commits land on main with Conventional Commits titles.
  2. release-please opens / updates a "chore(main): release X.Y.Z" PR containing the proposed version bump and generated changelog.
  3. When a maintainer merges that PR, a GitHub Release is published and the PyPI / Docker / Helm artifacts are built and uploaded.

Contributors never need to edit CHANGELOG.md, version strings, or tag manually.

Security issues

Please do not open public issues for security vulnerabilities. Report them privately via GitHub Security Advisories. See SECURITY.md for full details.


Happy hacking. If you get stuck, open a draft PR or start a Discussion — maintainers would rather see a half-finished idea than no idea at all.