feat(agents): harness-agnostic effort write-side + spawn legacy bridge - #4625
Draft
wpfleger96 wants to merge 3 commits into
Draft
feat(agents): harness-agnostic effort write-side + spawn legacy bridge#4625wpfleger96 wants to merge 3 commits into
wpfleger96 wants to merge 3 commits into
Conversation
wpfleger96
force-pushed
the
duncan/effort-write-genericize
branch
from
August 4, 2026 01:36
2d8f97a to
e9e8f6a
Compare
wpfleger96
force-pushed
the
duncan/effort-write-genericize
branch
from
August 4, 2026 02:31
e9e8f6a to
8f57144
Compare
…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
force-pushed
the
duncan/effort-write-genericize
branch
3 times, most recently
from
August 4, 2026 05:13
b2c6e1f to
1c03211
Compare
…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
force-pushed
the
duncan/effort-write-genericize
branch
from
August 4, 2026 06:13
1c03211 to
a4bd585
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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/thought_leveloption whose raw value equalled the global default was reported as a live override instead of collapsing toGlobalDefault. Fixed: compare via normalized values after applyingeffort_norm.thought_levelACP identity. When boththought_levelandthinking_effortACP categories are present,thought_levelwins. The winning entry'sconfig_idis captured and forwarded towrite_via; it is always entry-supplied, never hardcoded.effort_normbefore candidate-list construction and B comparison. An invalid ACP value (e.g.minimalfor Goose) is omitted as a value candidate so a lower tier wins;write_viastaysAcpSetConfigOptionwith the entry-suppliedconfig_idbecause the route targets the option, not its current value.normalizeEffortValueinbuzzAgentConfig.tsapplies the same Goose alias table as the Rusteffort_norm:none/disabled→off,med→medium,xhigh→max, case-insensitive. Canonical values pass through; unrecognised values returnnull(buzz-agent path:acceptedValues=nullpasses through raw).Phase 2 — Canonical effort contract + native-key persistence + mutation contract
desktop/src/features/agents/Single read policy.
resolveEffortFromEnv(envVars, nativeKey, legacyKey, acceptedValues)inagentConfigCore.tsis 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'legacyEffortConsumedmemos all call it.Descriptor.
deriveAgentConfigFieldModelemits an effort field with:optionSource: "harnessNative"for runtimes with a staticacceptedEffortValuesvocabulary (Goose);"buzzAgentCatalog"for buzz-agent;"legacyProviderModelCatalog"otherwise.acceptedEffortValuesfrom runtime metadata — never the seven-value buzz-agent list.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), andEditAgentAdvancedFields(record/instance) all branch onoptionSource === "harnessNative". For harness-native runtimes they renderHarnessNativeEffortFieldswith runtime metadata;EffortSelectFieldreceives canonical choices fromacceptedEffortValues, not the Buzz catalog.HarnessNativeEffortFields. Accepts alegacyEnvKeyprop (passed only by the definition/instance editors, not global). Reads viaresolveEffortFromEnv. 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 andAgentConfigFields's onChange. Encodes the scope-split policy:legacyKey = BUZZ_AGENT_THINKING_EFFORTat record/persona;legacyKey = nullat global. Tests import and exercise it directly.Model-coupled mutation guard. Heal-on-mount, orphan-clear, and
useEffortAutoClearinAgentConfigFieldsskip the native effort key whenisHarnessNativeEffort. Goose effort is model-independent — its vocabulary is static; the reader's skip-as-absent contract handles invalid values non-destructively. TheuseEffortAutoClearhook receives""ascurrentEffortfor harness-native runtimes, making it a no-op. BUZZ_AGENT_THINKING_EFFORT deletions on those paths also remain guarded behind!isHarnessNativeEffort.Inherit-transition strip.
resolveInheritedRuntimeSubmissionacceptsexcludePersonaEnvKeysto strip effort keys from the persona merge layer on a pinned→inherit transition.AgentInstanceEditDialogpasses[thinkingEnvVar, ...ALL_KNOWN_EFFORT_KEYS]when the runtime hasthinkingEnvVar. Gated onthinkingEnvVar(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.
legacyConsumedKeycontrols 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.rsnow exposeseffort_tier_alias(value, accepted_values, global_tier)— the shared normalizer used bybuild_thinking_fieldandapply_effort_bridge. Applies the Goose alias table; returnsNonefor unrecognised values (skip-as-absent).apply_effort_bridge.managed_agents/spawn/bridges canonical effort onto the spawn payload:env_varson theAgentCommandConfig.launch_config.launch.env(the per-launch env overlay); top-levelenv_varsis left unbridged, preserving the stored record.effort_tier_alias.Deploy integration test.
agent_deploy_effort_bridge_test.rsasserts thatapply_effort_bridgewrites tolaunch.env, notenv_vars, and that foreign keys are stripped from both.Test coverage
thought_levelpriority, ACP alias/case/invalid normalization, alias resolver tiers, bridge descriptor hygiene, deploy parity boundary.resolveEffortFromEnvread policy (native-first, legacy fallback, tier boundary, null-legacyKey);applyHarnessNativeEffortChangescope-split mutation;deriveAgentConfigFieldModelat global/definition/instance scope;HarnessNativeEffortFieldslegacy migration;excludePersonaEnvKeysinherit-transition strip;isHarnessNativeEffortguard (xhigh survives model-heal; minimal survives; buzz-agent auto-clear unchanged); inherited-defaults normalization; global coexisting native-pair preservation.