Repository navigation
Commit d07bbbd
authored
chore(release): integrate v1.0 PRs + bump 1.0.0 + CHANGELOG (#45)
* chore(release): conclave-cli distribution + OIDC publish workflow + dep 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.
* docs(synthesizer): document + version + test synthesizer behavior (v1.0 #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).
* test(logging): count only conclave's own handler, not pytest caplog handlers
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).
* test(logging): make handler-count assertions robust to pytest 9.x log 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.
* security(keys): key-leak audit + SECURITY.md threat model
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.
* test(logging): make handler-count assertions robust to pytest log capture
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.
* security(transport): drop httpx exception from TransportError cause chain (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.
* security(council): install transport-logging guard by default with opt-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.
* release: bump to 1.0.0 + add CHANGELOG + index updates
- 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.01 parent 97b8ec9 commit d07bbbd
20 files changed
Lines changed: 2804 additions & 74 deletions
File tree
- .github/workflows
- src/conclave
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
62 | 62 | | |
63 | 63 | | |
64 | 64 | | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
0 commit comments