Skip to content

feat(agents): harness-agnostic effort write-side + spawn legacy bridge - #4625

Draft
wpfleger96 wants to merge 3 commits into
mainfrom
duncan/effort-write-genericize
Draft

feat(agents): harness-agnostic effort write-side + spawn legacy bridge#4625
wpfleger96 wants to merge 3 commits into
mainfrom
duncan/effort-write-genericize

Conversation

@wpfleger96

@wpfleger96 wpfleger96 commented Aug 3, 2026

Copy link
Copy Markdown
Member

Summary

Genericizes the agent effort write-side so non-Buzz runtimes (starting with Goose) participate in the same canonical effort contract as buzz-agent, with a spawn bridge that translates the canonical key to whatever the target harness expects at launch time.

Three commits on top of main (631b05c88).


Phase 1 — B-collapse + ACP identity + alias normalization

desktop/src-tauri/src/managed_agents/config_bridge/

  • B-collapse for same-value live effort. Previously a live thought_level option whose raw value equalled the global default was reported as a live override instead of collapsing to GlobalDefault. Fixed: compare via normalized values after applying effort_norm.
  • thought_level ACP identity. When both thought_level and thinking_effort ACP categories are present, thought_level wins. The winning entry's config_id is captured and forwarded to write_via; it is always entry-supplied, never hardcoded.
  • ACP effort normalization. The selected ACP option's value is normalized through effort_norm before candidate-list construction and B comparison. An invalid ACP value (e.g. minimal for Goose) is omitted as a value candidate so a lower tier wins; write_via stays AcpSetConfigOption with the entry-supplied config_id because the route targets the option, not its current value.
  • Alias normalization (TS). normalizeEffortValue in buzzAgentConfig.ts applies the same Goose alias table as the Rust effort_norm: none/disabledoff, medmedium, xhighmax, case-insensitive. Canonical values pass through; unrecognised values return null (buzz-agent path: acceptedValues=null passes through raw).

Phase 2 — Canonical effort contract + native-key persistence + mutation contract

desktop/src/features/agents/

Single read policy. resolveEffortFromEnv(envVars, nativeKey, legacyKey, acceptedValues) in agentConfigCore.ts is the authoritative read path for all effort surfaces. It normalizes, applies native-first / valid-legacy-fallback, and returns { value, legacyConsumed }. deriveAgentConfigFieldModel, HarnessNativeEffortFields, and both editors' legacyEffortConsumed memos all call it.

