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
24 changes: 12 additions & 12 deletions .drift/acks/cli-docs.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
contract = "cli-docs"
digest = "sha256:2ac30bcfcf3e7d174db15b59fa2df4e9cb64c5c6a2c64f1d9e8bef415a4efd8f"
reviewed_by = "Claude (Opus 5.5)"
reviewed_at = "2026-09-25T12:48:43Z"
rationale = "Merge of origin/feature/Log-sink-seam (carrying dev's releases v0.61.0 through v0.65.0) into feature/Log-redaction. Against the parent the only trigger change is this branch's doctor_cmd.py arm that keeps an installed sink which failed during the replay of the held records, and the checks.log_sink row in pipelex/cli/agent_cli/CLAUDE.md carries that fourth case in the merged tree. Against this branch's previous head the only trigger change is the parent's check_model_cmd.py argument-help example moving from the retired @best-claude alias to @best-gpt, and neither docs/tools/cli/ nor the agent-CLI contract doc quotes that example or any retired deck alias (a grep for best-claude, best-gemini and best-mistral over both targets finds nothing). docs/tools/cli/agent-cli.md describes the doctor command by its flags and formats only, so nothing there is stale. The ack file's merge spliced the parent's header onto this branch's trigger snapshot, so this re-ack records the merged tree as reviewed as a whole. No doc change needed."
digest = "sha256:23181f87f119d19610be5c07c0757df6d1c4fdbca32c2265a9d0987053cb4790"
reviewed_by = "Louis Choquel"
reviewed_at = "2026-09-26T17:15:21Z"
rationale = "Review round 2: the fix commands' 'suggested fix not applied' tip now counts only safe fixes, and rename-model (unsafe) is not a --select/--ignore code; docs/tools/cli/fix.md says so. validate.md and agent-cli.md reviewed, no further change needed."

