feat(#6581): per-agent runtime, model and effort on agents: entries - #6583
Conversation
|
🤖 Review · Commit: |
Site previewPreview: https://b680cf26-site.fullsend-ai.workers.dev Commit: |
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
5ff4f6d to
b0d20f7
Compare
|
🤖 Review · Commit: |
b0d20f7 to
aeb4428
Compare
|
🤖 Finished Review · ✅ Success · Started 3:48 AM UTC · Completed 4:07 AM UTC Commit: Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $10.01 |
ReviewFindingsMedium
Low
Labels: PR modifies harness validation, runtime resolution, CI workflow, install setup, and documentation Previous runReviewFindingsMedium
Low
Previous run (2)ReviewFindingsMedium
Low
Next steps:
Previous run (3)ReviewFindingsLow
Previous run (4)ReviewFindingsLow
Previous run (5)ReviewFindingsLow
Previous run (6)ReviewFindingsLow
Previous run (7)ReviewFindingsMedium
Low
|
aeb4428 to
bac1f51
Compare
|
🤖 Finished Review · ✅ Success · Started 4:11 AM UTC · Completed 4:27 AM UTC Commit: Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $11.99 |
|
🤖 Review · Commit: |
|
🤖 Finished Review · ✅ Success · Started 3:35 PM UTC · Completed 3:55 PM UTC Commit: Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $14.26 |
Superseded by updated review
An agents: entry in the per-repo config.yaml can now set runtime, model and effort for one agent, in the place that already says which harness runs. A built-in agent (triage, code, review, fix, retro, prioritize) is tuned with a name-only entry — an enabled entry without source, valid only for a built-in name — and a custom agent carries the settings on its source: entry. `fullsend agent set <name> --runtime/--model/--effort` writes them. This replaces the out-of-band repository variables (TRIAGE_FULLSEND_MODEL, CODE_FULLSEND_RUNTIME, ...) with something reviewable, and makes runtime a per-agent choice; the repo-wide runtime: stays as the default for agents that set none. Names are agent names as passed to `fullsend run <agent>` — not harness role: values, since code and fix both carry role: coder — matched case-insensitively. Precedence: flag > env > the agent's entry > repo-wide config / harness default. Entries merge per field across the layered config (empty inherits, no tombstones). The source is surfaced as "<config path> agents.<name>" in the plan block, stderr and metrics.json. `fullsend run` loads one layered per-repo config (config.yaml over config.base.yaml, ADR 0069; base-only and nested .fullsend/ layouts included) for runtime selection and the model/effort application, and — because it never calls Validate() on the config it loads — validates the effective agents: list itself on every run: a mistyped entry fails the run for every agent, in the overlay or in config.base.yaml, and a stub runtime cannot be activated through an entry any more than through runtime:. Override-only entries register no harness: RegisteredAgents skips them (not a custom harness to enumerate, lock or list), the built-in keeps resolving through the agents-repo fallback, and the reusable workflow's config.yaml guard accepts them. The effective entries are exposed to overlay CEL as config.agents. Also relaxes the harness validModelName regex to accept provider/id format (e.g. xai-vertex/xai/grok-4.6) via a shared config.ValidModelRef function (#6570); the effort level list moves to config.ValidEffortLevels (harness imports config). A runtime: claude entry paired with a provider/id model gets a plan-block warning rather than a rejection. The first cut of this change used a separate role_overrides: map; it was folded onto agents: entries before merge — config.yaml already had a per-agent list keyed by the same names with the same layered merge. ADR 0091 records both. models.aliases from #6529 is deferred; #6577 tracks the alias bug. Closes #6581 Closes #6570 Closes #6529 Assisted-by: Claude Signed-off-by: Wayne Sun <gsun@redhat.com>
…ings Add features/runtime/agent-settings.feature. Scenario 1 (every run, dummy cost): the leased repo's runtime: is flipped to claude and agents: entries pin triage (and code, which triage hands off to via ready-to-code) back to dummy, so a passing run proves the per-agent entry beat the repo-wide key without inference. The new assertion `the run selected the "<runtime>" runtime from "<source>"` also checks metrics.json runtime_source ends with agents.triage — which config entry decided, not just which runtime ran. Scenario 2 (gated @requires:capability:runtime-pi): the repo stays on dummy and one custom agent is put on pi with `model: haiku` through its agents: entry while its harness says `model: opus`. `the run requested model "<m>" from "<source>" and the provider reported a "<family>" model` checks requested_model, override_source, the provider-reported model and num_turns > 0 in metrics.json, so both the runtime and the model of one agent demonstrably came from config.yaml. Existing pi transcript/token assertions complete it. New step `the repository agents are configured with:` takes a YAML mapping of agent name -> {runtime, model, effort}, sets only the given settings on the existing entry (a name-only entry for a built-in) — validated the way `fullsend run` validates them — and CleanupScenario restores the agents: list. Both pi scenarios (this one and the pre-existing pi.feature, which had never run in CI) need what the fleet harnesses carry and the behaviour custom harnesses did not: the vertex-ai provider/profile that grants egress (ADR-0065; the custom-harness step now commits the real scaffold files, since a per-repo install ships only .gitkeeps for those dirs) and the Vertex project env plus credential host files inlined into the sandbox. `make behaviour-test` declares BEHAVIOUR_CAPABILITIES=runtime-pi by default (a Makefile variable, because E2E Tests runs on pull_request_target and takes its workflow file from main). Assisted-by: Claude .codecov.yml: the per-file ignore list for behaviour-test code is replaced by pkg/behaviourtest/** and e2e/behaviour/** — integration-only code exercised by the behaviour/e2e jobs, not by the unit upload. Signed-off-by: Wayne Sun <gsun@redhat.com>
`fullsend github setup <owner/repo>` regenerated .fullsend/config.yaml from flags on every run, so a re-run (for example after a fullsend upgrade) would have dropped agents: entries and their per-agent settings, allowlist edits and hand-written comments, and its runtime prompt could flip a pi repo back to claude on Enter. Now an existing per-repo config.yaml is left out of the scaffold files entirely — kept verbatim, comments included — unless a flag that targets a config key is passed (--runtime, --agents, --mint-url, --inference-*), in which case only that key is changed on the loaded file. The runtime prompt runs only on a first install; the setup PR body reports the runtime the file already selects and points at agents: entries / `fullsend agent set`. --config rewrites config.base.yaml and keeps the existing overlay. A config.yaml that no longer parses fails the re-run instead of being regenerated; dry runs without repo credentials plan as a first install with a warning. Scope: per-repo GitHub setup. The converge full-rescaffold repair and the GitLab setup path keep their previous behaviour. docs/cli/github.md documents the re-run semantics. Assisted-by: Claude Signed-off-by: Wayne Sun <gsun@redhat.com>
8ade5de to
245adea
Compare
|
Re-review on |
|
🤖 Review · ❌ Terminated · Started 4:00 PM UTC · Ended 4:19 PM UTC Commit: |
|
@ggallen — local verification matrix for the folded shape, in case it helps the review. All runs use a from-source build of this branch on a Mac (podman + openshell), a per-repo fullsend dir carrying the fleet harnesses locally (
CI on the same shape (behaviour job, real pool repos): Two things the matrix surfaced that are in the PR rather than around it: |
|
🤖 Finished Review · ✅ Success · Started 4:00 PM UTC · Completed 4:19 PM UTC Commit: Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $16.63 |
ggallen
left a comment
There was a problem hiding this comment.
The refactored design is clean — putting model/runtime/effort on agents: entries instead of a separate role_overrides key is the right call. Reviewed the config schema (AgentEntry changes, validation, keyed merge across layers), the run/runtime path (precedence chain, source labels, stub runtime rejection), the agent CLI (agent set/agent remove), the GitHub setup re-run preservation, the workflow yq guard, and the ADR. No bugs found. Test coverage is thorough across all paths.
|
🤖 Finished Retro · ✅ Success · Started 5:38 PM UTC · Completed 5:52 PM UTC Commit: Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $4.59 |
Retro: PR #6583 — per-agent runtime, model and effort on
|
Summary
Lets
.fullsend/config.yamlsay, per agent, how it runs —runtime,model,effort— in the place that already says which harness runs: theagents:entry. A built-in agent is tuned with a name-only entry; a custom agent carries the settings on itssource:entry. This replaces the out-of-band repository variables (TRIAGE_FULLSEND_MODEL,CODE_FULLSEND_RUNTIME, ...) with something reviewable, and makes runtime a per-agent choice (the repo-wideruntime:stays as the default for agents that set none).Or:
fullsend agent set code --runtime claude --model sonnet --effort high.Names are agent names as passed to
fullsend run <agent>(and used by the workflow stages / theCODE_-style variable prefixes) — not harnessrole:values, sincecodeandfixboth carryrole: coder. Case-insensitive. A name-only entry that is not a built-in fails validation (codergets a "did you meancode" hint).Folds in the scope of two sibling issues so they ship together:
validModelNameis replaced by a shared, segment-basedconfig.ValidModelRef(^seg(/seg)*$), soprovider/idids such asxai-vertex/xai/grok-4.6validate in harness files and onagents:entries;/leading,trailing/,a//bare rejected.models:/effort:to.fullsend/config.yamlwithmodels.aliasesremap #6529 — per-agentmodel+effort(and, beyond its original scope,runtime).models.aliasesis deferred: provider-qualified per-agent models cover the need it was raised for, and pi: alias 'fable' resolves to a non-existent model id and is silently substituted #6577 tracks the alias bug separately (recorded in ADR 0091).Changes
internal/config:AgentEntrygainsruntime/model/effort(sourcebecomes optional);IsOverrideOnly,HasSettings,AgentSettingsFor,UpsertAgentSettings;ValidateAgentEntriesvalidates the settings and the override-only rule (built-in names only, with thecoderhint; duplicates case-insensitive); the keyed layered merge carries the three fields per field (empty inherits — no tombstones);Validate()checks the merged list so an overlay entry tuning a base-registered custom agent is valid; sharedValidModelRef,ValidAgentNames,ValidEffortLevels/ValidEffort(effort validators move here fromharness, which importsconfig);OverlayConfigFile/BaseConfigFileconstants.internal/harness:model:validated withconfig.ValidModelRef;RegisteredAgentsskips override-only entries (a built-in tuned by name is not a custom harness to enumerate, lock or list);BuildConfigMapexposes the fields onconfig.agentsfor overlay CEL.internal/runtime:ResolveForAgent(agents, repoRuntime, agent)— the entry's runtime when set, else the repo-wide key, both validated againstValidRuntimes(stub runtimes cannot be activated through an entry either).internal/cli/run.go: the config is loaded once per run (loadRunConfig, layeredconfig.yamloverconfig.base.yaml, base-only directories and the nested.fullsend/layout included) and feeds runtime selection, theFULLSEND_PI_MODELgate and the model/effort application. Becausefullsend runnever callsValidate()on the config it loads,agentSettingsrunsValidateAgentEntrieson the effective list itself — a mistyped entry fails the run for every agent, in the overlay or inconfig.base.yaml— and applies model/effort beneath the flag/env overrides. Org configs'agents:entries are honoured too.runtime: claude+ aprovider/idmodel gets a plan-block warning.defaultAgentsRepoKnownAgentsderives fromconfig.ValidAgentNames().internal/cli/agent.go: newfullsend agent set <name> [--runtime] [--model] [--effort](per-repo; upsert onto the existing entry, name-only entry for built-ins, overlay entry for a base-registered agent; only given flags change,""clears; validated before write);agent listshows settings and(built-in);agent removedrops the entry and its settings.internal/cli/github.go(setup re-run, per-repo GitHub only):fullsend github setup <owner/repo>re-run used to regenerate.fullsend/config.yamlfrom flags and would have droppedagents:entries, allowlist edits and comments — and its runtime prompt could flip a pi repo back to claude on Enter. Now an existing per-repoconfig.yamlis kept out of the scaffold entirely (verbatim) unless a config-targeting flag is passed (--runtime,--agents,--mint-url,--inference-*), in which case only that key is changed on the loaded file (loaded layered —config.base.yamlis fetched too, so an overlay entry tuning a base-registered custom agent validates;config.ParsePerRepoConfigWriterLayeredparses both layers from raw bytes); the runtime prompt is skipped on re-run; the setup PR reports the runtime the file already selects and points atagents:entries /agent set;--configrewritesconfig.base.yamland keeps the existing overlay; an unparsableconfig.yamlfails the re-run; dry-run without repo credentials plans as a first install with a warning. Scope: the converge full-rescaffold repair and the GitLab setup path keep their previous behaviour..github/workflows/reusable-dispatch.yml: theconfig.yamlyqguard allows an enabledagents:entry withoutsourcewhen it setsruntime/model/effort(the message says so). A repo must bump its pin before adding such an entry — an older pinned workflow rejects it — documented indocs/runtimes.md.runtime: selected ...andmetrics.json(runtime_source,override_source) name the source as<config path> agents.<name>.e2e/behaviour/features/runtime/agent-settings.feature): (1) every run at dummy cost — repo-wideruntime: claude, entries pintriage(andcode, which triage hands off to) todummy; assertsthe run selected the "dummy" runtime from "agents.triage"(runtimeandruntime_source). (2) gated@requires:capability:runtime-pi— repo stays ondummy; a custom agentpi-override(harnessmodel: opus) is put on pi withmodel: haikuthrough its entry; assertsselected "pi" runtime from "agents.pi-override",requested model "haiku" from "agents.pi-override"with a provider-reportedhaikumodel andnum_turns > 0, a pi tool call in the transcript, and tokens. New stepthe repository agents are configured with:(only the settings given change;CleanupScenariorestores theagents:list) plus the model assertion step, with unit tests inpkg/behaviourtest/steps.make behaviour-testdeclaresBEHAVIOUR_CAPABILITIES=runtime-piby default (a Makefile variable rather than workflow env, becauseE2E Testsruns onpull_request_targetand takes its workflow file frommain);BEHAVIOUR_CAPABILITIES= make behaviour-testskips them. This also un-skips the existingpi.feature, which had never run in CI and was broken as written twice over: the custom harnesses carried neither the Vertex project env ([pi-anthropic-vertex] disabled: set GOOGLE_CLOUD_PROJECT or ANTHROPIC_VERTEX_PROJECT_ID→ everyanthropic-vertex/...id "not found") nor thevertex-aiprovider/profile that grants egress since ADR-0065 (policy_denied). Both pi scenarios now declareprofiles/providerslike the fleet harnesses (the custom-harness step commits the real scaffold files; the per-repo install ships only.gitkeeps for those dirs — also a real gap for BYOA harnesses on per-repo installs) and inline whatcommon/env/gcp-vertex.env+ the credentialhost_filesdo. On pi,haikuresolves toanthropic-vertex/claude-haiku-4-5(pi-ai's catalog has it since 0.84.1; the extension registers that catalog verbatim and calls Vertex via@anthropic-ai/vertex-sdk, which mapsregion=globalto the global endpoint)..codecov.yml: the per-file ignore list for behaviour-test code is replaced bypkg/behaviourtest/**ande2e/behaviour/**.docs/runtimes.md(precedence diagram + table, per-agent section, migration note),docs/cli/run.md,docs/cli/agent.md(agent set),docs/cli/github.md(re-run semantics),choosing-a-runtime.md,docs/runtimes/pi.md+runtime-implementation.md,layered-config-reference.md(merge rule),architecture.md,behaviour-testing.md, ADR 0091 (0091-per-agent-runtime-model-effort.md; records the rejectedrole_overridesshape, how it narrows ADR 0045, the deliberate non-removal of repo-wideruntime:, and why Add per-rolemodels:/effort:to.fullsend/config.yamlwithmodels.aliasesremap #6529/Allow forward slash in model identifiers to support provider/model format #6570 are folded in).Backward compatibility / user impact (audited)
agents:lists is unchanged for sourced/disabled entries;Marshalomits empty fields (and now omits an emptysource:on disable-only entries); org-mode configs untouched.source(theyqguard), so a repo must bump its workflow pin before adding a name-only entry — documented.config.yaml), soconfig.base.yamlentries work;setup-agent-env.shrepository variables still win over config;action.ymlpasses no runtime/model flags, so config is authoritative absent variables. Runtimes receive--model/--effort(Claude Code) or--model/--thinking(pi) from the resolved harness and never read config themselves.repos status/converge skipconfig.yamlfor drift by design;repos migratecarries config across explicitly.Precedence
Testing
internal/config: parse/validate/round-trip for entries with settings (override-only built-ins, sourced custom agents,coderhint, unknown custom without source, invalid model/runtime/effort, case-insensitive duplicates), layered per-field merge incl. an overlay entry tuning a base-registered agent and a bad entry in the base,UpsertAgentSettings,ValidModelRef,ValidEffort.internal/harness:provider/idaccepted, malformed forms rejected;RegisteredAgentsskips override-only entries;BuildConfigMapexposes the fields.internal/runtime:ResolveForAgentprecedence and stub-runtime rejection.internal/cli: per-agent runtime viaresolveBackend(flag still wins, source label, org configs), stub runtime rejected at run time, layered/base-only/nested config discovery, bad entries rejected on the run path (overlay, base with overlay, base only, bad value),applyAgentSettings,agent set(built-in, custom, base-registered, validation),agent remove, setup re-run (flag-less re-run leavesconfig.yamlout;--runtimechanges only that key and keepsagents:entries; unparsable config fails;--configkeeps the overlay).go test ./internal/... ./pkg/behaviourtest/...green locally (the twoTestDummyRuntime_*failures ininternal/runtimereproduce identically onorigin/mainon this host);make lintclean; both feature files parse.triagerun on pi took model/effort from its entry (from-source build on a Mac), and in CI both behaviour scenarios passed on the previous shape (d548303: 24/24 scenarios, real Vertex Haiku reply with tool calls and tokens); this head re-runs them on the folded shape.Live target: fullsend-ai/pi-xai-vertex#1 now carries exactly this configuration as
agents:entries (six built-in agents: Grok on pi for triage/review, Gemini on pi for prioritize, Claude Code Sonnet for code/fix/retro) pinned to fullsend@main, replacing its nine role-prefixed repository variables; it merges after this PR lands.Closes #6581
Closes #6570
Closes #6529