Skip to content

cli: exit-code contract for zero-answer runs + close pooled httpx client#32

Merged
ernestprovo23 merged 2 commits into
mainfrom
cluster/cli
Jun 9, 2026
Merged

cli: exit-code contract for zero-answer runs + close pooled httpx client#32
ernestprovo23 merged 2 commits into
mainfrom
cluster/cli

Conversation

@ernestprovo23

Copy link
Copy Markdown
Member

Summary

Implements the CLI / lifecycle cluster of the Wave 2 backlog.

closes #17
closes #20

#17 — exit-code contract on conclave ask

Previously --json returned to its early-return before the no-members guard, so a
keyless run exited 0 with an empty payload while the human path exited 1 — a
CI/script consumer couldn't tell "no keys" from "ran fine".

New contract (documented in the ask command help):

code meaning
0 at least one usable member answer
1 run completed but produced zero usable answers (no keys, or every member failed). Under --json the full JSON is still written to stdout first, then exit 1
2 usage/config error (unknown mode, or no members resolved)

The failure signal is computed once as not result.successful_answers and applied to
both the JSON and human paths, so an all-members-failed run is now also a non-zero exit
(previously the human path only checked result.answers non-emptiness). successful_answers
is consistent across all four modes (synthesize/raw/debate/adversarial all populate
result.answers).

#20 — pooled httpx client lifecycle

transport.aclose() had no callers, leaking the process-wide AsyncClient.

  • The sync wrappers (ask_sync/debate_sync/adversarial_sync) now close the client in a
    finally inside the same asyncio.run loop that used it, so the CLI never leaks the
    pool and no "unclosed client" warning fires under strict asyncio.
  • Added Council.aclose() (async) and Council.close_sync() so long-lived library/server
    consumers can release the pool on shutdown; close_sync passes close_client=False to
    avoid a redundant double-close.
  • Re-exported conclave.aclose and documented the shutdown contract in the package docstring.

Tests

New tests/test_cli.py (Typer CliRunner, offline):

  • zero members → exit 1 on both plain and --json (JSON still emitted)
  • all members failed → exit 1
  • successful run → exit 0 with answers in the payload
  • bad mode / empty council → exit 2
  • transport.aclose() is actually invoked on sync run completion (no leaked client)
  • close_sync() fires aclose exactly once; real _get_client()aclose() closes the client

cli.py coverage 0% → 43%; total 82.7% (floor 75%). ruff check + ruff format --check
clean. 77 passed (was 68).

Do not merge — leaving checks to run.

ernestprovo23 and others added 2 commits June 8, 2026 22:22
closes #17: establish a documented exit-code contract on `conclave ask`. A
run that produces zero *usable* member answers now exits non-zero (1) on both
the human and `--json` paths. Under `--json` the full JSON result is still
emitted to stdout first, so a script can parse the payload and detect failure
via the exit code. Usage/config errors keep their distinct code 2. The check
is computed once (`result.successful_answers`) and applied to both paths, so an
all-members-failed run is also treated as a failure (previously the human path
keyed off `result.answers` non-emptiness and the JSON path always exited 0).

closes #20: wire the pooled httpx client lifecycle. The sync wrappers
(`ask_sync`/`debate_sync`/`adversarial_sync`) now close the shared client in a
`finally` inside the same event loop that used it, so the CLI never leaks the
pool and no "unclosed client" warning fires. Added `Council.aclose()` /
`Council.close_sync()` and re-exported `conclave.aclose` so long-lived
library/server consumers can release the pool on shutdown.

tests: new tests/test_cli.py (Typer CliRunner) pins the exit-code contract
(zero members, all-failed, success, bad mode, empty council) and verifies
`transport.aclose()` is actually invoked on sync run completion (no leaked
client). cli.py coverage 0% -> 43%; total 82.7% (floor 75%).
@ernestprovo23 ernestprovo23 merged commit fb33af9 into main Jun 9, 2026
5 checks passed
@ernestprovo23 ernestprovo23 deleted the cluster/cli branch June 10, 2026 00:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pooled httpx client never closed (aclose has no callers) --json exits 0 with empty payload when no members available (exit-code contract)

1 participant