Commit graph

3 commits

Author SHA1 Message Date
Bryan Helmkamp
2b53917759
fix(validate): treat undefined template vars in @file prompts as warnings (#290)
## Summary

`fabro validate` had inconsistent behavior for undefined template
variables depending on whether the prompt was inline or loaded via an
`@file` reference. Inline `{{ inputs.foo }}` produced a warning and
validation passed; the same expression inside a `@file`-imported prompt
produced a hard validation error.

Fixes #286.

## Root cause

Two template-rendering passes with different strictness, applied to
disjoint inputs:

1. **DOT-source pass**
(`lib/crates/fabro-workflow/src/operations/create.rs`) honored
`RenderMode::Structural` for `fabro validate` — undefined variables
downgraded to a `Severity::Warning` diagnostic, then lenient render
finished the job.
2. **Per-attribute pass**
(`lib/crates/fabro-workflow/src/transforms/variable_expansion.rs`)
inside `TemplateTransform` was always strict and had no `RenderMode`
awareness. Because `FileInliningTransform` runs *before*
`TemplateTransform`, expressions inside `@file` content only ever
encountered the strict pass.

## Fix

- Plumb `RenderMode` through `TransformOptions` into
`TemplateTransform`.
- In `RenderMode::Structural`, the transform catches
`TemplateError::UndefinedVariable` per attribute, emits a warning
diagnostic, and falls back to `render_lenient`.
- Diagnostics flow through a new `Transformed.diagnostics` field into
`Validated` alongside lint output.
- Diagnostics now include `node_id` when the undefined variable was
found inside a node attribute, which is more useful than the previous
"at line 1" location.
- `RenderMode` and the shared `template_undefined_variable_diagnostic`
helper moved to `pipeline/types.rs` so the transform layer can reach
them without a circular dep.

Strict mode (`fabro run`, preflight) is unchanged — undefined inputs
still hard-fail before a run is created.

## Behavior

Illustrative output shapes (variable names and line numbers depend on
the fixture):

Inline prompt (unchanged):
```
warning: undefined template variable `inputs.<name>` at line <n> (template_undefined_variable)
Validation: OK
```

`@file`-imported prompt (previously a hard error, now matches inline —
node-attributed instead of line-attributed):
```
warning [node: <id>]: undefined template variable `inputs.<name>` in node `<id>` (template_undefined_variable)
Validation: OK
```

## Test plan

- [x] `cargo nextest run --workspace` — 5773/5773 passing
- [x] `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D
warnings` clean
- [x] `cargo +nightly-2026-04-14 fmt --check --all` clean
- [x] New regression test
`bare_fabro_with_unbound_inputs_in_imported_prompt_validates_structurally_with_warning`
in `lib/crates/fabro-cli/tests/it/cmd/validate.rs` against new fixture
`test/templated_unbound_imported/`
- [x] Existing
`bare_fabro_with_unbound_inputs_validates_structurally_with_warning` and
`strict_render_hard_fails_on_unbound_inputs` still pass — verifies
inline structural and run-start strict behavior are both preserved
- [x] Manual reproduction of the exact inputs from the issue now
succeeds with a warning

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

---------

Co-authored-by: Aleksi Asikainen <1086393+salieri@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 17:31:22 -04:00
Bryan Helmkamp
f07bb4aaba
feat(cli): support sparse input overrides (#222)
## Summary
- Add repeatable `-I` / `--input KEY=VALUE` CLI overrides for workflow
run inputs on `fabro run`, `fabro create`, and `fabro preflight`. CLI
inputs are sparse per-key overrides that merge over the resolved config
inputs (preserving unrelated inherited values), unlike TOML
`[run.inputs]` which still replaces wholesale.
- Manifest bundling and graph-level goal resolution render workflow
source with the effective inputs before structural scanning, so
input-driven `@prompt`, `import`, and `stack.child_workflow` paths get
bundled correctly.
- Persist raw `KEY=VALUE` strings on `ManifestArgs.input` so server-side
replay applies the same sparse overrides on top of merged config.
- Review-driven cleanups: shared `TemplateContext::for_input_scan`
helper for the recurring "render inputs but defer goal" idiom (replaces
4 sites), `#[derive(Default)]` on `ManifestBuildInput` to drop
boilerplate, inline trivial `apply_input_overrides` wrapper, drop a
redundant clone, and tighten the parser/test helpers.

## Test plan
- [ ] `cargo nextest run -p fabro-cli -p fabro-config -p fabro-server -p
fabro-template -p fabro-workflow`
- [ ] `cargo +nightly-2026-04-14 fmt --check --all`
- [ ] `cargo +nightly-2026-04-14 clippy -p fabro-cli -p fabro-config -p
fabro-server -p fabro-template -p fabro-workflow --all-targets -- -D
warnings`
- [ ] Smoke: `fabro run <workflow> -I key=value --input other=42`
overrides those keys while preserving unrelated inherited inputs
- [ ] Smoke: `-I` accepts strings, integers, floats, booleans, empty
values; rejects arrays, inline tables, datetimes; rejects missing `=`
and empty key
- [ ] Smoke: input-driven `@prompts/{{ inputs.foo }}` and
`stack.child_workflow="{{ inputs.bar }}/workflow.fabro"` paths bundle
correctly when overridden via `-I`

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 10:00:35 -04:00
Bryan Helmkamp
283eab181f
refactor(docs): split docs/ into public/ and internal/
Invert the docs convention so the Mintlify-published site lives under
docs/public/ and internal artifacts (strategy docs, brainstorms, plans,
etc.) sit at docs/ root or docs/internal/. Tools that default to writing
into docs/ now land in the catch-all instead of leaking into the
published tree.

- Move Mintlify content (administration/, agents/, api-reference/,
  changelog/, core-concepts/, examples/, execution/, getting-started/,
  human-tools/, integrations/, languages/, reference/, tutorials/,
  workflows/, images/, logo/, docs.json, favicon.svg, dot-highlight.js)
  into docs/public/.
- Collapse docs-internal/ into docs/internal/.
- Update Rust path references (fabro-api/build.rs, fabro-server,
  fabro-dev), TypeScript generator arg, CI path filters, clippy.toml
  reasons, AGENTS.md/CLAUDE.md, and README.md image refs.

Mintlify dashboard project root must be updated to docs/public/ in a
follow-up. .mintignore move/trim and .claude/skills/ updates land in a
separate commit.
2026-04-27 07:21:13 -07:00
Renamed from docs/workflows/variables.mdx (Browse further)