fabro/lib
Scott Werner 882d11288b
Some checks failed
Rust / Format (push) Has been cancelled
Rust / Clippy (push) Has been cancelled
Rust / Generated Docs (push) Has been cancelled
Rust / Test (Linux) (push) Has been cancelled
Rust / Test (macOS) (push) Has been cancelled
A goal can't reference itself; prompts can reference the goal (#512)
Implements the goal self-reference behavior for interpolation
unification. Independent off `main` — no dependency on the other interp
PRs (touches only the template/goal-render path).

## What changes for users

A graph `goal` is a template that interpolates `{{ inputs.* }}`
(unchanged). A node `prompt` can reference the rendered goal via `{{
goal }}` (unchanged). **New:** a goal can **no longer reference itself**
— `{{ goal }}` *inside* a goal was previously a silent passthrough (left
as the literal text `{{ goal }}`); it's now a clear error.

```
graph [goal="Refine {{ goal }}"]   # error: a goal cannot reference itself
work  [prompt="Work on {{ goal }}"] # fine: prompts reference the rendered goal
```

## How

- **Structural guarantee:** the goal renders with **no `goal` key in
scope** (`TemplateContext::new().with_inputs(..)` instead of the
`for_input_scan` passthrough), so a self-reference can't resolve.
- **Friendly lint:** before rendering, `resolved_goal` checks the goal
template for a top-level `goal` reference — new
`fabro_template::references_top_level_variable`, backed by MiniJinja
`undeclared_variables` — and emits a dedicated `goal_self_reference`
diagnostic (`Severity::Error`) with a clear message and fix-it, instead
of a generic "undefined variable `goal`". Fails `fabro validate` and
run-create alike.

The goal is resolved in two transform passes (FileInlining +
TemplateTransform); the diagnostic is emitted **once** (FileInlining
discards its goal-resolution diagnostics; TemplateTransform is the
canonical emitter).

## Behavior change (release notes)

A goal containing `{{ goal }}` now **errors** instead of passing through
as literal text. The error message is the migration signal.

## Tests

- `references_top_level_variable` detection
- transform-level rejection (`Severity::Error`)
- single-emission across the two passes
- end-to-end `validate` rejection
- existing goal/prompt tests still green (prompts reference goal; goal
interpolates inputs)

## Verification

- `cargo build --workspace`
- `cargo +nightly clippy --workspace --all-targets -- -D warnings`
- `cargo +nightly fmt --check`
- `cargo nextest run --workspace`: 6800 passed

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 19:14:28 -04:00
..
crates A goal can't reference itself; prompts can reference the goal (#512) 2026-06-18 19:14:28 -04:00
packages/fabro-api-client feat(workflow): support overriding cwd for local sandbox provider (#467) 2026-06-14 12:32:32 -04:00