Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 162 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# =============================================================================
# Release & publish workflow (conclave v1.0 — distribution + release-eng)
#
# !!! INERT UNTIL CONFIGURED — SAFE TO MERGE !!!
#
# This workflow does NOTHING until BOTH of the following are true:
# (a) a GitHub *Release* is published (a pushed git tag ALONE does not trigger
# it — the trigger is `release: published`, the explicit human gesture), AND
# (b) the `conclave-cli` PyPI project + its OIDC Trusted Publisher are configured
# by the owner (see RELEASING.md "One-time PyPI setup").
#
# Until (a) AND (b) hold, merging this file changes nothing at runtime: no tag is
# cut here, no version is bumped here, nothing is published here. Cutting v1.0.0 is
# then a single tag + GitHub Release away (the full runbook is in RELEASING.md).
#
# What it does WHEN a Release is published:
# build — builds sdist + wheel with `python -m build`, uploads them as
# workflow artifacts so publish + sign consume the exact same bytes.
# pypi-publish — publishes those artifacts to PyPI via OIDC Trusted Publishing
# (NO API token, NO stored secret), with PEP 740 attestations.
# Fails CLOSED if the Trusted Publisher is not yet configured —
# it never falls back to anything insecure.
# sign — signs the sdist + wheel with Sigstore keyless (ambient OIDC ->
# Fulcio) and attaches the `.sigstore` bundles to the GitHub
# Release assets.
#
# Distribution-name note: the PyPI distribution name is `conclave-cli` (the name
# `conclave` is taken by an unrelated project). The command + import package stay
# `conclave`. The Trusted Publisher must therefore name PROJECT `conclave-cli`.
#
# Pin discipline: every `uses:` is pinned to a full 40-char commit SHA with a
# `# vX.Y.Z` comment. conclave's other workflows (test.yml, gitleaks.yml) pin to
# mutable major tags; this publish workflow holds elevated OIDC permissions, so it
# is hardened one level further with full commit-SHA pins.
# =============================================================================
name: Release

on:
# Primary, safe trigger: only fires once a GitHub Release is *published*.
# A pushed tag by itself does NOT publish a Release and therefore does NOT
# trigger this workflow.
release:
types: [published]

# Least-privilege at the top level; each job widens scope locally only as needed.
permissions:
contents: read

jobs:
# --------------------------------------------------------------------------
# Job 1: build sdist + wheel and publish them as workflow artifacts so the
# publish and sign jobs consume the exact same bytes (no rebuild drift).
# --------------------------------------------------------------------------
build:
name: Build sdist + wheel
runs-on: ubuntu-latest
permissions:
contents: read

steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Set up Python 3.12
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.12"
cache: pip

- name: Install build frontend
run: python -m pip install --upgrade "build==1.2.2.post1"

- name: Build sdist + wheel
run: python -m build --sdist --wheel --outdir dist/

- name: Show built artifacts
run: ls -l dist/

- name: Upload dist artifacts
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: dist
path: dist/
if-no-files-found: error

# --------------------------------------------------------------------------
# Job 2: publish to PyPI via OIDC Trusted Publishing.
#
# No API token, no stored secret: pypa/gh-action-pypi-publish mints a short-lived
# OIDC token from this job's `id-token: write` and exchanges it for a PyPI upload
# token. PyPI must have a Trusted Publisher (pending or active) for `conclave-cli`
# pointing at owner=ernestprovo23, repo=conclave, workflow=release.yml — see
# RELEASING.md. Until that exists, this job fails CLOSED (publish denied); it
# never falls back to anything insecure.
#
# PEP 740 attestations are generated + attached by default (the action's
# `attestations: true` default) since OIDC Trusted Publishing is in use.
# --------------------------------------------------------------------------
pypi-publish:
name: Publish to PyPI (OIDC Trusted Publishing)
needs: build
runs-on: ubuntu-latest
# id-token: write is scoped to THIS job only (never granted at workflow top level).
permissions:
id-token: write # REQUIRED for OIDC Trusted Publishing — mints the token
contents: read