Descriptor. deriveAgentConfigFieldModel emits an effort field with:

  • optionSource: "harnessNative" for runtimes with a static acceptedEffortValues vocabulary (Goose); "buzzAgentCatalog" for buzz-agent; "legacyProviderModelCatalog" otherwise.
  • acceptedEffortValues from runtime metadata — never the seven-value buzz-agent list.
  • Legacy fallback (legacyKey = BUZZ_AGENT_THINKING_EFFORT) at record/persona scope only; omitted at global/onboarding (Delta 5: that key is buzz-agent's own native key there).

UI surfaces. AgentConfigFields (global/onboarding), PersonaAdvancedFields (persona/definition), and EditAgentAdvancedFields (record/instance) all branch on optionSource === "harnessNative". For harness-native runtimes they render HarnessNativeEffortFields with runtime metadata; EffortSelectField receives canonical choices from acceptedEffortValues, not the Buzz catalog.

HarnessNativeEffortFields. Accepts a legacyEnvKey prop (passed only by the definition/instance editors, not global). Reads via resolveEffortFromEnv. Save = canonical native write + atomic legacy delete at record/persona scope; native-key-only at global scope (preserves coexisting foreign native keys — Delta 5).

applyHarnessNativeEffortChange. Exported production mutation helper called by both the component and AgentConfigFields's onChange. Encodes the scope-split policy: legacyKey = BUZZ_AGENT_THINKING_EFFORT at record/persona; legacyKey = null at global. Tests import and exercise it directly.

Model-coupled mutation guard. Heal-on-mount, orphan-clear, and useEffortAutoClear in AgentConfigFields skip the native effort key when isHarnessNativeEffort. Goose effort is model-independent — its vocabulary is static; the reader's skip-as-absent contract handles invalid values non-destructively. The useEffortAutoClear hook receives "" as currentEffort for harness-native runtimes, making it a no-op. BUZZ_AGENT_THINKING_EFFORT deletions on those paths also remain guarded behind !isHarnessNativeEffort.

Inherit-transition strip. resolveInheritedRuntimeSubmission accepts excludePersonaEnvKeys to strip effort keys from the persona merge layer on a pinned→inherit transition. AgentInstanceEditDialog passes [thinkingEnvVar, ...ALL_KNOWN_EFFORT_KEYS] when the runtime has thinkingEnvVar. Gated on thinkingEnvVar (not Goose-specific) — applies to all runtimes with a native effort key, including buzz-agent; spawn still inherits persona effort at the persona tier.

Legacy row visibility. legacyConsumedKey controls advanced-row suppression. The legacy row is hidden only when the key was validly consumed; invalid or unrecognised legacy values remain visible as advanced env rows.


Phase 3 — Shared alias resolver + spawn effort bridge + deploy test

desktop/src-tauri/

effort_tier_alias. runtime_metadata.rs now exposes effort_tier_alias(value, accepted_values, global_tier) — the shared normalizer used by build_thinking_field and apply_effort_bridge. Applies the Goose alias table; returns None for unrecognised values (skip-as-absent).

apply_effort_bridge. managed_agents/spawn/ bridges canonical effort onto the spawn payload:

  • Strips all known foreign effort keys from the env before inserting the resolved winner.
  • For local agents: writes to env_vars on the AgentCommandConfig.
  • For deploy/cloud agents: writes to launch_config.launch.env (the per-launch env overlay); top-level env_vars is left unbridged, preserving the stored record.
  • Resolves the canonical winner from all tiers (record native → record legacy → persona native → persona legacy → global native), normalized through effort_tier_alias.

Deploy integration test. agent_deploy_effort_bridge_test.rs asserts that apply_effort_bridge writes to launch.env, not env_vars, and that foreign keys are stripped from both.


Test coverage

  • Rust: ACP B-collapse, thought_level priority, ACP alias/case/invalid normalization, alias resolver tiers, bridge descriptor hygiene, deploy parity boundary.
  • TS: resolveEffortFromEnv read policy (native-first, legacy fallback, tier boundary, null-legacyKey); applyHarnessNativeEffortChange scope-split mutation; deriveAgentConfigFieldModel at global/definition/instance scope; HarnessNativeEffortFields legacy migration; excludePersonaEnvKeys inherit-transition strip; isHarnessNativeEffort guard (xhigh survives model-heal; minimal survives; buzz-agent auto-clear unchanged); inherited-defaults normalization; global coexisting native-pair preservation.

@wpfleger96
wpfleger96 force-pushed the duncan/effort-write-genericize branch from 2d8f97a to e9e8f6a Compare August 4, 2026 01:36
@wpfleger96 wpfleger96 changed the title feat(config-bridge): persist Goose effort under native key + spawn legacy bridge feat(agents): harness-agnostic effort write-side + spawn legacy bridge Aug 4, 2026
@wpfleger96
wpfleger96 force-pushed the duncan/effort-write-genericize branch from e9e8f6a to 8f57144 Compare August 4, 2026 02:31
…entity + alias normalization (Phase 1)

- EffortNormalization struct in runtime_metadata.rs with canonical values,
  aliases, and normalize_str(); GOOSE_EFFORT_NORMALIZATION static cited to
  goose thinking.rs:277-308 (canonical: off/low/medium/high/max; aliases:
  none|disabled→off, med→medium, xhigh→max)
- effort_normalization field on KnownAcpRuntime; accepted_effort_values on
  AcpRuntimeCatalogEntry (serialized to TS as the single canonical authority)
- LEGACY_THINKING_EFFORT_KEY and ALL_KNOWN_EFFORT_KEYS constants in
  config_bridge/mod.rs; effort_tier_alias() for per-tier native+legacy key
  resolution with normalization and global-tier exclusion
- find_effort_config_option() identifies live effort by ACP category
  thought_level (Goose/claude-agent-acp); falls back to legacy effort
  category for transition compatibility; thought_level always wins when both
  present; returns (value, config_id) so write-route uses the real entry id
- build_thinking_field() normalizes all candidates before precedence,
  override tracking, and B-equality; B-collapse compares normalized ACP
  value against normalized non-ACP winner — none↔off and xhigh↔max collapse
  correctly; write_via uses actual matched config_id (not hardcoded effort)
- Test hygiene: test_runtime()/buzz_agent_runtime() use catalog lookups;
  6 goose_runtime() tests wrapped with with_no_goose_config(); 4 readiness
  stubs gain effort_normalization: None
- 6 new tests in reader_tests_ext.rs pin real Goose ACP shape, both-present
  priority, legacy fallback, alias B-collapse (none↔off, xhigh↔max), and
  env alias normalization

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96
wpfleger96 force-pushed the duncan/effort-write-genericize branch 3 times, most recently from b2c6e1f to 1c03211 Compare August 4, 2026 05:13
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 and others added 2 commits August 4, 2026 02:06
…tation contract (Phase 2)

- acceptedEffortValues on AcpRuntimeCatalogEntry: Goose carries canonical
  off|low|medium|high|max (source: goose thinking.rs:277-308); null for
  buzz-agent (per-model catalog). Single authority for UI, spawn bridge,
  and reader.
- normalizeEffortValue() in buzzAgentConfig.ts: aliases none|disabled→off,
  med→medium, xhigh→max (case-insensitive); null acceptedValues = pass-through
  (buzz-agent path unchanged).
- ALL_KNOWN_EFFORT_KEYS in buzzAgentConfig.ts mirrors Rust constant; used for
  record/persona runtime-transition cleanup.
- agentConfigCore.ts: native key wins; legacy-only pre-migration save shown via
  effortValue fallback; legacyConsumedKey suppresses duplicate advanced row.
  optionSource harnessNative when acceptedEffortValues present.
- AgentConfigFields.tsx: descriptor-driven global/onboarding path for
  harnessNative runtimes (isHarnessNativeEffort): canonical options from
  acceptedEffortValues, currentEffort from normalised descriptor value,
  effortValidForAutoClear from acceptedEffortValues. Unconditional
  BUZZ_AGENT_THINKING_EFFORT deletes guarded by !isHarnessNativeEffort (Delta 5:
  global-scope coexistence). allStructuredKeys includes legacyConsumedKey;
  effort clear deletes native + legacy; bakedStructuredKeys() replaces static set.
- buzzAgentModelTuningFields.tsx: applyHarnessNativeEffortChange() exported
  production helper — single spec for the mutation contract. legacyKey=null at
  global/onboarding scope (Delta 5), legacyKey=BUZZ_AGENT_THINKING_EFFORT at
  record/persona scope. EffortSelectField gains canonicalValues prop to bypass
  Buzz 7-value catalog. HarnessNativeEffortFields calls production helper.
- personaRuntimeModel.ts: resolveInheritedRuntimeSubmission gains
  excludePersonaEnvKeys param. Strips harness-native effort keys from persona
  layer on inherit-transition so the record never acquires a frozen copy of
  persona effort (effort inherits at spawn; it is not a credential).
- AgentInstanceEditDialog.tsx: passes excludePersonaEnvKeys with runtime's
  thinkingEnvVar + ALL_KNOWN_EFFORT_KEYS when runtime has acceptedEffortValues.
- bakedEnvHelpers.ts: getInheritedAgentDefaults accepts nativeEffortKey option;
  baked lookup is native-only for non-buzz-agent runtimes (Delta-4 boundary).
  bakedStructuredKeys() exported for AgentConfigFields.
- useAgentDialogDefaults.ts: accepts nativeEffortKey; seeds inherited env under
  native key.
- AgentDefinitionDialog + AgentInstanceEditDialog: pass prev/next native effort
  keys to selectionOnRuntimeChange; handleInheritHarnessChange clears all known
  effort keys on pin↔inherit toggle.
- runtimeModelProviderSelection.ts: selectionOnRuntimeChange clears prev native
  + next native + legacy at record/persona scope; skip when native === legacy
  (buzz-agent same-key invariant).
- agentConfigOptions.tsx: resetConfigForHarnessChange preserves all effort keys
  (global-scope Delta-5 rule; each runtime's descriptor strips foreign keys).
- agent_config.rs: is_safe_to_reveal derives allowed effort keys from
  ALL_KNOWN_EFFORT_KEYS so GOOSE_THINKING_EFFORT is unmasked at baked tier.
- AGENTS.md rule-2 'until PR 2.7' clause removed: native-key persistence lands
  in this commit.
- Test coverage: applyHarnessNativeEffortChange record/persona + global scope
  pins; resolveInheritedRuntimeSubmission inherit-no-materialize (native +
  legacy), concrete-override-wins, non-effort-merged, full-submit-payload-clean;
  global Goose descriptor optionSource/value/invalid/no-legacy pins;
  runtimeModelProviderSelection useLiteralKeys cleanup.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…st (Phase 3)

- apply_effort_bridge() in config_bridge/mod.rs: applied after merged user env
  in resolve_effective_agent_env_with_def. Strips all foreign known effort keys
  (runtime-scoped invariant), then resolves canonical effort via tier-first
  precedence (record native → record legacy → persona native → persona legacy
  → global native → definition native; global legacy excluded end-to-end).
  Removes raw native key (possibly alias-form) from env before inserting
  canonical winner — skip-as-absent applies to the output map, not just winner
  selection.
- effort_tier_alias() with global_tier=true: honours native key only at global
  tier; legacy alias excluded there (plan v3 Delta 2).
- readiness_effort_bridge_tests.rs: 12 spawn tests covering bridge activation,
  tier-first precedence (record beats persona, record legacy beats persona
  native, global legacy excluded), alias normalization (none→off, xhigh→max),
  invalid-skip-as-absent (minimal skipped, persona native wins), and the
  bidirectional global coexist invariant (Goose and buzz-agent from same global
  config each receive only their own native key; GOOSE_THINKING_EFFORT stripped
  from buzz-agent descriptor, BUZZ_AGENT_THINKING_EFFORT stripped from Goose).
- agents_deploy.rs: deploy parity test pins that a legacy-only Goose record
  produces GOOSE_THINKING_EFFORT in launch.env while top-level env_vars retains
  the legacy key as compatibility bookkeeping (Delta-4 contract boundary).

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96
wpfleger96 force-pushed the duncan/effort-write-genericize branch from 1c03211 to a4bd585 Compare August 4, 2026 06:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant