docs: establish contribution, changelog, and release procedures#17
Merged
Conversation
`__version__` hardcoded "3.0.0.dev0" while core/version.py resolves the served version from importlib.metadata. The literal was unused and would have gone stale on the first release bump, leaving two disagreeing answers to "which version is this". Read the distribution metadata instead, so `version` in pyproject.toml is the single source of truth. Kept self-contained rather than importing core.version, which would pull settings/pydantic into `import idc_api`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Neither existed, and the conventions were only implicit: Conventional Commits were already de facto (last ~22 commits) but written down nowhere. - CONTRIBUTING.md: branching, commits, the hand-curated changelog rule, versioning (SemVer with MAJOR pinned to the URL prefix, /v3 <-> 3.y.z), and a release runbook. - CHANGELOG.md: Keep a Changelog, seeded with the [3.0.0b1] surface. - README.md: pointers only, per the doc conventions in CLAUDE.md. The runbook records two traps in the existing pipeline: a `v*` tag deploys prod (the glob matches pre-release tags), and it must point at a commit already promoted to test, since prod redeploys test's digest without rebuilding. The version bump therefore needs its own commit through test, or /v3/version reports the previous release. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Prod redeploys test's existing digest and never rebuilds, so the version the server reports is whatever test baked in from pyproject.toml. Tagging v3.0.0 on the commit that shipped as 3.0.0b1 therefore deployed cleanly and quietly served the wrong version at /v3/version. resolve-prod now checks out the tagged commit and asserts that the tag equals "v" + the packaged version, failing before the reviewer gate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CONTRIBUTING duplicated content that dev/ already owned, and in one case disagreed with it. - Move the release runbook (and the v3 beta rationale) into deployment.md as "Cutting a release", absorbing the constraint paragraph that already sat under the trigger table. It depends on the tier machinery, the promote dispatch, and the approval gate -- all documented there. Cutting a release is a maintainer task, not a contributor one. - CONTRIBUTING keeps the process a contributor needs -- branching, commits, the changelog rule, the versioning policy -- and points at the developer guide for setup/test/CI/invariants rather than restating them. - developer_guide.md's CI section listed only ruff + pytest; CI also runs ruff format, bandit, and pip-audit. Two docs disagreed on what CI runs. - Register CONTRIBUTING.md and CHANGELOG.md as lanes in CLAUDE.md's documentation conventions, which enumerated every other doc but not these. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6f62239 to
d95af26
Compare
There was a problem hiding this comment.
Pull request overview
This PR formalizes the project’s contribution, changelog, and release processes by adding first-class documentation and tightening the production promotion workflow so release tags cannot disagree with the packaged version.
Changes:
- Add
CONTRIBUTING.mdandCHANGELOG.md, and wire them into existing documentation pointers. - Extend the deployment runbook with a concrete “Cutting a release” procedure (including the v3 beta plan).
- Make version reporting derive from distribution metadata (single source of truth) and add a
promote.ymlguard to enforcetag == "v" + pyproject.toml versionfor prod releases.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
src/idc_api/__init__.py |
Derives __version__ from installed distribution metadata (with a clear source-tree fallback). |
README.md |
Adds pointers to the new contributing and changelog docs. |
dev/developer_guide.md |
Updates CI documentation to reflect all checks (ruff format, bandit, pip-audit, pytest). |
dev/deployment.md |
Adds a detailed “Cutting a release” runbook and documents key release constraints. |
CONTRIBUTING.md |
New contributor process doc: branching, commit conventions, changelog rules, versioning, release safety notes. |
CLAUDE.md |
Registers CONTRIBUTING.md and CHANGELOG.md in the documentation “lanes” conventions. |
CHANGELOG.md |
New Keep a Changelog–style changelog seeded with v3 beta (3.0.0b1) content. |
.github/workflows/promote.yml |
Adds a pre-gate guard on tag pushes to ensure tag/version alignment with pyproject.toml. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Establishes the contribution and release process, which until now lived only in people's heads:
there was no
CONTRIBUTING.md, noCHANGELOG.md, and no tags.Rebased onto
mainafter #16 merged; the docs describe the merged/mcptrailing-slash fix andthe
ruff formatCI step, both now onmain.What's here
CONTRIBUTING.md— branching, Conventional Commits (documented, not CI-enforced — the last~22 commits already conform), the changelog rule, and the versioning policy.
CHANGELOG.md— hand-curated Keep a Changelog, seeded with a[3.0.0b1]section describing the actual v3 surface. Deliberately not generated from commitsubjects:
ci: rework multi-tier deployis not what a caller of the API needs to read.dev/deployment.md→ "Cutting a release" — the runbook, placed with the tier machinery,promote dispatch, and approval gate it depends on. Includes the v3 beta plan.
promote.yml— a guard that fails a prod tag whose version disagrees withpyproject.toml.Versioning: MAJOR pinned to the URL prefix
SemVer with one house rule:
/v3↔3.y.z, always. Additive change → MINOR; bugfix → PATCH;breaking the contract → a new
/v4prefix and4.0.0, never a silent break under/v3. Thiskeeps
api_versionpredictive of the URL and matches the clean break v3 already made from v1/v2.Two traps in the existing release pipeline
Both are now documented, and the second is enforced:
v*tag must point at a commit already promoted to test — prod redeploys test's digestand never rebuilds. (This was already noted in
deployment.md; the runbook absorbs it.)v3.0.0on the commit thatshipped as
3.0.0b1would deploy cleanly and quietly serve3.0.0b1at/v3/version.resolve-prodnow assertstag == "v" + pyproject versionand fails before the reviewergate. Consequence: you can no longer tag a release without bumping
pyproject.tomlfirst.This is also why the version is not derived from the git tag (
hatch-vcset al): the tagtriggers a promotion, not a build, so a tag-derived version could never reach the prod image.
Note that
promote.ymldoesn't run on pull requests, so this guard's first live exercise will bethe
v3.0.0b1tag. Its logic is verified below, but that run is worth watching.Bug found along the way
src/idc_api/__init__.pyhardcoded__version__ = "3.0.0.dev0", whilecore/version.pyresolvesthe served version from
importlib.metadata. The literal was unused and would have gone stale onthe first release bump — a release procedure saying "bump the version" was going to be quietly
wrong. It now derives from the same distribution metadata, so
pyproject.tomlis the single source.Docs reconciliation
dev/developer_guide.mdtold contributors CI runsruff checkandpytest. It also runsruff format,bandit, andpip-audit, so two docs disagreed on what CI enforces — and the onecontributors are pointed at understated it. Fixed.
CONTRIBUTING.mdnow points at the developerguide for setup/test/CI/invariants instead of restating them, and
CLAUDE.md's doc-conventionssection (which enumerated every lane except these two new files) registers both.
Verification
Run against the rebased branch:
pytest: 70 passed.ruff check,ruff format --check,bandit: clean.__version__ == package_version(); build stamp still appends (3.0.0.dev0+abc1234);import idc_apistill pulls in neitherduckdbnorfastapi(no import cycle).actionlintclean onpromote.yml; ran the guard's shell logic against the realpyproject.toml— rejectsv3.0.0andv3.0.0b1against today's3.0.0.dev0tree, accepts only an exact match.Follow-up, not in this PR
Cutting the beta means bumping
pyproject.tomlto3.0.0b1in its own commit, promoting it totest, then tagging
v3.0.0b1.Drafted with Claude Code.
🤖 Generated with Claude Code