chore(release): integrate v1.0 PRs + bump 1.0.0 + CHANGELOG#45
Merged
Conversation
…ep audit Distribution + release-engineering for the v1.0 cut (readiness review must-dos 1, 2 scaffolding, and 6): - pyproject: [project].name -> conclave-cli (PyPI `conclave` is an unrelated project). Command (`conclave`), import package (`conclave`), and repo are unchanged. Adds [project.urls]. Version left at 0.3.0 (the 1.0.0 bump is the release commit per RELEASING.md). - README: install is now `pip install conclave-cli`, with a note on the install-name vs command/import split; editable source install kept for dev. - .github/workflows/release.yml: OIDC Trusted-Publishing publish workflow, triggered on `release: published`. build (python -m build) -> pypi-publish (pypa/gh-action-pypi-publish, no token, PEP 740 attestations) -> sign (Sigstore keyless, attaches .sigstore bundles to the Release). Every `uses:` pinned to a full commit SHA with a `# vX.Y.Z` comment. Inert until a Release is published AND the conclave-cli Trusted Publisher is configured. - test.yml: new fail-closed `pip-audit` job auditing the resolved deps. - requirements-dev.lock: hash-pinned dev + runtime tree (uv pip compile --generate-hashes) for reproducible installs. - RELEASING.md: operator runbook (one-time Trusted-Publisher setup, cut-a-release checklist, post-release verification, rollback/yank). - DOCUMENTATION_INDEX.md: index the new release-eng artifacts + version history.
#5) The synthesizer/judge path is the heart of conclave's "council" value prop but was undocumented and lightly tested, risking silent degradation. This makes it sound and observable for 1.0. Investigation (current behavior, unchanged): - synthesizer = constructor arg, else config `synthesizer:`, else built-in default "claude" (registry.DEFAULT_SYNTHESIZER). Same model judges in adversarial and consolidates in debate. - degraded paths were ALREADY observable, not silent: no-usable-answers, unkeyed synthesizer, and synthesizer-call-failure each set CouncilResult.synthesis_error (adversarial: AdversarialResult.verdict_error, mirrored). synthesis stays None; member answers preserved. No silent quiet-concat path existed to fix -- confirmed + pinned with tests. Changes: - version the synthesis prompt set: new conclave.prompts.SYNTHESIS_PROMPT_VERSION, re-exported from council, stamped onto every CouncilResult as `prompt_version` (lazy default_factory avoids the prompts<->models import cycle). Prompt text is byte-stable; the constant + text are pinned so a prompt change without a version bump fails CI. - document selection/default/configurability/fallback in the council module docstring + _synthesize docstring, and a README "Synthesizer behavior" section. - DOCUMENTATION_INDEX: new test file row, CouncilResult field note, changelog row. - tests/test_synthesizer.py (21 tests): default + arg/config/CLI override selection; observable degradation for synthesize, debate, and adversarial judge (unkeyed + call-failure); prompt-version stability across every mode. No non-synthesis behavior changed; happy-path synthesis output is byte-for-byte unchanged. Mocks at the existing httpx-transport boundary (offline).
…andlers pytest 9.x's logging plugin attaches LogCaptureHandler instances to every logger during a test, inflating raw len(logger.handlers). The two handler-count assertions in test_logging.py asserted an exact total of 1 and now see 3 under pytest 9.1.0, failing in CI (and on a fresh local resolve) regardless of source changes. Filter capture handlers out so the tests assert on the single handler get_logger actually installs -- preserving their intent while being robust to the runner. Production logging code is unchanged (outside pytest it adds exactly one StreamHandler).
… capture CI on this branch resolved pytest 9.1.0 (deps are pinned >=8.0.0; main's last green run predates the 9.1.0 release). pytest 9.x attaches its LogCaptureHandler (a StreamHandler subclass) directly to the non-propagating `conclave` logger during a run, so `len(logger.handlers) == 1` now sees 3 handlers and test_logging.py's one-shot-configuration assertions fail across 3.11/3.12/3.13. Count only conclave's own handler via `type(h) is logging.StreamHandler` (pytest's is a subclass) instead of all handlers. This preserves the test's intent exactly -- the factory installs one StreamHandler and never duplicates it -- while ignoring pytest-injected capture handlers, and is stable across pytest versions. No production code changed.
Audit and harden the BYO-keys leak surface ahead of v1.0, with regression tests per vector (tests/test_keyleak_audit.py): - cache write path: assert a planted key-shaped secret echoed in a provider error never reaches a cache file/filename/key (cache stores the already- redacted CouncilResult); add an explicit invariant comment in cache.store. - streaming path: assert a mid-stream provider error echoing a key is absent from every streamed StreamEvent AND the final ModelAnswer; document the transport->providers redaction boundary in stream_sse. - __repr__/__str__: assert no adapter/config/result object renders key material (no object stores a key; transient request headers are not retained). - provider 400/422 echo: assert buffered error capture runs through redact() for prefixed and unprefixed custom-endpoint keys. - httpx/httpcore DEBUG logging (out-of-band of redact): document loudly and add an opt-in guard_transport_logging() helper that drops transport DEBUG records (the only level that emits auth headers). - audit-found gap (not in the original attack map): the partial-failure catch-alls in Council.fan_out and streaming._drive_member built error strings from raw exception text without redact(); wrap both in redact(). Add a Threat model section to SECURITY.md (trust boundary, what IS protected, accepted limitations, vector map) without touching the disclosure policy. Add .gitleaks.toml allowlisting the obviously-fake test fixtures (test tree only). Tests 191 -> 209; coverage 89%+. Disclosure policy unchanged.
…ture The two handler-count assertions in test_logging.py counted ALL handlers on the conclave logger, which fails under newer pytest (9.x) whose logging plugin injects LogCaptureHandler instances. Filter to conclave's own StreamHandler via a _app_stream_handlers helper (excludes any handler whose class name contains "Capture") so the tests assert on conclave's one-shot config without coupling to the pytest version. Passes under pytest 8.4.2 and 9.1.0. Test-only; no source change. Unblocks CI for the key-leak audit PR.
…hain (RANK 1/5) The httpx exception raised on a transport failure carries a live .request whose .headers hold the Authorization/x-api-key value. Surfacing it as TransportError.__cause__/__context__ left the key one cause-chain hop away, leaking under traceback.format_exception, logging.exception, or a cause-chain repr. The four raise sites in post_json/stream_sse now route through _raise_transport_error (raise ... from None) plus a boundary clear that nulls __context__, so neither a formatter nor a direct attribute walk can reach the header-bearing exception. Pinned by V8 tests.
…t-out (RANK 6) Council.__init__ now calls transport.guard_transport_logging() automatically unless allow_transport_debug_logging=True, so a process holding a real key is protected from httpx/httpcore DEBUG header leakage out of the box. The guard is idempotent and scoped to the httpx/httpcore loggers only; it never touches the host root logger. SECURITY.md reworded from opt-in to default-on/opt-out, with a new What-IS-protected bullet and vector-map rows for the cause-chain hardening (RANK 1/5) and the default-on guard (RANK 6). Adds V9 (guard default-on + opt-out) and V10 (client close hygiene, RANK 8) regression tests.
…' into chore/v1-integrate-release-prep # Conflicts: # tests/test_logging.py
…to chore/v1-integrate-release-prep # Conflicts: # DOCUMENTATION_INDEX.md
- pyproject [project].version 0.3.0 -> 1.0.0 (name stays conclave-cli) - src/conclave/__init__.py __version__ -> 1.0.0 - DOCUMENTATION_INDEX current version line -> 1.0.0; index rows for CHANGELOG + keyleak-audit tests - new CHANGELOG.md (Keep-a-Changelog) covering v0.3.0 -> v1.0.0
This was referenced Jun 14, 2026
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.
v1.0.0 release integration
Integrates the three v1.0 readiness PRs into a single release-prep branch, bumps to 1.0.0, and adds a
CHANGELOG.md. This PR supersedes #42, #43, and #44 — all of their work is merged here.What merged
chore/v1-distribution-release-eng— distribution rename toconclave-cli, OIDC Trusted-Publisher release workflow + Sigstore signing + PEP 740 attestations,pip-auditCI job, hash-pinnedrequirements-dev.lock,RELEASING.md.sec/v1-keyleak-audit-threatmodel— key-leak hardening: httpx exception dropped fromTransportError.__cause__; transport-logging guard default-on inCouncil.__init__(opt-out viaallow_transport_debug_logging);SECURITY.mdthreat model;.gitleaks.toml;tests/test_keyleak_audit.py.feat/v1-synthesizer-behavior— synthesizer behavior documented + versioned:SYNTHESIS_PROMPT_VERSIONstamped onto everyCouncilResult.prompt_version; observable (never-silent) degradation across synthesize/debate/adversarial-judge; README "Synthesizer behavior" section;tests/test_synthesizer.py.Conflict resolutions (4)
tests/test_logging.py(chore(release): conclave-cli distribution + OIDC publish workflow + dep audit #42+security(keys): key-leak audit + SECURITY.md threat model #44+docs(synthesizer): document + version + test synthesizer behavior (v1.0 #5) #43) — all three made the same pytest-9.1.0 fix. Kept one robust version that counts only conclave's own handler viatype(h) is logging.StreamHandler(an exact-type allowlist, which inherently excludes pytest'sLogCaptureHandlerStreamHandler subclass). Passes on pytest 8.x and 9.x.README.md(chore(release): conclave-cli distribution + OIDC publish workflow + dep audit #42+docs(synthesizer): document + version + test synthesizer behavior (v1.0 #5) #43) — additive; kept both. Install instruction ispip install conclave-cli; "Synthesizer behavior" section retained. 326 lines.DOCUMENTATION_INDEX.md(chore(release): conclave-cli distribution + OIDC publish workflow + dep audit #42+docs(synthesizer): document + version + test synthesizer behavior (v1.0 #5) #43) — kept both branches' version-history rows; added the keyleak-audit test row, theCHANGELOG.mdproject-files row, and theprompt_versionmodel note; bumped the "current version" line to 1.0.0.src/conclave/council.py(security(keys): key-leak audit + SECURITY.md threat model #44+docs(synthesizer): document + version + test synthesizer behavior (v1.0 #5) #43) — auto-merged into different regions; verified both survive:Council.__init__keeps the transport-logging guard install AND the synthesizer wiring; the synthesis path keeps prompt-version stamping AND thesynthesis_error/verdict_errordegradation signals.Version bump
pyproject.toml[project].version→1.0.0(distribution name staysconclave-cli).src/conclave/__init__.py__version__→1.0.0.Build + tests (local, isolated worktree venv, pytest 9.1.0)
ruff check .clean ·ruff format --check .clean (31 files).fail_under=75).python -m build→conclave_cli-1.0.0-py3-none-any.whl+conclave_cli-1.0.0.tar.gz.CHANGELOG
New
CHANGELOG.md(Keep-a-Changelog) with a[1.0.0] - 2026-06-14entry covering distribution, key-leak hardening, synthesizer versioning, and release engineering, plus a post-1.0 roadmap note (vote mode #3, stdio MCP server #8) and compare links.🤖 Generated with Claude Code