# client.proposals

> client.proposals — the proposals operations.

`client.proposals` — the `proposals` operations.

On `AsyncKindgi` every method is the same, awaited.

## `client.proposals.list()`

```python signature
list(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    status: Literal['draft', 'evaluating', 'evaluated', 'not-better', 'evaluation-failed', 'in-review', 'promoted', 'refused', 'rejected', 'expired', 'superseded', 'rolled-back', 'withdrawn'] | None = None,
    agent_id: str | UUID | None = None,
    tier: Literal['settings-block', 'prompt-block'] | None = None,
    scope_kind: Literal['tenant', 'org', 'project', 'segment'] | None = None,
    scope_id: str | UUID | None = None,
    segments: Sequence[ScopeSegment | Mapping[str, str]] | None = None,
    timeout: float | None = None,
) -> FixProposalCollectionPage
```

List improvement proposals. `GET /v1/proposals`

Newest first, cursor-paginated, only the proposals of agents the caller can read. Filters: `?agentId=`, `?tier=`, `?status=` (statuses are derived, so a page filtered by status can hold fewer rows than `limit`), and the live scope a proposal is for (`scopeKind`, `scopeId`, `segment`, exactly as the promotions history takes it).

## `client.proposals.create()`

```python signature
create(
    body: CreateProposalBody | Mapping[str, Any] | None = None,
    /,
    *,
    idempotency_key: str | None = None,
    timeout: float | None = None,
    **fields: Any,
) -> FixProposal
```

Propose new content for a data block. `POST /v1/proposals`

A hand-written proposal: new settings values or a new prompt template for a block `fromVersion` pins, for a live scope. Checked as publishing that block version would be (its schema carries over), and refused when it equals the pinned content. The same change from the same version for the same scope is one proposal: answered `200` with `X-Proposal-Deduped: true`. Needs `publish` on the agent.

## `client.proposals.improve()`

```python signature
improve(
    body: ImproveBody | Mapping[str, Any] | None = None,
    /,
    *,
    idempotency_key: str | None = None,
    timeout: float | None = None,
    **fields: Any,
) -> ImprovementPass
```

Start an improvement pass. `POST /v1/proposals/improve`

The runtime looks for better values for the version's tunable settings (keys its settings blocks' schemas mark `x-kindgi-tunable`) on the test set, within the budget, and writes its best candidate as an improvement proposal, which waits for a reviewer when requested. It answers at once with the pass, `running`. The pass works in one project, as an improve schedule does: `projectId`, else the scope's project, else (a tenant or org scope) the agent version's. The test set must be one that project reaches, and the pass's eval runs run there; its proposal's version is made where the agent's versions are. Checked first: the version is active and has something for the pass to work on (`400 validation-failed`, with `details.reason`): for a settings pass, a settings block with tunable keys (else `no-tunable-keys`), and for a prompt pass, instructions from a pinned prompt block (else `no-pinned-prompt-block`); the agent registry takes the versions a pass makes (`409 registry-read-only`; under `kindgi dev`, only for the whole tenant, since the pack's files serve every scope), and the agent has a live version for the whole tenant (`409 proposal-needs-pin`). No pass starts in a suspended project (`409 project-suspended`), and a pass going when it is ends `cancelled`, with the reason in `cancelReason`. Needs `publish` on the agent, and `write` on the pass's project when it isn't the version's. Without improvement passes in this runtime, `501 improve-unsupported`.

## `client.proposals.improve_estimate()`

```python signature
improve_estimate(
    body: ImproveBody | Mapping[str, Any] | None = None,
    /,
    *,
    timeout: float | None = None,
    **fields: Any,
) -> ImprovementPassEstimate
```

Estimate an improvement pass. `POST /v1/proposals/improve/estimate`

What a pass this input would start is estimated to cost, before you start it. The body is the start body of `POST /v1/proposals/improve`, resolved and reach-checked the same way: an unknown or inactive `fromVersion` (or the version serving the scope) answers `404 agent-version-not-found`, and a test set or scope the pass's project doesn't reach answers `400 reference-out-of-scope`, exactly as `improve` would. `estimatedCostUsd` is `min(budget.maxCostUsd, the replay count times the version's mean cost over its recent completed runs)` — clamped to the budget cap — and is absent when the version has no cost history; `basis` gives the unclamped inputs: the replay count, and that mean when there is one. The estimate prices every replay at that one mean, so a candidate whose prompt or settings cost more or less per run isn't captured, and the estimate leaves out a prompt pass's one-off drafting of its templates — a pass can cost more than the estimate. Because `estimatedCostUsd` is clamped to `budget.maxCostUsd`, an estimate at the cap means the pass is expected to spend its whole budget; the pass checks its cost before each comparison, so the one already running can take it a little over, and it starts no more once the cost reaches `budget.maxCostUsd`. It changes nothing (no pass starts) and takes no Idempotency-Key. Needs `publish` on the agent, and `write` on the pass's project when it isn't the version's. Without improvement passes in this runtime, `501 improve-unsupported`.

