From 8a964d31f6bd89bbc8fd4ec0a34fc938e3fc718b Mon Sep 17 00:00:00 2001 From: Ralph Bean Date: Fri, 31 Jul 2026 10:02:13 -0400 Subject: [PATCH 01/12] docs(adr): add ADR 0080 for config.yaml vs. agent env var scope fullsend-ai/agents#567's review thread (discussion_r3686020058) found no rule for when a knob belongs in config.yaml vs. an agent's env.runner/ env.sandbox. ADR 0080 amends ADR 0049: cross-agent knobs are config.yaml fields (no {AGENT}_ prefix, no env var), single-agent knobs are {AGENT}_-prefixed env vars (no config.yaml field). Cross-references added to ADR 0049 and docs/architecture.md. Assisted-by: Claude Sonnet 4.6 Signed-off-by: Ralph Bean --- ...-agent-configuration-env-var-convention.md | 5 ++ ...0080-config-yaml-vs-agent-env-var-scope.md | 78 +++++++++++++++++++ docs/architecture.md | 5 ++ 3 files changed, 88 insertions(+) create mode 100644 docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md diff --git a/docs/ADRs/0049-agent-configuration-env-var-convention.md b/docs/ADRs/0049-agent-configuration-env-var-convention.md index 9ebadcb72a..022a495bd3 100644 --- a/docs/ADRs/0049-agent-configuration-env-var-convention.md +++ b/docs/ADRs/0049-agent-configuration-env-var-convention.md @@ -41,6 +41,11 @@ tune agent behavior. It does not retroactively rename existing context vars (event data like `GITHUB_PR_URL`, `ISSUE_NUMBER`) or infrastructure vars (tokens, paths, credentials). Those remain as they are. +This ADR covers naming and delivery once a knob is known to be +agent-specific. For the rule on whether a new knob should be an +`{AGENT}_` env var at all versus a `config.yaml` field, see +[ADR 0080](0080-config-yaml-vs-agent-env-var-scope.md). + ## Decision Agent configuration environment variables follow a single convention: diff --git a/docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md b/docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md new file mode 100644 index 0000000000..889328f6a4 --- /dev/null +++ b/docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md @@ -0,0 +1,78 @@ +--- +title: "80. config.yaml vs. agent env vars: where a config option belongs" +status: Accepted +relates_to: + - agent-infrastructure + - governance +topics: + - configuration + - harness + - conventions +--- + +# 80. config.yaml vs. agent env vars: where a config option belongs + +Date: 2026-07-31 + +Amends: [ADR 0049](0049-agent-configuration-env-var-convention.md) + +## Status + +Accepted + +## Context + +[fullsend-ai/agents#567](https://github.com/fullsend-ai/agents/pull/567) +added `TRIAGE_AUTO_CODE` as a harness `env.runner` default in +`harness/triage.yaml`. A reviewer flagged that fullsend-ai/fullsend#1754, +the issue that requested the knob, asked for it to "live in the per-repo/ +per-org config surface" — i.e. `.fullsend/config.yaml` — not a harness +default (see the [review +discussion](https://github.com/fullsend-ai/agents/pull/567#discussion_r3686020058)). +`config.yaml` already carries fields like `create_issues.allow_targets` +that are unrelated to any single agent. ADR 0049 defines how agent config +env vars are *named* (`{AGENT}_{SETTING_NAME}`) but says nothing about +*when* a knob should be a `config.yaml` field instead of an env var, so +there was no rule to check the PR against. + +## Decision + +A config option belongs in exactly one of the two surfaces, based on +whether its behavior is meaningful to more than one agent: + +- **Applies uniformly across agents (or to dispatch/policy behavior, not a + specific agent's inference-time logic):** it is a `config.yaml` field. + It gets a plain name with no `{AGENT}_` prefix, and it is not also + settable via environment variable — `config.yaml` (`internal/config` + accessors) is the single source of truth. `roles`, `auto_merge`, and + `create_issues.allow_targets` are existing examples. +- **Tunes the behavior of one specific agent:** it is an `{AGENT}_`-prefixed + env var per ADR 0049, delivered via that agent's `env.runner`/ + `env.sandbox`. It is not also settable as a `config.yaml` field — + overriding it per repo or org means overriding the harness (e.g. via + `base:` composition, per ADR 0045), not adding a parallel field to + `config.yaml`. + +A knob only moves from one surface to the other by a deliberate migration, +not by adding a second way to set the same value. Applying this rule to +`TRIAGE_AUTO_CODE`: it governs one agent's (triage's) behavior, so it +correctly belongs in `harness/triage.yaml` `env.runner`, not +`config.yaml`. fullsend-ai/fullsend#1754's "per-repo/per-org config +surface" request is satisfied by documenting the existing harness override +path (`base:` composition or an org/repo harness copy), not by adding a +`config.yaml` field — the gap the reviewer found is a documentation gap, +not a placement gap. + +## Consequences + +- Resolves the fullsend-ai/agents#567 ambiguity: `TRIAGE_AUTO_CODE` stays + an env var; `docs/agents/.md`'s Variables table must state how to + override it (which harness layer to edit), matching the guidance ADR + 0049 already expects. +- `config.yaml` cannot accumulate `{agent}_foo`-style fields — any such + field is a signal the knob was misplaced. +- An env var cannot quietly gain a `config.yaml` mirror with its own + precedence rules; there is one settable location per config option. +- A knob whose scope grows from one agent to several requires a new + decision (an ADR update or explicit review), not a silent field + addition to `config.yaml`. diff --git a/docs/architecture.md b/docs/architecture.md index e0deddd5a6..4d4ed6c0b5 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -119,6 +119,11 @@ repo baseline and overrides) `env.sandbox` in the harness YAML. Each agent documents its config vars in `docs/agents/.md` ([ADR 0049](ADRs/0049-agent-configuration-env-var-convention.md)). +- Config surface boundary: a knob that applies to one agent is an + `{AGENT}_`-prefixed harness env var (never a `config.yaml` field); a + knob that applies across agents or governs dispatch/policy is a + `config.yaml` field (never also an env var) + ([ADR 0080](ADRs/0080-config-yaml-vs-agent-env-var-scope.md)). - Agent-driven branch targeting: the code agent writes its chosen target branch to structured output. The post-script validates the choice against an allowlist and falls back to the repo's auto-detected default branch. From e94e2e6dd68bc00e753daf1e7305d3cc79576cba Mon Sep 17 00:00:00 2001 From: Ralph Bean Date: Fri, 31 Jul 2026 10:04:33 -0400 Subject: [PATCH 02/12] docs(adr): add clarified-by link from ADR 0049 to ADR 0080 Follows the Status-section annotation convention used elsewhere (e.g. ADR 0024's "Amended by" note) instead of duplicating the cross-reference in Context. Assisted-by: Claude Sonnet 4.6 Signed-off-by: Ralph Bean --- docs/ADRs/0049-agent-configuration-env-var-convention.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/ADRs/0049-agent-configuration-env-var-convention.md b/docs/ADRs/0049-agent-configuration-env-var-convention.md index 022a495bd3..0b770dc038 100644 --- a/docs/ADRs/0049-agent-configuration-env-var-convention.md +++ b/docs/ADRs/0049-agent-configuration-env-var-convention.md @@ -19,6 +19,10 @@ Date: 2026-06-16 Accepted +*Clarified by [ADR 0080](0080-config-yaml-vs-agent-env-var-scope.md), which +decides when a knob should be an `{AGENT}_` env var at all versus a +`config.yaml` field.* + ## Context Agents need behavioral knobs — settings that tune *how* they work without @@ -41,11 +45,6 @@ tune agent behavior. It does not retroactively rename existing context vars (event data like `GITHUB_PR_URL`, `ISSUE_NUMBER`) or infrastructure vars (tokens, paths, credentials). Those remain as they are. -This ADR covers naming and delivery once a knob is known to be -agent-specific. For the rule on whether a new knob should be an -`{AGENT}_` env var at all versus a `config.yaml` field, see -[ADR 0080](0080-config-yaml-vs-agent-env-var-scope.md). - ## Decision Agent configuration environment variables follow a single convention: From eb2c2d96414165e702332f96e0d833b27f5321e4 Mon Sep 17 00:00:00 2001 From: Ralph Bean Date: Fri, 31 Jul 2026 10:10:49 -0400 Subject: [PATCH 03/12] docs(byoa): document config.yaml vs. agent env var naming convention Cross-reference the {AGENT}_ prefix convention and the config.yaml scope boundary in the harness env reference, since bring-your-own-agent.md had no mention of either despite being the main custom-agent authoring guide. Assisted-by: Claude Sonnet 4.6 Signed-off-by: Ralph Bean --- docs/guides/user/bring-your-own-agent.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/guides/user/bring-your-own-agent.md b/docs/guides/user/bring-your-own-agent.md index 2b9878fe51..e19af09ff3 100644 --- a/docs/guides/user/bring-your-own-agent.md +++ b/docs/guides/user/bring-your-own-agent.md @@ -289,6 +289,13 @@ security: fail_mode: closed # "closed" (default) or "open" ``` +> **Naming convention:** Prefix settings that tune one agent's behavior with +> that agent's role in caps, e.g. `REVIEW_SEVERITY_THRESHOLD` — this avoids +> collisions when multiple agents share a sandbox or env file. A setting +> meant to apply the same way across every agent (like `roles` or +> `create_issues.allow_targets`) belongs in `config.yaml` instead, not as an +> env var — see [Registering your agent](#registering-your-agent). + ### Deprecated fields > **Deprecated:** `runner_env` is deprecated. Use `env.runner` From 9dcdffecdbb652e3a49091df11aacbe932439bbb Mon Sep 17 00:00:00 2001 From: Ralph Bean Date: Fri, 31 Jul 2026 10:25:25 -0400 Subject: [PATCH 04/12] docs: tweak language and drop unnecessary reference The link to registering-your-agent just isn't helpful here. Signed-off-by: Ralph Bean --- docs/guides/user/bring-your-own-agent.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/guides/user/bring-your-own-agent.md b/docs/guides/user/bring-your-own-agent.md index e19af09ff3..662637ebbf 100644 --- a/docs/guides/user/bring-your-own-agent.md +++ b/docs/guides/user/bring-your-own-agent.md @@ -291,10 +291,11 @@ security: > **Naming convention:** Prefix settings that tune one agent's behavior with > that agent's role in caps, e.g. `REVIEW_SEVERITY_THRESHOLD` — this avoids -> collisions when multiple agents share a sandbox or env file. A setting -> meant to apply the same way across every agent (like `roles` or -> `create_issues.allow_targets`) belongs in `config.yaml` instead, not as an -> env var — see [Registering your agent](#registering-your-agent). +> collisions when multiple agents share a sandbox or env file. +> +> A setting meant to apply the same way across every agent (like +> `roles` or `create_issues.allow_targets`) belongs in `config.yaml` +> instead, not as an env var. ### Deprecated fields From f9d3289570f42f19c92d46eddc252e4d18e63e93 Mon Sep 17 00:00:00 2001 From: fullsend-fix <278716306+fullsend-ai-coder[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2026 14:53:35 +0000 Subject: [PATCH 05/12] fix: address review feedback on PR #5798 - Use "Amended by" instead of "Clarified by" in ADR 0049's back-reference to ADR 0080, matching both the codebase pattern and ADR 0080's own "Amends" header. - Add "See also" cross-reference to ADR 0080 in ADR 0055's Consequences section where it discusses ADR 0049's naming convention. Addresses review feedback on #5798 --- docs/ADRs/0049-agent-configuration-env-var-convention.md | 2 +- docs/ADRs/0055-unified-env-var-delivery.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/ADRs/0049-agent-configuration-env-var-convention.md b/docs/ADRs/0049-agent-configuration-env-var-convention.md index 0b770dc038..67727bf21f 100644 --- a/docs/ADRs/0049-agent-configuration-env-var-convention.md +++ b/docs/ADRs/0049-agent-configuration-env-var-convention.md @@ -19,7 +19,7 @@ Date: 2026-06-16 Accepted -*Clarified by [ADR 0080](0080-config-yaml-vs-agent-env-var-scope.md), which +*Amended by [ADR 0080](0080-config-yaml-vs-agent-env-var-scope.md), which decides when a knob should be an `{AGENT}_` env var at all versus a `config.yaml` field.* diff --git a/docs/ADRs/0055-unified-env-var-delivery.md b/docs/ADRs/0055-unified-env-var-delivery.md index 6b6a260c37..cfbcd7bbc3 100644 --- a/docs/ADRs/0055-unified-env-var-delivery.md +++ b/docs/ADRs/0055-unified-env-var-delivery.md @@ -180,6 +180,9 @@ for harnesses that still reference it. `runner_env` deprecation. - ADR 0049's env var naming convention applies unchanged — the delivery mechanism changes but the `{AGENT}_{SETTING_NAME}` convention does not. + See also [ADR 0080](0080-config-yaml-vs-agent-env-var-scope.md), which + clarifies when a knob should use the `{AGENT}_` env var pattern versus + being a `config.yaml` field. - Modular `.env` files via `host_files` remain the right choice for per-tool env groups shared across multiple harnesses. - This change extends the harness schema; runners older than Phase 1 will From a8d654ab61a595fc2e05d5f280d3c00c0f41d88b Mon Sep 17 00:00:00 2001 From: Ralph Bean Date: Fri, 31 Jul 2026 12:19:57 -0400 Subject: [PATCH 06/12] docs(adr): address review feedback on ADR 0080 - Add an override convention paragraph clarifying that CI workflow env: is for infrastructure plumbing, not agent behavior knobs, and that env.runner/env.sandbox values must be literals since they don't shell-expand passthrough syntax (maruiz93 review comment) - Link ADR 0049 and the governance/agent-infrastructure problem docs inline in Context (qodo-code-review comment) Assisted-by: Claude Opus 4.6 Signed-off-by: Ralph Bean --- ...0080-config-yaml-vs-agent-env-var-scope.md | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md b/docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md index 889328f6a4..ceb63befda 100644 --- a/docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md +++ b/docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md @@ -30,10 +30,12 @@ per-org config surface" — i.e. `.fullsend/config.yaml` — not a harness default (see the [review discussion](https://github.com/fullsend-ai/agents/pull/567#discussion_r3686020058)). `config.yaml` already carries fields like `create_issues.allow_targets` -that are unrelated to any single agent. ADR 0049 defines how agent config -env vars are *named* (`{AGENT}_{SETTING_NAME}`) but says nothing about -*when* a knob should be a `config.yaml` field instead of an env var, so -there was no rule to check the PR against. +that are unrelated to any single agent, per the [governance](../problems/governance.md) +and [agent infrastructure](../problems/agent-infrastructure.md) problem +docs. [ADR 0049](0049-agent-configuration-env-var-convention.md) defines +how agent config env vars are *named* (`{AGENT}_{SETTING_NAME}`) but says +nothing about *when* a knob should be a `config.yaml` field instead of an +env var, so there was no rule to check the PR against. ## Decision @@ -53,6 +55,17 @@ whether its behavior is meaningful to more than one agent: `base:` composition, per ADR 0045), not adding a parallel field to `config.yaml`. +**Override convention:** `env.runner`/`env.sandbox` values are agent +defaults. A per-repo or per-org override edits the harness (`base:` +composition, per ADR 0045), not the CI workflow `env:` block — that block +is reserved for infrastructure plumbing (credentials, project IDs, +regions), not agent behavior knobs. This also means behavior defaults in +`env.runner`/`env.sandbox` must be literals (e.g. `TRIAGE_AUTO_CODE: +"on"`), not shell-style passthrough expressions (e.g. +`${TRIAGE_AUTO_CODE:-on}`) — those two mechanisms deliver plain key-value +pairs, not shell-expanded strings, so passthrough syntax would not expand +and instead be treated as a literal value. + A knob only moves from one surface to the other by a deliberate migration, not by adding a second way to set the same value. Applying this rule to `TRIAGE_AUTO_CODE`: it governs one agent's (triage's) behavior, so it From d6fced41996d47deafa6b41a24b86213cf7cb680 Mon Sep 17 00:00:00 2001 From: fullsend-fix <278716306+fullsend-ai-coder[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2026 16:47:31 +0000 Subject: [PATCH 07/12] docs(adr): fix incorrect failure mode for passthrough syntax in ADR 0080 The ADR incorrectly stated that shell-style passthrough expressions like ${TRIAGE_AUTO_CODE:-on} would be "treated as a literal value." In reality, os.Expand treats the entire VAR:-default as the variable name, which resolves to an empty string (data loss). Updated the text to describe the actual failure mode and added a cross-reference to ADR 0055's documentation of os.Expand's expansion rules. Addresses review feedback on #5798 --- docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md b/docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md index ceb63befda..80294b0eea 100644 --- a/docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md +++ b/docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md @@ -63,8 +63,11 @@ regions), not agent behavior knobs. This also means behavior defaults in `env.runner`/`env.sandbox` must be literals (e.g. `TRIAGE_AUTO_CODE: "on"`), not shell-style passthrough expressions (e.g. `${TRIAGE_AUTO_CODE:-on}`) — those two mechanisms deliver plain key-value -pairs, not shell-expanded strings, so passthrough syntax would not expand -and instead be treated as a literal value. +pairs, not shell-expanded strings, so passthrough syntax would be +mis-parsed — `os.Expand` treats the entire `VAR:-default` as the +variable name, which resolves to an empty string rather than applying +the intended default (see [ADR 0055](0055-unified-env-var-delivery.md), +§ Runner-side expansion). A knob only moves from one surface to the other by a deliberate migration, not by adding a second way to set the same value. Applying this rule to From 164abd0bd0f5232f8351b0876853e99146c8d42c Mon Sep 17 00:00:00 2001 From: Ralph Bean Date: Fri, 31 Jul 2026 16:48:14 -0400 Subject: [PATCH 08/12] fix(adr): tighten config.yaml vs env var boundary in ADR 0080 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses review feedback from waynesun09 and fullsend-ai-review: - Fix ADR 0055 cross-reference (§ Runner behavior, not § Runner-side expansion) - Swap auto_merge for kill_switch as a config.yaml example (auto_merge is dead code, see #5808) - State explicitly that this ADR narrows ADR 0049's per-agent-vars rule to the independently-tunable case, rather than claiming 0049 is silent on config.yaml - Rename the Decision's first prong to "pipeline/dispatch policy" so it doesn't read as agent behavior when it isn't - Acknowledge TRIAGE_AUTO_CODE as a boundary case rather than asserting its classification is settled Assisted-by: Claude Opus 4.6 Signed-off-by: Ralph Bean --- ...0080-config-yaml-vs-agent-env-var-scope.md | 55 ++++++++++++------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md b/docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md index 80294b0eea..c61488da91 100644 --- a/docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md +++ b/docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md @@ -33,33 +33,43 @@ discussion](https://github.com/fullsend-ai/agents/pull/567#discussion_r368602005 that are unrelated to any single agent, per the [governance](../problems/governance.md) and [agent infrastructure](../problems/agent-infrastructure.md) problem docs. [ADR 0049](0049-agent-configuration-env-var-convention.md) defines -how agent config env vars are *named* (`{AGENT}_{SETTING_NAME}`) but says -nothing about *when* a knob should be a `config.yaml` field instead of an -env var, so there was no rule to check the PR against. +how agent config env vars are *named* (`{AGENT}_{SETTING_NAME}`) and +requires separate per-agent vars when the same concept is independently +tunable per agent (e.g. `CODE_MAX_FILE_SIZE` vs `REVIEW_MAX_FILE_SIZE`), +but it draws no line against `config.yaml` — nothing in it says when a +knob should live there instead of as an env var, so there was no rule to +check the PR against. ## Decision A config option belongs in exactly one of the two surfaces, based on -whether its behavior is meaningful to more than one agent: +whether its behavior is meaningful to more than one agent. This narrows +ADR 0049's per-agent-vars rule (a setting that applies to multiple agents +gets separate vars per agent) to the case where each agent needs its own +independently tunable value; a single value meant to apply the same way +across every agent is a different case, and belongs in `config.yaml` +instead: -- **Applies uniformly across agents (or to dispatch/policy behavior, not a - specific agent's inference-time logic):** it is a `config.yaml` field. - It gets a plain name with no `{AGENT}_` prefix, and it is not also - settable via environment variable — `config.yaml` (`internal/config` - accessors) is the single source of truth. `roles`, `auto_merge`, and +- **Pipeline/dispatch policy — governs whether or how agents run, or + applies the same way across every agent, rather than tuning one agent's + own inference-time logic:** it is a `config.yaml` field. It gets a plain + name with no `{AGENT}_` prefix, and it is not also settable via + environment variable — `config.yaml` (`internal/config` accessors) is + the single source of truth. `roles`, `kill_switch`, and `create_issues.allow_targets` are existing examples. -- **Tunes the behavior of one specific agent:** it is an `{AGENT}_`-prefixed - env var per ADR 0049, delivered via that agent's `env.runner`/ - `env.sandbox`. It is not also settable as a `config.yaml` field — - overriding it per repo or org means overriding the harness (e.g. via - `base:` composition, per ADR 0045), not adding a parallel field to - `config.yaml`. +- **Single-agent behavior tuning — adjusts how one specific agent does its + own job:** it is an `{AGENT}_`-prefixed env var per ADR 0049, delivered + via that agent's `env.runner`/`env.sandbox`. It is not also settable as + a `config.yaml` field — overriding it per repo or org means overriding + the harness (e.g. via `base:` composition, per ADR 0045), not adding a + parallel field to `config.yaml`. **Override convention:** `env.runner`/`env.sandbox` values are agent defaults. A per-repo or per-org override edits the harness (`base:` composition, per ADR 0045), not the CI workflow `env:` block — that block is reserved for infrastructure plumbing (credentials, project IDs, -regions), not agent behavior knobs. This also means behavior defaults in +regions), not agent behavior knobs (see [ADR 0081](0081-reserve-workflow-env-for-infra-plumbing.md) +for the full rule and its exceptions). This also means behavior defaults in `env.runner`/`env.sandbox` must be literals (e.g. `TRIAGE_AUTO_CODE: "on"`), not shell-style passthrough expressions (e.g. `${TRIAGE_AUTO_CODE:-on}`) — those two mechanisms deliver plain key-value @@ -67,13 +77,20 @@ pairs, not shell-expanded strings, so passthrough syntax would be mis-parsed — `os.Expand` treats the entire `VAR:-default` as the variable name, which resolves to an empty string rather than applying the intended default (see [ADR 0055](0055-unified-env-var-delivery.md), -§ Runner-side expansion). +§ Runner behavior). A knob only moves from one surface to the other by a deliberate migration, not by adding a second way to set the same value. Applying this rule to -`TRIAGE_AUTO_CODE`: it governs one agent's (triage's) behavior, so it +`TRIAGE_AUTO_CODE` is a boundary case: it decides whether the code agent +runs next, which sounds like dispatch policy, but that decision is made +inside triage's own post-script, as part of triage's inference-time +behavior — not by a shared dispatch/CLI layer gating multiple agents +uniformly. That keeps it single-agent behavior tuning today, so it correctly belongs in `harness/triage.yaml` `env.runner`, not -`config.yaml`. fullsend-ai/fullsend#1754's "per-repo/per-org config +`config.yaml`. If the check is ever lifted out of triage's post-script +into a shared dispatch layer, it becomes pipeline/dispatch policy and +should move to `config.yaml` as a deliberate migration, not before. +fullsend-ai/fullsend#1754's "per-repo/per-org config surface" request is satisfied by documenting the existing harness override path (`base:` composition or an org/repo harness copy), not by adding a `config.yaml` field — the gap the reviewer found is a documentation gap, From 76dc57e493a8c9b7cd3629202c39799a6d4b172e Mon Sep 17 00:00:00 2001 From: Ralph Bean Date: Fri, 31 Jul 2026 16:48:22 -0400 Subject: [PATCH 09/12] docs(adr): add ADR 0081 reserving CI workflow env: for infra plumbing waynesun09's review of ADR 0080 found the CI workflow env: block already being used for agent behavior overrides (fullsend-ai/agents#567 docs), contradicting the harness-composition override path ADR 0080 states. Rather than folding this into 0080's scope, split it into its own decision: workflow env: is for infrastructure plumbing and CI-runtime-only values, never static agent behavior defaults. Amends ADR 0049's "CI workflow injection" delivery mechanism and is laterally related to ADR 0080. Assisted-by: Claude Opus 4.6 Signed-off-by: Ralph Bean --- ...-agent-configuration-env-var-convention.md | 9 ++- ...reserve-workflow-env-for-infra-plumbing.md | 74 +++++++++++++++++++ docs/architecture.md | 6 ++ 3 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 docs/ADRs/0081-reserve-workflow-env-for-infra-plumbing.md diff --git a/docs/ADRs/0049-agent-configuration-env-var-convention.md b/docs/ADRs/0049-agent-configuration-env-var-convention.md index 67727bf21f..d0924cf1b7 100644 --- a/docs/ADRs/0049-agent-configuration-env-var-convention.md +++ b/docs/ADRs/0049-agent-configuration-env-var-convention.md @@ -21,7 +21,9 @@ Accepted *Amended by [ADR 0080](0080-config-yaml-vs-agent-env-var-scope.md), which decides when a knob should be an `{AGENT}_` env var at all versus a -`config.yaml` field.* +`config.yaml` field, and [ADR 0081](0081-reserve-workflow-env-for-infra-plumbing.md), +which narrows the "CI workflow injection" delivery mechanism to +infrastructure values.* ## Context @@ -97,7 +99,10 @@ on the host. A config var needed by both must appear in both places. 3. **For CI workflow injection:** The CI workflow sets the value from org secrets, repo variables, or hardcoded defaults. This is the same mechanism - used for all other env vars — no change needed. + used for all other env vars — no change needed. *Note: [ADR 0081](0081-reserve-workflow-env-for-infra-plumbing.md) + narrows this to infrastructure values and CI-runtime-only computed + values; static agent behavior defaults go through harness composition + instead.* ### Defaults diff --git a/docs/ADRs/0081-reserve-workflow-env-for-infra-plumbing.md b/docs/ADRs/0081-reserve-workflow-env-for-infra-plumbing.md new file mode 100644 index 0000000000..ddda01fedd --- /dev/null +++ b/docs/ADRs/0081-reserve-workflow-env-for-infra-plumbing.md @@ -0,0 +1,74 @@ +--- +title: "81. Reserve CI workflow env: for infrastructure plumbing, not agent behavior" +status: Accepted +relates_to: + - agent-infrastructure + - governance +topics: + - configuration + - harness + - conventions +--- + +# 81. Reserve CI workflow env: for infrastructure plumbing, not agent behavior + +Date: 2026-07-31 + +Amends: [ADR 0049](0049-agent-configuration-env-var-convention.md) + +## Status + +Accepted + +## Context + +[ADR 0080](0080-config-yaml-vs-agent-env-var-scope.md) states that a +per-repo or per-org override of an agent behavior knob means overriding +the harness (`base:` composition, per [ADR 0045](0045-forge-portable-harness-schema.md)), +not the CI workflow `env:` block. Review of that ADR found the practice +already drifting: fullsend-ai/agents#567's docs describe setting +`TRIAGE_AUTO_CODE` via the workflow `env:` block, "matching the +convention used for `REVIEW_FINDING_SEVERITY_THRESHOLD`" — but +[ADR 0055](0055-unified-env-var-delivery.md)'s own canonical example sets +`REVIEW_FINDING_SEVERITY_THRESHOLD` in the harness's `env.sandbox`, not +the workflow file. Nothing had stated the workflow-env path was out of +bounds for behavior knobs, so nothing caught the drift. + +ADR 0049 lists "CI workflow injection" as one of three delivery +mechanisms for agent config vars, alongside `.env` files and +`runner_env`, without distinguishing infrastructure values (credentials, +project IDs, regions) from agent behavior knobs. + +## Decision + +The CI workflow `env:` block (`.github/workflows/.yml`) is +reserved for infrastructure plumbing — credentials, project IDs, +regions, and other values sourced from CI-native inputs (secrets, +org/repo variables). Agent behavior knobs, as scoped by ADR 0080 and +named per ADR 0049, are never set there. They go through harness +composition instead: `env.runner`/`env.sandbox` defaults live in the +canonical harness, and a per-repo or per-org override edits those +defaults via `base:` composition (ADR 0045). + +The one exception: a value that can only be computed at CI runtime — +derived from `github.event.*`, a build matrix variable, or a secret that +cannot be expressed as static harness data — may be set in the workflow +`env:` block even if it configures agent behavior. This ADR does not try +to enumerate every such case up front; a genuine new one can be added +here by minor annotation as it turns up. + +This narrows ADR 0049's "CI workflow injection" delivery mechanism: it +remains valid for infrastructure vars and CI-runtime-only values, not for +static agent behavior defaults. + +## Consequences + +- fullsend-ai/agents#567's docs need correcting: `TRIAGE_AUTO_CODE`'s + override path is harness composition, not CI workflow `env:` — the + precedent it cited was itself non-conformant. +- New agent behavior knobs get one documented override path (harness + `base:` composition), removing the ambiguity between three candidate + mechanisms. +- A workflow `env:` entry that sets an agent behavior default, and isn't + one of the CI-runtime-only exceptions, is a signal the knob was placed + in the wrong layer. diff --git a/docs/architecture.md b/docs/architecture.md index 4d4ed6c0b5..cdd9b7c420 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -124,6 +124,12 @@ repo baseline and overrides) knob that applies across agents or governs dispatch/policy is a `config.yaml` field (never also an env var) ([ADR 0080](ADRs/0080-config-yaml-vs-agent-env-var-scope.md)). +- CI workflow `env:` scope: the workflow `env:` block is reserved for + infrastructure plumbing (credentials, project IDs, regions) and values + computable only at CI runtime; agent behavior defaults are set via + harness `env.runner`/`env.sandbox` and overridden through `base:` + composition, never the workflow file + ([ADR 0081](ADRs/0081-reserve-workflow-env-for-infra-plumbing.md)). - Agent-driven branch targeting: the code agent writes its chosen target branch to structured output. The post-script validates the choice against an allowlist and falls back to the repo's auto-detected default branch. From a7b4585ed451c8ec32b93ee7fe38cae53b762702 Mon Sep 17 00:00:00 2001 From: fullsend-fix <278716306+fullsend-ai-coder[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2026 21:13:34 +0000 Subject: [PATCH 10/12] docs: align review.md guidance with ADR 0081 and cross-ref ADR 0024 - Update docs/agents/review.md to recommend harness env.sandbox and base: composition for REVIEW_FINDING_SEVERITY_THRESHOLD overrides instead of CI workflow env: block, per ADR 0081. - Add ADR 0081 cross-reference annotation to ADR 0024 to note the narrowed scope of CI workflow env: injection. Addresses review feedback on #5798 --- docs/ADRs/0024-harness-definitions.md | 5 +++++ docs/agents/review.md | 11 ++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/ADRs/0024-harness-definitions.md b/docs/ADRs/0024-harness-definitions.md index 5304240b85..22762ea578 100644 --- a/docs/ADRs/0024-harness-definitions.md +++ b/docs/ADRs/0024-harness-definitions.md @@ -28,6 +28,11 @@ and the manual `.env` file convention.* adds `openshell.profiles` and URL-based `providers` fields to the harness schema for portable provider and profile resolution.* +*See also [ADR 0081](0081-reserve-workflow-env-for-infra-plumbing.md), which +narrows the CI workflow `env:` injection path described in "Template +instantiation" below to infrastructure plumbing and CI-runtime-only values; +static agent behavior defaults go through harness composition instead.* + ## Context Each agent invocation requires configuration that ties together several moving diff --git a/docs/agents/review.md b/docs/agents/review.md index b7e8c1ea55..dbeeefff49 100644 --- a/docs/agents/review.md +++ b/docs/agents/review.md @@ -83,9 +83,14 @@ See [Configuring with AGENTS.md](../guides/user/customizing-with-agents-md.md) a |----------|-------------|---------|--------------| | `REVIEW_FINDING_SEVERITY_THRESHOLD` | Minimum severity for findings to include in the review. Findings below this level are omitted from both the narrative body and the posted inline comments. | `low` | `info`, `low`, `medium`, `high`, `critical` | -Set this in the CI workflow `env:` block. The env file passes it to the -sandbox automatically, and the post-script reads it from the runner -environment directly — no separate configuration is needed. +Set this in the harness's `env.sandbox` (the upstream default lives in +`harness/review.yaml`). To override per repo or org, use `base:` +composition ([ADR 0045](../ADRs/0045-forge-portable-harness-schema.md)) +rather than the CI workflow `env:` block — workflow `env:` is reserved +for infrastructure plumbing per +[ADR 0081](../ADRs/0081-reserve-workflow-env-for-infra-plumbing.md). +The post-script reads the value from the runner environment directly — +no separate configuration is needed. The review agent omits findings below the threshold from its output. The post-script also filters the structured `findings` array as From afc0e1a461c00d3cf6a3e2e4dc54e01aac489203 Mon Sep 17 00:00:00 2001 From: Ralph Bean Date: Mon, 3 Aug 2026 11:13:12 -0400 Subject: [PATCH 11/12] fix(adr): address round 2 review feedback on ADRs 0080/0081 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ADR 0080: correct the passthrough-syntax failure mode — env validation rejects `${VAR:-default}` at harness load (it treats the whole expression as a host variable name), rather than os.Expand silently resolving it to an empty string. Also fix the premise: env vars do support `${VAR}` host-variable expansion (ADR 0055), just not shell default-value syntax. - ADR 0080: state the override-convention paragraph as a decision ADR 0081 makes, not a pre-existing fact, and note it overrides ADR 0049's "CI workflow injection" item for behavior knobs specifically. - ADR 0081: tighten "other values sourced from CI-native inputs" to "other infrastructure values..." so it doesn't re-admit behavior knobs set from a repo variable. - ADR 0081: add Consequences follow-ups — the agents repo's own review.md still needs the same fix as fullsend's copy, and `CODE_ALLOWED_TARGET_BRANCHES` is still hardcoded in workflow env: blocks despite ADR 0053 already deciding it belongs in harness runner_env. Assisted-by: Claude Opus 4.6 Signed-off-by: Ralph Bean --- ...0080-config-yaml-vs-agent-env-var-scope.md | 23 +++++++++++-------- ...reserve-workflow-env-for-infra-plumbing.md | 18 +++++++++++---- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md b/docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md index c61488da91..5b943dc38e 100644 --- a/docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md +++ b/docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md @@ -66,18 +66,21 @@ instead: **Override convention:** `env.runner`/`env.sandbox` values are agent defaults. A per-repo or per-org override edits the harness (`base:` -composition, per ADR 0045), not the CI workflow `env:` block — that block -is reserved for infrastructure plumbing (credentials, project IDs, -regions), not agent behavior knobs (see [ADR 0081](0081-reserve-workflow-env-for-infra-plumbing.md) -for the full rule and its exceptions). This also means behavior defaults in +composition, per ADR 0045), not the CI workflow `env:` block. +[ADR 0081](0081-reserve-workflow-env-for-infra-plumbing.md) reserves that +block for infrastructure plumbing (credentials, project IDs, regions), +not agent behavior knobs — overriding ADR 0049's "CI workflow injection" +delivery mechanism for behavior knobs specifically; see that ADR for the +full rule and its exceptions. This also means behavior defaults in `env.runner`/`env.sandbox` must be literals (e.g. `TRIAGE_AUTO_CODE: "on"`), not shell-style passthrough expressions (e.g. -`${TRIAGE_AUTO_CODE:-on}`) — those two mechanisms deliver plain key-value -pairs, not shell-expanded strings, so passthrough syntax would be -mis-parsed — `os.Expand` treats the entire `VAR:-default` as the -variable name, which resolves to an empty string rather than applying -the intended default (see [ADR 0055](0055-unified-env-var-delivery.md), -§ Runner behavior). +`${TRIAGE_AUTO_CODE:-on}`) — `env.runner`/`env.sandbox` support `${VAR}` +host-variable expansion (see [ADR 0055](0055-unified-env-var-delivery.md), +§ Runner behavior), not shell default-value syntax, so passthrough syntax +is rejected at harness load: env validation treats `TRIAGE_AUTO_CODE:-on` +as a host variable name and fails with `host variable … is not set`; even +absent validation, `os.Expand` would resolve the whole reference to an +empty string, not the intended default. A knob only moves from one surface to the other by a deliberate migration, not by adding a second way to set the same value. Applying this rule to diff --git a/docs/ADRs/0081-reserve-workflow-env-for-infra-plumbing.md b/docs/ADRs/0081-reserve-workflow-env-for-infra-plumbing.md index ddda01fedd..9d49638c25 100644 --- a/docs/ADRs/0081-reserve-workflow-env-for-infra-plumbing.md +++ b/docs/ADRs/0081-reserve-workflow-env-for-infra-plumbing.md @@ -43,9 +43,9 @@ project IDs, regions) from agent behavior knobs. The CI workflow `env:` block (`.github/workflows/.yml`) is reserved for infrastructure plumbing — credentials, project IDs, -regions, and other values sourced from CI-native inputs (secrets, -org/repo variables). Agent behavior knobs, as scoped by ADR 0080 and -named per ADR 0049, are never set there. They go through harness +regions, and other infrastructure values sourced from CI-native inputs +(secrets, org/repo variables). Agent behavior knobs, as scoped by ADR +0080 and named per ADR 0049, are never set there. They go through harness composition instead: `env.runner`/`env.sandbox` defaults live in the canonical harness, and a per-repo or per-org override edits those defaults via `base:` composition (ADR 0045). @@ -65,7 +65,17 @@ static agent behavior defaults. - fullsend-ai/agents#567's docs need correcting: `TRIAGE_AUTO_CODE`'s override path is harness composition, not CI workflow `env:` — the - precedent it cited was itself non-conformant. + precedent it cited was itself non-conformant. The fullsend-ai/agents + repo's own `docs/review.md` carries the same non-conformant guidance + for `REVIEW_FINDING_SEVERITY_THRESHOLD` that this PR fixed in + `fullsend`'s copy, and needs the equivalent fix. +- `CODE_ALLOWED_TARGET_BRANCHES: ''` is still hardcoded in + `reusable-code.yml`/`reusable-dispatch.yml`'s workflow `env:` block, + even though [ADR 0053](0053-agent-driven-branch-targeting.md) already + decided this value belongs in the harness's `runner_env`, not the + workflow YAML. It's a pre-existing non-conformance this ADR's rule + makes explicit; removing it from the workflow files is a follow-up, + not part of this decision. - New agent behavior knobs get one documented override path (harness `base:` composition), removing the ambiguity between three candidate mechanisms. From 30c7d8021465d37673e24d1d677c402dcea46c89 Mon Sep 17 00:00:00 2001 From: Ralph Bean Date: Mon, 3 Aug 2026 12:20:32 -0400 Subject: [PATCH 12/12] docs(adr): explain AGENT_ prefix rationale for single-agent env vars maruiz93 asked why single-agent behavior tuning vars still need the {AGENT}_ prefix if they only live in that agent's own harness. ADR 0049 already answers this (shared .env sourcing and shared runner_env/ env.sandbox contexts can collide) but ADR 0080 didn't restate it, so the rationale wasn't self-contained. Assisted-by: Claude Opus 4.6 Signed-off-by: Ralph Bean --- docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md b/docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md index 5b943dc38e..6fa0d2dc38 100644 --- a/docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md +++ b/docs/ADRs/0080-config-yaml-vs-agent-env-var-scope.md @@ -59,7 +59,11 @@ instead: `create_issues.allow_targets` are existing examples. - **Single-agent behavior tuning — adjusts how one specific agent does its own job:** it is an `{AGENT}_`-prefixed env var per ADR 0049, delivered - via that agent's `env.runner`/`env.sandbox`. It is not also settable as + via that agent's `env.runner`/`env.sandbox`. The prefix matters even + though the var lives in one agent's harness: `.env` files can be sourced + together and `runner_env`/`env.sandbox` can share a host environment, so + the agent name scopes the var and prevents collisions in those shared + contexts (ADR 0049, Consequences). It is not also settable as a `config.yaml` field — overriding it per repo or org means overriding the harness (e.g. via `base:` composition, per ADR 0045), not adding a parallel field to `config.yaml`.