steps:
- name: Download dist artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: dist
path: dist/

# Production PyPI via Trusted Publishing (OIDC). No `password:` — the action
# uses ambient OIDC. PEP 740 attestations are on by default.
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
with:
packages-dir: dist/
# No `password:` — OIDC Trusted Publishing is used (no stored secret).
# `attestations` defaults to true under Trusted Publishing -> PEP 740.

# --------------------------------------------------------------------------
# Job 3: sign the release artifacts with Sigstore keyless.
#
# The job's ambient OIDC mints a short-lived Fulcio cert (issuer
# https://token.actions.githubusercontent.com), signs each dist file, and
# attaches the resulting `.sigstore` bundle(s) to the GitHub Release assets
# (release-signing-artifacts, the action's default on a release event).
#
# We self-verify (`verify: true`) against THIS workflow's own identity so a
# broken run can never publish a bad bundle.
# --------------------------------------------------------------------------
sign:
name: Sign release artifacts (Sigstore keyless)
needs: build
runs-on: ubuntu-latest
permissions:
id-token: write # REQUIRED for ambient OIDC -> Fulcio
contents: write # REQUIRED to attach .sigstore bundles to the Release assets

steps:
- name: Download dist artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: dist
path: dist/

- name: Sign sdist + wheel and attach bundles to the Release
uses: sigstore/gh-action-sigstore-python@5b79a39c381910c090341a2c9b0bf022c8b387e1 # v3.4.0
with:
inputs: ./dist/*.tar.gz ./dist/*.whl
# Self-check: verify what we just signed against THIS workflow's own
# identity so a broken run never publishes a bad bundle.
verify: true
verify-cert-identity: "https://github.com/ernestprovo23/conclave/.github/workflows/release.yml@${{ github.ref }}"
verify-oidc-issuer: "https://token.actions.githubusercontent.com"
# release-signing-artifacts defaults to true: on a release event the
# .sigstore bundles are attached to the Release assets automatically.
release-signing-artifacts: true
upload-signing-artifacts: true
36 changes: 36 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,39 @@ jobs:

- name: ruff format --check
run: ruff format --check .

audit:
name: "pip-audit"
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Cache pip
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-audit-${{ hashFiles('pyproject.toml') }}

# Install the project so its resolved runtime + dev deps are present in the
# environment, then audit that environment. conclave is security-positioned,
# so this gate is FAIL-CLOSED: a known vulnerability in any resolved
# dependency fails CI. The dep surface is tiny (httpx + a few well-maintained
# libs), so false-positive churn is low. If a transitive CVE with no fix
# blocks an unrelated PR, suppress it narrowly with
# `--ignore-vuln <GHSA/PYSEC id>` and a tracking note (see RELEASING.md).
- name: Install package with dev extras
run: pip install -e ".[dev]"

- name: Install pip-audit
run: pip install pip-audit

# `--skip-editable` drops the local editable `conclave` install (it is not on
# PyPI and cannot be audited); every real dependency is still audited.
- name: Audit resolved dependencies for known vulnerabilities
run: pip-audit --skip-editable
5 changes: 4 additions & 1 deletion DOCUMENTATION_INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ Run: `pytest` (config in `pyproject.toml`, `asyncio_mode = "auto"`).

| File | Path | Purpose |
|------|------|---------|
| Packaging | [`pyproject.toml`](pyproject.toml) | hatchling build, deps (httpx, pydantic, rich, typer, pyyaml — no LLM SDK), dev extras, console script, pytest config. License: MIT. |
| Packaging | [`pyproject.toml`](pyproject.toml) | hatchling build, deps (httpx, pydantic, rich, typer, pyyaml — no LLM SDK), dev extras, console script, pytest config. License: MIT. **PyPI distribution name `conclave-cli`** (the name `conclave` is an unrelated project); command + import stay `conclave`. |
| Release runbook | [`RELEASING.md`](RELEASING.md) | Operator runbook: one-time PyPI OIDC Trusted-Publisher setup for `conclave-cli`, cut-a-release checklist (bump→tag→publish Release), post-release verification (Sigstore bundle, PEP 740 attestations), rollback/yank. |
| Dev lockfile | [`requirements-dev.lock`](requirements-dev.lock) | Hash-pinned dev + runtime tree for reproducible installs/CI. Regenerate via `uv pip compile --universal --generate-hashes --python-version 3.11 --extra dev pyproject.toml -o requirements-dev.lock`. |
| License | [`LICENSE`](LICENSE) | MIT License. Copyright (c) 2026 Ernest Provo. Matches the `pyproject.toml` license field. |
| Security policy | [`SECURITY.md`](SECURITY.md) | BYO-keys vulnerability reporting policy: how to report, scope, and the key-handling guarantees consumers can rely on. |
| Contributing guide | [`CONTRIBUTING.md`](CONTRIBUTING.md) | Dev setup, the BYO-keys contract for contributors, and the PR checklist (tests, ruff lint/format, coverage). |
Expand All @@ -91,6 +93,7 @@ Run: `pytest` (config in `pyproject.toml`, `asyncio_mode = "auto"`).

| Date | Change |
|------|--------|
| 2026-06-14 | v1.0 distribution + release-engineering (PR-A): PyPI distribution name → `conclave-cli` (command + import stay `conclave`); OIDC Trusted-Publishing release workflow (`.github/workflows/release.yml`, SHA-pinned, PEP 740 attestations, Sigstore keyless signing, inert until a Release fires + publisher configured); `pip-audit` fail-closed CI job in `test.yml`; hash-pinned `requirements-dev.lock`; `RELEASING.md` operator runbook. No version bump (stays 0.3.0 until the 1.0.0 release commit). |
| 2026-06-09 | Roadmap features shipped: adversarial proposer resilience (#9), optional result cache (#6), debate convergence early-stop (#4), 4 first-class providers groq/deepseek/mistral/together (#5), streaming for synthesize/raw (#7); tests 121→191. #8 local-server-mode spike evaluated (no-go on HTTP). Doc sync: System Context diagram now shows all 9 providers; PDD §12 resolved questions archived to `docs/archive/pdd-resolved-questions-2026-06-09.md` (PDD back under 500 lines); `config.example.yml` stale "LiteLLM" comment fixed. |
| 2026-06-08 | v0.3.0 version bump; CI foundation (Actions matrix, ruff, coverage floor, gitleaks, branch protection); redact() custom-endpoint key-leak fix (#14); status_error consolidation + conditional temperature (#16/#22); provider-metadata single-source + import-time drift guard + config memoization (#19/#15); CLI exit-code contract + httpx client lifecycle (#17/#20); transport/cli/logging test backfill (#18); public release + community files. |
| 2026-06-08 | PDD §11 repositioned vs. new direct peers (`llm-council-core`, `the-llm-council`); §12 Q1/Q3/Q4/Q5 resolved. Index Tests table updated for the PR #2 split (`test_adapters.py`, `test_providers.py`). |
Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@ See the canonical spec and design docs:

## Install

```bash
pip install conclave-cli
```

> **Name split (read this once).** The PyPI **distribution** name is
> `conclave-cli` — the name `conclave` on PyPI is an *unrelated* project (a
> blockchain client, not this one). Everything else stays `conclave`: the CLI
> command you type is `conclave`, the package you import is `conclave`, and the
> repo is `conclave`. So:
> `pip install conclave-cli` → run `conclave ...` / `from conclave import Council`.

From a source checkout (for development), install it editable instead:

```bash
# from the repo root
pip install -e .
Expand Down
Loading
Loading