mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-09 03:20:56 +00:00
# Demote non-category leak fields to plain `String` Second slice of the interpolation unification — the first **reducing** PR stacked on the foundation (#472), per the reduce-first sequencing: narrowing changes land before capability additions. (The other reducing slice, the DOT de-templating, already landed independently as #474.) ## Why The target model gives `InterpString` to fields in five categories — `command` / `script` / `headers` / `env` / `url` — wherever they appear. A handful of fields were typed `InterpString` but are *identifiers or commit content*, not in any category: - `run.model.provider` / `run.model.name` - `cli.exec.model.provider` / `cli.exec.model.name` - `run.git.author.name` / `run.git.author.email` - `run.scm.owner` / `run.scm.repository` Their consumers never resolved them — they leaked raw source text via `as_source()`. This PR demotes them to plain `String` (layer and resolved structs) with **no interpolation**. ## The principle: only `InterpString` fields access variables These fields are dropped from the variable substitute pass entirely, so both `{{ vars.* }}` and `{{ env.* }}` are now literal text. This **removes an incidental behavior**: run-scoped plain-`String` fields used to get `{{ vars.* }}` substituted via the String pass (a lucky accident), while `env` always leaked literally. Variable access becomes deliberate and typed rather than accidental; if any of these fields should support variables later, that's a controlled promotion back to `InterpString`. ## Behavior changes (honest list) - **The incidental run-scoped `{{ vars.* }}` substitution on these eight fields stops working.** To keep the removal visible rather than silent, a `tracing::warn!` fires at resolve time when a demoted field still contains claimed template tokens (`warn_if_demoted_template`). Unclaimed `{{ ... }}` text (jq programs, Go templates) never interpolated and does not warn. - `{{ env.* }}` / `{{ secrets.* }}` / `{{ inputs.* }}` never resolved on these fields, so nothing else changes. ## Added in review: D11 demotions (separate commit, revertable) The rule got refined during review: a field is `InterpString` iff it is in one of the five categories **and resolved at the run boundary** (the only point where `vars`/`secrets`/`inputs` exist — they're server state, so connect-time and startup-time fields can't reach them even in principle). A separate commit applies the clean subset so it can be cherry-picked out if we change course: - `cli.target.http.url` / `cli.target.unix.path` — consumed at CLI connect time; consumers only ever leaked raw source, so nothing working is removed. - `run.working_dir` — **the outlier; see the PR comment.** Its `{{ vars.* }}` substitution worked; demoted on the category test alone. ## What's deliberately NOT here - The **control-plane fields** (`server.storage.root` / `listen.unix.path` / S3 fields / `github.app_id/client_id/slug` / `server.api.url` / `server.web.url`) — untouched here, demoted in a follow-up PR. **Resolved during review** (see the resolution comment): `InterpString` was conflating the user-facing workflow language with the internal control plane. Control-plane fields never interpolate; the few deployment knobs that need late binding (e.g. `FABRO_WEB_URL`, whose only real usage is the split-web PoC ferrying a compose env var across a file mount) become explicit native `EnvVars` reads, and `fabro-server/src/interp.rs` shrinks to deletion. `slack.default_channel` stays `InterpString` (consumed with run context). ## Implementation notes - Consumers move from `as_source()` to direct `String` access; the foundation's `#[expect(disallowed_methods, ... demotion pending ...)]` annotations for these fields are removed (no longer `InterpString`). - `fabro-checkpoint`'s author plumbing and `fabro-manifest`'s scm fields simplify accordingly. ## Verification - `cargo build --workspace` - `cargo nextest run --workspace` → 6684 passed, 181 skipped - `cargo +nightly fmt --check --all` - `cargo +nightly clippy --workspace --all-targets -- -D warnings` → clean 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| build-support | ||
| fabro-acp | ||
| fabro-agent | ||
| fabro-api | ||
| fabro-auth | ||
| fabro-automation | ||
| fabro-checkpoint | ||
| fabro-cli | ||
| fabro-client | ||
| fabro-config | ||
| fabro-core | ||
| fabro-dev | ||
| fabro-dump | ||
| fabro-environment | ||
| fabro-github | ||
| fabro-graphviz | ||
| fabro-hooks | ||
| fabro-http | ||
| fabro-install | ||
| fabro-interview | ||
| fabro-llm | ||
| fabro-macros | ||
| fabro-manifest | ||
| fabro-mcp | ||
| fabro-mcp-server | ||
| fabro-model | ||
| fabro-oauth | ||
| fabro-options-metadata | ||
| fabro-proc | ||
| fabro-redact | ||
| fabro-sandbox | ||
| fabro-server | ||
| fabro-slack | ||
| fabro-spa | ||
| fabro-static | ||
| fabro-store | ||
| fabro-telemetry | ||
| fabro-template | ||
| fabro-test | ||
| fabro-tool | ||
| fabro-tracker | ||
| fabro-types | ||
| fabro-util | ||
| fabro-validate | ||
| fabro-variable | ||
| fabro-vault | ||
| fabro-workflow | ||