## `client.proposals.get()`

```python signature
get(proposal_id: str | UUID, /, *, timeout: float | None = None) -> FixProposal
```

Fetch an improvement proposal. `GET /v1/proposals/{proposalId}`

Needs `read` on its agent.

## `client.proposals.evaluate()`

```python signature
evaluate(
    proposal_id: str | UUID,
    body: EvaluateProposalBody | Mapping[str, Any] | None = None,
    /,
    *,
    idempotency_key: str | None = None,
    timeout: float | None = None,
    **fields: Any,
) -> FixProposal
```

Compare a proposal on a test set. `POST /v1/proposals/{proposalId}/evaluate`

The first evaluation publishes the block version and derives the agent version (`derivedFrom.proposalId`); they serve no scope until a promotion makes them live. Then a comparison eval run replays that version on the test set, against the recorded outputs (`baseline: 'recorded'`). Needs `publish` on the agent, and a live version of it for the whole tenant: an agent with none serves its latest version wherever nothing is pinned, so a new version would go live there at once (`409 proposal-needs-pin`). Allowed from `draft`, `evaluated`, `not-better`, `evaluation-failed`, `refused`, `superseded` and `expired`.

## `client.proposals.rescore()`

```python signature
rescore(
    proposal_id: str | UUID,
    /,
    *,
    idempotency_key: str | None = None,
    timeout: float | None = None,
) -> FixProposal
```

Rescore a proposal's latest evaluation. `POST /v1/proposals/{proposalId}/rescore`

After people judge a comparison's new answers on its replay runs (`perCase[].changes.new`, `runIds`), a new comparison eval run scores the same replays again, counting those judgments, as `POST /v1/eval-runs/{runId}/rescore` does: same test set version and settings, `comparison.rescoreOf`, nothing replayed. It becomes the proposal's evaluation, so the proposal is `evaluating`, then `evaluated` or `not-better` as the rescore says; the run rescored stays as it was. Takes no body fields. Needs `publish` on the agent. Allowed from `evaluated`, `not-better`, `refused`, `superseded` and `expired`.

## `client.proposals.request()`

```python signature
request(
    proposal_id: str | UUID,
    body: ProposalReasonBody | Mapping[str, Any] | None = None,
    /,
    *,
    idempotency_key: str | None = None,
    timeout: float | None = None,
    **fields: Any,
) -> FixProposal
```

Request a proposal's promotion for its scope. `POST /v1/proposals/{proposalId}/request`

A promotion of the candidate for the proposal's scope, with its evaluation's comparison, through the scope's gate: as `POST /v1/agents/{agentId}/promotions` answers. Needs `promote` on the agent. Allowed from `evaluated`, `not-better` (the gate decides), `refused`, `superseded` and `expired`.

## `client.proposals.rollback()`

```python signature
rollback(
    proposal_id: str | UUID,
    body: ProposalReasonBody | Mapping[str, Any] | None = None,
    /,
    *,
    idempotency_key: str | None = None,
    timeout: float | None = None,
    **fields: Any,
) -> FixProposal
```

Roll back a promoted proposal. `POST /v1/proposals/{proposalId}/rollback`

The scope goes back to the version its own pin held before the proposal's promotion (or, with none, falls back to the scope above). Only while the proposal's version still serves the scope. Needs `promote` on the agent.

## `client.proposals.withdraw()`

```python signature
withdraw(
    proposal_id: str | UUID,
    body: WithdrawProposalBody | Mapping[str, Any] | None = None,
    /,
    *,
    idempotency_key: str | None = None,
    timeout: float | None = None,
    **fields: Any,
) -> FixProposal
```

Withdraw a proposal. `POST /v1/proposals/{proposalId}/withdraw`

Closes it. Not while it's in review (decide its approval instead), nor once promoted, rejected or rolled back. Needs `publish` on the agent.

## `client.proposals.draft()`

```python signature
draft(*args: Any, **kwargs: Any) -> NoReturn
```

Removed in 0.1.5: use `client.proposals.create`.

## `client.proposals.dry_run()`

```python signature
dry_run(*args: Any, **kwargs: Any) -> NoReturn
```

Removed in 0.1.5: use `client.proposals.evaluate`.

## `client.proposals.submit_review()`

```python signature
submit_review(*args: Any, **kwargs: Any) -> NoReturn
```

Removed in 0.1.5: use `client.proposals.request`.

## `client.proposals.apply()`

```python signature
apply(*args: Any, **kwargs: Any) -> NoReturn
```

Removed in 0.1.5: use `client.proposals.request`.