[trigger_files]
"pipelex/cli/__init__.py" = "blob:8b137891791fe96927ad78e64b0aad7bded08bdc"
Expand All @@ -12,7 +12,7 @@ rationale = "Merge of origin/feature/Log-sink-seam (carrying dev's releases v0.6
"pipelex/cli/agent_cli/commands/__init__.py" = "blob:e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
"pipelex/cli/agent_cli/commands/accept_gateway_terms_cmd.py" = "blob:cb8338c09a7b5dee6088551c156d69dab613213a"
"pipelex/cli/agent_cli/commands/agent_cli_factory.py" = "blob:49b8bab889d8673e7d4f1d683efe0928e43914b9"
"pipelex/cli/agent_cli/commands/agent_output.py" = "blob:3dc0283ef1daee4e17183b01f8a5c9d92f2688af"
"pipelex/cli/agent_cli/commands/agent_output.py" = "blob:27f8c90f6a209ee780e06f58f630d7efa59227ab"
"pipelex/cli/agent_cli/commands/bundle_path_resolver.py" = "blob:30339c5d91048f4e67312d5691ed9a65d57472e0"
"pipelex/cli/agent_cli/commands/check_model_cmd.py" = "blob:91589e517509248a4639a37e82f46a69538f8071"
"pipelex/cli/agent_cli/commands/codegen/__init__.py" = "blob:e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
Expand Down Expand Up @@ -47,11 +47,11 @@ rationale = "Merge of origin/feature/Log-sink-seam (carrying dev's releases v0.6
"pipelex/cli/agent_cli/commands/run/pipe_cmd.py" = "blob:df5e7f80a328932ffde901eda8d0585d009678ad"
"pipelex/cli/agent_cli/commands/run/stdin_resolver.py" = "blob:c7c7d7495508f1c4e5a46eba336a4ab1694ebcf7"
"pipelex/cli/agent_cli/commands/validate/__init__.py" = "blob:e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
"pipelex/cli/agent_cli/commands/validate/_validate_core.py" = "blob:df07de7f203f57041907e080bc5fd80f9f593301"
"pipelex/cli/agent_cli/commands/validate/_validate_core.py" = "blob:de1e56549d9e036d218cfd308174082bab7bc800"
"pipelex/cli/agent_cli/commands/validate/app.py" = "blob:d2d0ed19288c2d1c25be1650dd962189fcdb3b86"
"pipelex/cli/agent_cli/commands/validate/bundle_cmd.py" = "blob:27cc2c0b0a7fb5d3a232c6c3ce44663572e14260"
"pipelex/cli/agent_cli/commands/validate/method_cmd.py" = "blob:794914167c13975bc0cdff7b301a8268ff1b79b6"
"pipelex/cli/agent_cli/commands/validate/pipe_cmd.py" = "blob:2f7dce6a1d3d9987aa14146bac79d6b0e7e985e1"
"pipelex/cli/agent_cli/commands/validate/bundle_cmd.py" = "blob:fb4aab0b8e4e381bb0c44cb07308987938d69e41"
"pipelex/cli/agent_cli/commands/validate/method_cmd.py" = "blob:cf841e73d6af1a7e90d5ddfadaad012928f1a3ba"
"pipelex/cli/agent_cli/commands/validate/pipe_cmd.py" = "blob:36f660819d85bca46bc4348f4b3e7a75fb393d19"
"pipelex/cli/bundle_target_resolution.py" = "blob:8dccc50b700b877968ed1b2c1c3652ab1c32e956"
"pipelex/cli/cli_factory.py" = "blob:1f859ab3b995e1a67e5fdc812a168d56c338e970"
"pipelex/cli/commands/__init__.py" = "blob:e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
Expand Down Expand Up @@ -85,7 +85,7 @@ rationale = "Merge of origin/feature/Log-sink-seam (carrying dev's releases v0.6
"pipelex/cli/commands/doctor_cmd.py" = "blob:26db32ff20d1c064bc421093825a05e980c9b770"
"pipelex/cli/commands/fix/__init__.py" = "blob:e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
"pipelex/cli/commands/fix/_diff_sandbox.py" = "blob:5ce264f5b0ab4ff4172f4d8789450916947f5c0e"
"pipelex/cli/commands/fix/_fix_core.py" = "blob:8f6a187a0fb42c498629b177753d81620800b0da"
"pipelex/cli/commands/fix/_fix_core.py" = "blob:dac7056fcaf50210c910e4cad1ba14328b7f8adc"
"pipelex/cli/commands/fix/app.py" = "blob:6f019930e8e3e0043ee46d87493ba4602eb33a76"
"pipelex/cli/commands/fix/bundle_cmd.py" = "blob:427364cc453957dfe0cec879f019050329c13b3c"
"pipelex/cli/commands/graph_cmd.py" = "blob:d54737af7efc9ab9bbf3f978050d017af0e27a41"
Expand Down Expand Up @@ -120,14 +120,14 @@ rationale = "Merge of origin/feature/Log-sink-seam (carrying dev's releases v0.6
"pipelex/cli/commands/show_cmd.py" = "blob:9492c9af966877f694ca61f7b1db3d151a1ddf56"
"pipelex/cli/commands/update_cmd.py" = "blob:06100ec3b9d6c66ca89a8c1536aa27983ae409ca"
"pipelex/cli/commands/validate/__init__.py" = "blob:e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
"pipelex/cli/commands/validate/_validate_core.py" = "blob:3294c9c716ac616a89213c7356d38a8eff62da40"
"pipelex/cli/commands/validate/_validate_core.py" = "blob:ef7e10657b5a9ffd84f8cf01a4ffa76ead726da7"
"pipelex/cli/commands/validate/app.py" = "blob:309061423af13f2f6c496a024306ad696af567ab"
"pipelex/cli/commands/validate/bundle_cmd.py" = "blob:1fbf3e0cf695db39101c83160a724df6468988e9"
"pipelex/cli/commands/validate/method_cmd.py" = "blob:f12ee233fc985d04a4512f1fef6b9ea02a1b9644"
"pipelex/cli/commands/validate/pipe_cmd.py" = "blob:d67267571559006459628180f2c8b216ba7fee31"
"pipelex/cli/commands/which_cmd.py" = "blob:68661d4bab22e2c4dab183d59bd9f2c6dd573472"
"pipelex/cli/deck_notice.py" = "blob:542877125e89e35b02bc969cfee952155b0d7c2c"
"pipelex/cli/error_handlers.py" = "blob:41f33d7eae4d583f380f3ec34637733b0ca9d840"
"pipelex/cli/error_handlers.py" = "blob:c704505651f24a0e9bb85fed0da1d00445c9e980"
"pipelex/cli/exceptions.py" = "blob:1d51c3a53ddd3f6cf8de566fa2ade3fa75ab7a3a"
"pipelex/cli/installed_methods.py" = "blob:09197f47baf8d7251d28d148a0544b4a23ee7365"
"pipelex/cli/method_resolver.py" = "blob:1230dde5f89ac28bbdcbcbbd0c1d485c71803caf"
Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

