fabro/lib
Scott Werner 41fb7e7e1f
Some checks are pending
Rust / Format (push) Waiting to run
Rust / Clippy (push) Waiting to run
Rust / Generated Docs (push) Waiting to run
Rust / Test (Linux) (push) Waiting to run
Rust / Test (macOS) (push) Waiting to run
inputs is template-only: reject {{ inputs.* }} in InterpString with a clear error (D12) (#513)
Implements the `inputs`-template-only half of **D12**. Independent off
`main` — touches only `fabro-types` interp; no overlap with #511 or
#512.

## What changes for users

`{{ inputs.* }}` in an `InterpString` field (command, script, header,
env, URL — MCP transports, prepare steps, hooks, server settings) now
fails with a **clear, actionable message**:

> `{{ inputs.X }}` is only available in prompts and goals, not in
command, script, header, env, or URL fields

It *already* failed there (no resolve context ever provided an inputs
lookup, so it errored as a generic "unavailable"); this makes the
rejection explicit and points the user at where `inputs` belongs.

## How

- **`ResolveCtx` drops its unused `inputs` lookup** (`with_inputs` had
zero production callers). The type now structurally cannot resolve
`inputs` in an `InterpString` field; `lookup_for(Inputs)` returns
`None`.
- The `Unavailable` error message is `inputs`-specific and points to
prompts/goals.
- **`substitute_with` still preserves `inputs` tokens**
(unknown-namespace passthrough), so `run.goal` — an `InterpString` that
feeds a template — keeps forwarding `{{ inputs.* }}` to its prompt/goal
render. This is the load-bearing behavior that makes "inputs works in
goals" coexist with "inputs rejected in InterpString fields", and it's
covered by an existing test
(`substitute_variables_preserves_late_bound_tokens`).
- Module docs updated: three resolvable namespaces in `InterpString`
(`env`/`vars`/`secrets`); `inputs` is template-only.

## Note on timing

The rejection fires at **resolve time** (use-time / run boundary), not
at `fabro validate`. That matches how the other late-bound namespaces
behave and keeps this PR small; a validate-time fail-fast would need to
distinguish goal (forwards inputs) from pure-`InterpString` fields and
is a larger, separate change if we want it.

## Tests

`resolve_with_rejects_inputs_as_template_only` (rejection + friendly
message); `substitute_variables_preserves_late_bound_tokens` confirms
goal forwarding is unaffected.

Verified: `cargo build --workspace`, nightly `clippy --workspace
--all-targets -D warnings`, `fmt`, `cargo nextest run --workspace`
(**6796 passed**).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 08:43:18 -04:00
..
crates inputs is template-only: reject {{ inputs.* }} in InterpString with a clear error (D12) (#513) 2026-06-24 08:43:18 -04:00
packages/fabro-api-client feat(workflow): support overriding cwd for local sandbox provider (#467) 2026-06-14 12:32:32 -04:00