## [Unreleased]

### Added

- **An unknown model is a located `unknown_model` validation item**: a pipe whose model field names a handle, alias, preset or waterfall the model deck does not define now validates to an invalid verdict with one `pipe_validation` item of the new closed `error_type` `unknown_model`, carrying the `pipe_code`, `domain_code`, `source`, `field_name`, the `field_path` of the reference (`pipe.<code>.model`, or `pipe.<code>.model_to_structure`), and the new optional item fields `model_reference` (the reference as written), `model_type` and `suggestions` (the deck's close matches of the same kind, also kept in the message). Every pipe type that names a model gives the same item, where `PipeExtract` and `PipeSearch` used to give an `unknown_validation_error` whose message was a Python repr, and with exactly one suggestion the item carries an unsafe `rename-model` fix, which `pipelex fix bundle` never applies on its own because a fuzzy match can name a different model. The MTHDS Test Corpus gains the `invalid_unknown_model` entry covering the new `error.unknown_model` tag.

### Changed

- **Every refusal raised while loading a bundle is a validation verdict (Breaking)**: validation turns any refusal of the caller's input that has no arm of its own into an invalid verdict with one item, keeping the refusal's message only when it is caller-facing and otherwise its title, while a configuration or runtime fault still propagates as no verdict. A refusal raised while a pipe is built leaves the library load as the new `PipeLoadRefusalError`, naming the pipe, its domain and its file with the original as its cause, and gives a `pipe_validation` item located there; any other gives a `blueprint_validation` item. `pipelex validate bundle` no longer prints a traceback for an unknown model, `pipelex-agent validate bundle` answers it with `is_valid: false` and exit 1 instead of the no-verdict envelope and exit 2, and the in-process validator behind `/validate` returns it as an invalid verdict instead of raising.
- **`PipeOperatorModelChoiceError` is the located unknown-model refusal (Breaking)**: it is now raised whenever a pipe naming an unknown model is built, including on the run path, which still refuses the bundle before any pipe runs with an HTTP 422 whose message now names the pipe and the field; it is `input`-domained and caller-facing, carries `domain_code`, `field_name`, `suggestions` and `source`, and its `model_choice` is the reference as written. The agent CLI's hint for it points at `check-model` rather than `doctor`.
- **`pipelex validate pipe` and `pipelex validate --all` render a refusal as an invalid bundle**: a pipe whose dry run fails, and a refusal of the libraries while they load, now print the grouped invalid-bundle output and exit 1 instead of a traceback; `pipelex-agent validate pipe` and `--all` answer a load-time refusal with the invalid-verdict envelope.

## [v0.66.0] - 2026-09-25

### Highlights
Expand Down
2 changes: 1 addition & 1 deletion docs/contribute/mthds-test-corpus.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ An entry directory holds either exactly one `.mthds` file, or several with a `bu
]
```

4. **Name no model.** Presets and aliases are resolved by the validation engine, not only at run time, so an entry pinning one fails validation outright on any consumer whose deck does not define it — which turns an entry about a language feature into an entry about model selection. Leave the choice to each consumer's deck.
4. **Name no model.** Presets and aliases are resolved by the validation engine, not only at run time, so an entry pinning one fails validation outright on any consumer whose deck does not define it — which turns an entry about a language feature into an entry about model selection. Leave the choice to each consumer's deck. The one entry that names a model is `invalid_unknown_model`, whose defect is exactly that: it names a handle no deck defines, so it fails with `unknown_model` on every consumer.

5. **Validate it locally** — against the local runtime, never the hosted API, which lags it:

Expand Down
1 change: 1 addition & 0 deletions docs/errors/authoring-and-language.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ own page. Classes are grouped by subsystem.
- [`PipeFactoryError`](pipe-factory-error.md) — Pipe factory
- [`PipeInputError`](pipe-input-error.md) — Pipe input
- [`PipeInputsFactoryError`](pipe-inputs-factory-error.md) — Pipe inputs factory
- [`PipeLoadRefusalError`](pipe-load-refusal-error.md) — Pipe load refusal
- [`PipeOperatorModelChoiceError`](pipe-operator-model-choice-error.md) — Pipe operator model choice
- [`PipeRunError`](pipe-run-error.md) — Pipe run
- [`PipeRunInputsError`](pipe-run-inputs-error.md) — Pipe run inputs
Expand Down
2 changes: 1 addition & 1 deletion docs/errors/model-choice-not-found-error.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description: "Reference for the `ModelChoiceNotFoundError` Pipelex error class."

# Model choice not found

Error raised when a model choice cannot be found in the model deck.
Raised when a model reference names a handle, alias, preset or waterfall the model deck does not define: by the deck check a pipe runs when it is built, and by the deck when a run resolves a reference. When a pipe is built, the pipe operator raises it again as a ``PipeOperatorModelChoiceError`` located on the pipe and the field, so a bundle naming an unknown model is an invalid validation verdict (error type ``unknown_model``), never a failure of the validator.

| Field | Value |
|---|---|
Expand Down
21 changes: 21 additions & 0 deletions docs/errors/pipe-load-refusal-error.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
title: "Pipe load refusal"
description: "Reference for the `PipeLoadRefusalError` Pipelex error class."
---

<!-- pipelex:generated -->

# Pipe load refusal

Raised by the library load when building one pipe of a bundle raises a refusal of the caller's input that carries no locator of its own. It names the pipe and its file, and bundle validation reports it as an invalid verdict.

| Field | Value |
|---|---|
| `error_type` | `PipeLoadRefusalError` |
| `title` | Pipe load refusal |
| `type_uri` | `https://docs.pipelex.com/latest/errors/pipe-load-refusal-error/` |
| `error_domain` | `input` |
| Defined in | `pipelex.core.pipes.exceptions` |
| Parent class | [`PipelexError`](pipelex-error.md) |

[Back to Error Reference](index.md)
4 changes: 3 additions & 1 deletion docs/errors/pipe-operator-model-choice-error.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ description: "Reference for the `PipeOperatorModelChoiceError` Pipelex error cla

# Pipe operator model choice

Raised by a pipe operator (``PipeLLM``, ``PipeStructure``, ``PipeImgGen``, ``PipeExtract``, ``PipeSearch``) when it is built from its blueprint and a model field names a model the model deck does not define. Bundle validation reports it as an invalid verdict whose item has the error type ``unknown_model``, and a run refuses the bundle with it before any pipe runs.

| Field | Value |
|---|---|
| `error_type` | `PipeOperatorModelChoiceError` |
| `title` | Pipe operator model choice |
| `type_uri` | `https://docs.pipelex.com/latest/errors/pipe-operator-model-choice-error/` |
| `error_domain` | _(inherited from parent)_ |
| `error_domain` | `input` |
| Defined in | `pipelex.core.pipes.exceptions` |
| Parent class | [`PipelexError`](pipelex-error.md) |

Expand Down
2 changes: 1 addition & 1 deletion docs/errors/validate-bundle-error.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description: "Reference for the `ValidateBundleError` Pipelex error class."

# Validate bundle

Raised when bundle validation fails.
Raised when a bundle is refused while it is loaded or validated: the invalid verdict, carrying one structured item per refusal in ``validation_errors``. Every refusal of the bundle itself becomes one — the parser, factory and pipe-validation errors, a failing dry run, an unknown model (``unknown_model``) and any other refusal of the caller's input — while a failure of the tool or its environment propagates as a no-verdict fault instead.

| Field | Value |
|---|---|
Expand Down
3 changes: 3 additions & 0 deletions docs/tools/cli/agent-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ For `bundle`, additional options are available:

On a successful run, the envelope also carries `pending_signatures` — the library-wide list of pipes still declared as `PipeSignature` (unimplemented forward declarations), each namespaced by `pipe_ref` (`domain.code`). In JSON it is a `pending_signatures` array, in markdown a "Pending signatures" section. A top-down build reads it to see exactly which headers remain to implement. The envelope also carries a derived `is_runnable` boolean (`true` ⇔ `pending_signatures` is empty), and the markdown states the runnability verdict in plain English — runnable when complete, NOT yet runnable above the "Pending signatures" section otherwise. `validate bundle`, `validate method`, and `validate pipe --all` carry them and gate on them: without `--allow-signatures`, the command exits non-zero when `is_runnable` is false. Bare `validate pipe <code>` omits them, and a `--pipe` slice surfaces them for information without gating.

!!! note "Every refusal of the bundle is an invalid verdict"
A refusal of your input raised while the bundle is loaded or validated is answered with the invalid-verdict envelope — `is_valid: false` and a `validation_errors` array — and exit code `1`, never the no-verdict envelope and exit code `2`, which is kept for failures of the tool or its environment. `validate pipe <code>` and `validate pipe --all` are the exception for a failing dry run: they answer it with `is_valid: false`, `error_type: DryRunError` and exit code `1`, without a `validation_errors` array. A pipe naming a model the model deck does not define gives one `pipe_validation` item with `error_type: unknown_model`, carrying `pipe_code`, `domain_code`, `source`, `field_path` (`pipe.<code>.model`), `model_reference` (the reference as written), `model_type` and `suggestions` (the deck's close matches), plus a `rename-model` `suggested_fix` when there is exactly one suggestion. Any other refusal of your input with no code of its own gives one item without an `error_type`, located on its pipe when it was raised while that pipe was built. A refusal whose class does not classify itself as your input yet, as some pipe-factory refusals do not, still leaves as no verdict. See [Error Model](../../under-the-hood/error-model.md#validation_errors-structured-bundle-validation-diagnostics).

!!! note "Advisory warnings on validate"
Whole-bundle and whole-library validate surfaces (`validate bundle`, `validate method`, `validate pipe --all`) also carry a `warnings` array — advisory optionality lints on a VALID bundle that never flip the verdict or the exit code. Each entry has the **same shape as a validation error item** (`category`, `error_type`, `pipe_code`, `domain_code`, `variable_names`, `message`) — this is a different shape from the `init`/`doctor` setup `warnings` (`{type, message}`) documented under Output Contract below. Three families ride the array, always in this order: the useless-`!` lint (`optional_force_redundant`), a `!` (force) input whose slot is guaranteed present in every analyzed flow, so the assertion can never fire; the vacuous-presence lint (`input_presence_vacuous`), an entry-pipe input that must be supplied but whose concept declares no required field, so the empty object satisfies it and a caller cannot tell what to fill in (see [Understanding Optionality](../../building-methods/pipes/understanding-optionality.md)); and the [intent-hint](../../building-methods/concepts/intent-hints.md) lints (`hint_unknown_key`, `hint_unknown_intent`, `hint_inapplicable_intent`). Every whole-bundle validate channel — this CLI, the bare CLI, the builder ops and the protocol validation report — assembles them from one composition point, so which advisories you see does not depend on which command you typed. Hint findings are bounded per site: a site naming many undefined keys reports the first few and then how many more there were, and a long authored key or value is elided in the message. In markdown, warnings render as a "Warnings" section. The array is empty when there is nothing to report; `validate pipe` omits it (no flow context to lint in). `validate bundle`/`validate method` with `--pipe` keep it, and it stays bundle-wide there — the slice narrows the dry run, not the validation.

Expand Down
2 changes: 1 addition & 1 deletion docs/tools/cli/fix.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ pipelex fix bundle my_bundle.mthds --select match-sequence-output

## What Gets Fixed

Only fixes classified as SAFE are applied — deterministic corrections derived from the structured validation errors, never from guesswork. Errors without a safe deterministic fix are left in place and reported as remaining errors. The available fix rule codes (for `--select`/`--ignore`) are listed in the error message when you pass an unknown code.
Only fixes classified as SAFE are applied — deterministic corrections derived from the structured validation errors, never from guesswork. Errors without a safe deterministic fix are left in place and reported as remaining errors. An unsafe fix, such as `rename-model` for a model name the deck only nearly matches, still shows on its error for you to apply by hand, and is not a code `--select` or `--ignore` accepts. The available fix rule codes (for `--select`/`--ignore`) are listed in the error message when you pass an unknown code.

## Related Documentation

Expand Down
Loading
Loading