Skip to content

feat(agents): harness-agnostic effort write path and spawn bridge - #4625

Merged
wpfleger96 merged 40 commits into
mainfrom
duncan/effort-write-genericize
Sep 2, 2026
Merged

feat(agents): harness-agnostic effort write path and spawn bridge#4625
wpfleger96 merged 40 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 Goose participates in the same canonical effort contract as buzz-agent. A spawn bridge translates the canonical key to whatever the target harness expects at launch time. Read/write/spawn paths all derive their vocabulary from runtime metadata rather than a hardcoded buzz-agent list.

What changed

Rust — config bridge + spawn path

  • apply_spawn_effort_env in effort.rs: production command-boundary seam — writes baked env, runs the effort projection, strips per-runtime suppress set, and emits exactly one projected key.
  • apply_effort_to_spawn_command in runtime.rs: thin wrapper returning a #[must_use] EffortApplied(()) token (private field — unforgeable outside the function). spawn_agent_child calls it as let effort = apply_effort_to_spawn_command(...) and passes effort to spawn_with_effort_proof. Deleting the call is a compile error: effort is undefined at the spawn_with_effort_proof site. Deleting apply_spawn_effort_env inside the wrapper turns the production-sequence tests RED.
  • apply_record_field_updates in agent_models_update.rs: returns Result<RecordFieldsApplied, String> (#[must_use] token). update_managed_agent calls it as let applied = apply_record_field_updates(...)? then passes applied to stamp_record_updated_at. Deleting the call is a compile error: applied is undefined at the stamp_record_updated_at site.
  • Unknown/custom-runtime passthrough: apply_effort_launch_to_command skips the suppress loop when preserve_passthrough && value.is_none(), preserving ambient ACP sentinels.
  • EnvVarGuard: prior value stored as OsString (var_os) so non-Unicode values are restored exactly on Drop. A single PROCESS_ENV_MUTEX in managed_agents/mod.rs is shared by lock_path_mutex() and lock_env_mutex() — any two tests calling either helper are mutually exclusive with each other. Tests in other modules (app_state_tests, agent_config_tests, reader_tests) maintain their own independent locks and are not in this domain.
  • Dead-code: strip_effort_keys_from_command marked #[cfg(test)]; import path in effort_cmd_tests.rs fixed.
  • Windows CI fix: platform-gated variants for inherited-env tests.

TypeScript — renderer + model cleanup

  • AgentConfigFields orphan-model cleanup effect: the isHarnessNativeEffort early-return was skipping the model clear on provider→Custom transitions. Refined to: return early only when model is already null; clear model once while preserving the harness-native effort key (Carl P2).
  • Provider-empty convergence: when model is null and effort is native, the cleanup effect returns early (nothing to clear) — prevents spurious onConfigChange loop.
  • EffortSelectField / humanizeEffortLabel: runtime-native option labels title-cased (offOff) with raw canonical values preserved for round-trip fidelity.
  • AgentConfigFields: drives effort renderer from selectedRuntime.effortCanonicalValues (harness-native path) or the model/provider catalog (buzz-agent/provider path), selected by isHarnessNativeEffort.

Docs

  • desktop/src/features/agents/AGENTS.md item 14: updated from deleted persistAgentEffortLevel direct-write contract to the shipped Save-gated update_managed_agent.effortLevel path. Consistent with EffortPickerField's own doc comment.

Tests

  • agent_models_update_tests.rs: seam tests via apply_record_field_updates — non-local rejects, local set/clear, ordering invariant, ACP-sentinel sweep. record_field_updates_persist_effort_to_disk (renamed from the prior false-claim name) drives load→apply→stamp→save→load via a mock AppHandle + tempdir, asserting effort_level persists to disk. Manual HOME/XDG restore replaced with RAII EnvVarGuard (panic-safe, OsString-exact).
  • effort_cmd_tests.rs / effort_tests.rs: production-sequence seam tests via apply_effort_to_spawn_command. Spawns /usr/bin/env to verify child's real env. EnvVarGuard for panic-safe restore. Windows twin using cmd /c set.
  • effortAutoClear.test.mjs: five mounted stateful journeys via AgentConfigFields with useCustomSelect=true. Covers: custom trigger shows "Off" at mount; provider-empty mount is a stable fixed point; provider→Custom switch converges; stale Anthropic model cleared on Custom switch with Goose effort preserved (Carl P2 regression); Settings-style Save/reread preserves effort.
  • agentDefaultsEditor.test.mjs: two full Save/Next journey tests through the real production parent trees. Both start with GOOSE_THINKING_EFFORT: "low" and operate the real Popover-based effort control (click trigger → click "off" option) before Save/Next, asserting zero writes after selection. The set_global_agent_config stub captures the submitted payload; each test asserts raw GOOSE_THINKING_EFFORT: "off" in the captured config. The stub stores its canonical response from the actual payload; the fresh remount's get_global_agent_config returns that stored object (not a hand-written fixture), then asserts "Off" shown. The DefaultConfigStep test starts with isDirty: false — the real-control effort selection calls onConfigChange → updateDraft → isDirtyRef=true, making the commit() on Next load-bearing.

Mutation evidence

  • Delete let effort = apply_effort_to_spawn_command(...) call from spawn_agent_child → compile error: error[E0425]: cannot find value effort`` at spawn_with_effort_proof site.
  • Delete let applied = apply_record_field_updates(...)? from update_managed_agent → compile error: error[E0425]: cannot find value applied`` at stamp_record_updated_at site.
  • Delete apply_spawn_effort_env from inside apply_effort_to_spawn_command wrapper → production_sequence_goose_inherited_collision_resolved_in_child RED.
  • Revert isHarnessNativeEffort && guard in cleanup useEffect to bare if (isHarnessNativeEffort) return → stale model not cleared → Carl P2 regression test RED.
  • Remove isHarnessNativeEffort || from the nothing-to-clear condition → provider-empty mount emits onConfigChange → loop test RED.
  • Remove isHarnessNativeEffort branch in AgentConfigFields.tsx:634-636 → both agentDefaultsEditor.test.mjs mount assertions fail: trigger shows "Select" instead of initial effort label.
  • Remove preserve_passthrough guard in apply_effort_launch_to_commandproduction_sequence_custom_inherited_acp_sentinel_survives RED.
  • Drop GOOSE_THINKING_EFFORT from the set_global_agent_config stub payload → payload assertion in agentDefaultsEditor.test.mjs fails (undefined !== "off") → RED (verified).
  • Remove the effort-select dirtying steps from the DefaultConfigStep test (so isDirty stays false) → commit() is a no-op → write-count assertion after Next fails (0 instead of 1) → RED.

@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 7 times, most recently from c93e1c4 to febf701 Compare August 4, 2026 18:32
@wpfleger96 wpfleger96 changed the title feat(agents): harness-agnostic effort write-side + spawn legacy bridge feat(agents): harness-agnostic effort write-side + spawn legacy bridge (Goose + Claude Code) Aug 4, 2026
@wpfleger96
wpfleger96 force-pushed the duncan/effort-write-genericize branch from 80df98f to 7c4c183 Compare August 4, 2026 21:58
@wpfleger96 wpfleger96 changed the title feat(agents): harness-agnostic effort write-side + spawn legacy bridge (Goose + Claude Code) feat(agents): harness-agnostic effort write-side + spawn bridge for Goose Aug 12, 2026
@wpfleger96
wpfleger96 force-pushed the duncan/effort-write-genericize branch 3 times, most recently from c60c87c to 5882e44 Compare August 12, 2026 18:00

@kalvinnchau kalvinnchau left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Pin-to-inherit can retain stale effort when the current runtime has no effort key — desktop/src/features/agents/ui/AgentInstanceEditDialog.tsx:500

handleInheritHarnessChange decides whether to clear effort from the pre-toggle prospectiveRuntime. For an agent pinned to Claude (no thinkingEnvVar) whose linked persona uses Goose, selecting Inherit skips the clear. On the next render the prospective runtime becomes Goose, but the record's existing BUZZ_AGENT_THINKING_EFFORT remains; resolveInheritedRuntimeSubmission only excludes effort keys from the persona layer, so that record value is saved and the spawn bridge aliases it into Goose effort. This violates the stated pin↔inherit clear contract and can silently resurrect an effort value from an unrelated prior runtime. Resolve the runtime for nextInherit inside the handler (or clear whenever either the before or after runtime owns effort), and pin the Claude-pin → Goose-persona transition with an integration-level state/submit test.

[P2] Consumed legacy effort is also emitted as an advanced field — desktop/src-tauri/src/managed_agents/config_bridge/reader.rs:125

For a Goose record containing only a valid legacy BUZZ_AGENT_THINKING_EFFORT, build_thinking_field consumes it and exposes canonical thinking_effort, but normalized_env_keys contains only GOOSE_THINKING_EFFORT. The loop at line 140 therefore also emits the legacy key in surface.advanced. The config panel presents one persisted setting twice, with conflicting semantics, and an edit can appear to target a generic env var while the normalized field is already interpreting it. Invalid or unconsumed legacy values should remain advanced, so hide the legacy key only when record-tier alias resolution actually consumed it; add a surface test for valid-consumed versus invalid-visible legacy values.

Validation at exact PR tip 5882e4494656b0ce3deeffb2c1d1c1dea25e47f3:

  • just desktop-test: 4,774 passed, 0 failed
  • just desktop-tauri-test: passed (workspace; one pre-existing ignored native performance test)
  • git diff --check a8e5c89e2..HEAD: clean

@wpfleger96
wpfleger96 force-pushed the duncan/effort-write-genericize branch 4 times, most recently from c4a6e99 to 513a75c Compare August 19, 2026 22:14
@wpfleger96
wpfleger96 force-pushed the duncan/effort-write-genericize branch from 3bbf0e8 to a9eff9e Compare August 25, 2026 14:21
@wpfleger96
wpfleger96 marked this pull request as ready for review August 25, 2026 20:03
@wpfleger96
wpfleger96 requested a review from a team as a code owner August 25, 2026 20:03
@wpfleger96
wpfleger96 force-pushed the duncan/effort-write-genericize branch 2 times, most recently from af3d4d5 to a1778cd Compare August 25, 2026 23:21

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Carl, an automated reviewer, commenting via Wes’s GitHub account.

Changes requested

  • [P2] Preserve custom-runtime effort env in the restart snapshotdesktop/src-tauri/src/managed_agents/spawn_snapshot.rs:181

    The launch projection deliberately uses an empty suppress set for an unknown/custom runtime so wrapper-specific variables such as GOOSE_THINKING_EFFORT remain ordinary pass-through env. SpawnConfigSnapshot::from_inputs, however, unconditionally removes every globally known effort key. For an unknown command, effective_effort only reads BUZZ_ACP_EFFORT_LEVEL, so an exact-case GOOSE_THINKING_EFFORT disappears from both snapshot.env and snapshot.effort_level even though the child receives it. Changing that variable therefore produces no restart diff and can leave a running custom-wrapper agent on the old effort despite auto-restart-on-config-change. Scope snapshot cleanup to the runtime projection: known runtimes can collapse their suppress set, while unknown runtimes must retain non-sentinel effort-looking keys as ordinary env. Please pin the custom-wrapper case with a snapshot regression test.

The authority, normalization, mutation, local/remote launch, and reader paths were otherwise coherent in the read-only diff review. Existing checks on this immutable head are green; PR code was not executed locally under the automation trust policy.

kalvinnchau
kalvinnchau previously approved these changes Aug 26, 2026

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Carl, an automated reviewer, commenting via Wes’s GitHub account.

Changes requested at exact head 239cbdd30cefee7a3a28d02271fc21de5226967f.

P2 — Mixed-case ACP sentinel changes for a custom runtime still disappear from restart snapshots

The new custom-runtime snapshot scoping fixes non-sentinel keys such as GOOSE_THINKING_EFFORT, but it still loses a mixed-case ACP sentinel on Windows.

For an unknown/custom runtime, launch projection deliberately uses an empty suppression set (desktop/src-tauri/src/managed_agents/config_bridge/effort.rs:246-259), so a user-provided buzz_acp_effort_level=high survives into the child environment when no canonical column overwrites it. Windows treats that key as BUZZ_ACP_EFFORT_LEVEL. Snapshot extraction, however, reads the destination with an exact-case map lookup (desktop/src-tauri/src/managed_agents/spawn_snapshot.rs:153-157), yielding no effort_level, then removes every case variant of the sentinel (spawn_snapshot.rs:194-203; config_bridge/effort.rs:208-226). Changing that effective child input therefore changes neither snapshot field, so restart-required remains false and a running custom agent can keep stale effort.

Make unknown-runtime snapshot handling mirror actual projection semantics: either capture the sentinel case-insensitively before stripping it, or retain a pass-through sentinel when projection did not emit/consume it. Add mixed-case custom-runtime regressions with and without a canonical column. The new test at spawn_snapshot/tests_ext.rs:239-275 covers only exact-case GOOSE_THINKING_EFFORT, so it does not exercise this collision.

The prior non-sentinel custom-runtime blocker is otherwise closed. I reviewed immutable GitHub source and diffs only; no PR code was checked out or executed. Hosted checks were green at review time.

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Carl, an automated reviewer, commenting via Wes's GitHub account.

The write/spawn projection is internally consistent, but the global/onboarding Goose Effort control still persists the legacy Buzz key that the new projection intentionally does not consume from the global tier.

  • deriveAgentConfigFieldModel sets currentPersistence.key and its displayed value to BUZZ_AGENT_THINKING_EFFORT for every runtime with thinkingEnvVar, while only targetApplication uses runtime.thinkingEnvVar (desktop/src/features/agents/lib/agentConfigCore.ts:206-220). AgentConfigFields reads and writes through that persistence key (desktop/src/features/agents/ui/AgentConfigFields.tsx:244-257,638-640,876-885). Selecting Goose effort therefore saves the legacy key rather than GOOSE_THINKING_EFFORT.
  • The launch authority accepts the legacy alias only at record/persona scope; its global tier reads the runtime-native key only (desktop/src-tauri/src/managed_agents/config_bridge/effort.rs:362-383). The UI consequently shows the global Goose setting as saved, but the next Goose spawn silently ignores it.

Please make the global/onboarding persistence contract match the launch projection, and add a regression covering global Goose selection through the persisted env to a projected GOOSE_THINKING_EFFORT descriptor.

Hayt and others added 2 commits September 1, 2026 12:47
Drop fireEvent and useRef from the deferred import block; neither is
used in the test suite and biome flagged them as unused variables.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Merge origin/main into duncan/effort-write-genericize. Conflict resolutions:

- agents_deploy.rs: keep HEAD's effort-strip-free strip_key (the projection
  already reduced descriptor.env to one canonical effort key via
  resolve_effective_harness_descriptor; stripping it from launch.env would
  hide the projected value from the remote deploy) plus add ACP session
  policy strip from main.
- runtime.rs: keep HEAD's apply_spawn_effort_env seam, add
  apply_demo_config_home from main.
- spawn_snapshot.rs: keep HEAD's effective_effort(descriptor) call, add
  session_policy field from main.
- spawn_snapshot/tests_ext.rs: retain HEAD's custom-runtime snapshot tests,
  add main's session-policy badge tests.
- effort_tests.rs: compact apply_spawn_effort_env call arguments to reduce
  line count below the 1000-line file-size ratchet (was 1043, now 993).

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…tchet

effort_tests.rs was 1039 lines at the merge head — the merge from origin/main
added 12 session-policy tests that re-inflated the file after the earlier trim.
No coverage was removed; the command-boundary strip and production-sequence
tests are now in effort_cmd_tests.rs declared via #[path] as mod cmd_tests
inside the tests module. Both files are under 1000 lines.

- effort_tests.rs: 701 lines (was 1039)
- effort_cmd_tests.rs: 234 lines (new file)
- All 37 effort tests pass
- just file-size-check passes

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Carl, an automated reviewer, commenting via Wes’s GitHub account.

Changes requested

Reviewed head 7c0278288b24586d3e5a41857e8b7446c7770817 against base bd73490418266f267d9bb3bdf13e64582adc8e80. This is a bounded corrective review of the existing Save-gated, next-spawn native-effort contract, with local-only instance writes and deployment-owned remote configuration.

P2: Preserve model cleanup while preserving native effort

The new unconditional return at AgentConfigFields.tsx:480–496 stops the feedback loop but also skips clearing the previous provider's model.

Source-derived reproduction: in Defaults, select Goose with Anthropic and a model, then choose Custom provider… and enter a different provider ID. The actual Custom selection handler retains config.model; the new native-effort return prevents the one-time cleanup, and the custom input changes only provider. The previous provider-specific model survives into Save and is submitted with the new provider. During the blank Custom state, the model control even presents an empty/disabled value while validity still reads the retained model.

Smallest repair: return only when no eligible field needs clearing. Clear the model once, but preserve native effort; with a null model and native effort alone, emit nothing. Add a stateful-parent regression that invokes the real provider control, checks bounded config-change emissions, model === null, and preserved GOOSE_THINKING_EFFORT=off, then exercises Save/reread.

The current Custom-switch test replaces props directly, never setting userEditedProviderRef, and does not feed changes through React state. The separate Save test has a nonempty provider throughout. Neither establishes the claimed provider-to-Custom convergence proof.

P2: Remove or test-gate the obsolete production strip helper

After runtime moved to apply_spawn_effort_env, effort.rs:257–269 retains strip_effort_keys_from_command as non-test code, but its remaining callers are the old helper tests. The exact-head Desktop Core job fails deterministically in desktop-tauri-clippy:

error: function `strip_effort_keys_from_command` is never used
--> src/managed_agents/config_bridge/effort.rs:257:15
= note: `-D dead-code` implied by `-D warnings`
error: could not compile `buzz-desktop` (lib) due to 1 previous error

Remove the obsolete helper and migrate its assertions to the live boundary, or make it test-only if it still has a useful test role. Do not restore its unconditional production call: that would reintroduce the custom-wrapper environment loss.

Closed findings and validation limits

The prior render loop itself, inherited custom-wrapper environment loss, and Windows /usr/bin/env failure are addressed in source. The exact-head Windows Rust job is successful. The local set/clear/absent persistence seam and env-before-effort ordering remain intact; configured descriptor projection still feeds local launch, deployment, and restart snapshots.

All three independent review lanes returned and were integrated. Review used immutable source/diffs and hosted check metadata/logs only: no checkout, build, test, import, or execution of PR code. Desktop smoke shards 3/4 also report failures; their causes were not investigated or attributed here. Previously deferred live-ACP and baked-reader policies are not reopened. Exit criteria are the two repairs above with the real provider-transition regression and a passing Desktop Core gate.

Hayt and others added 2 commits September 1, 2026 15:57
…l-clear fix

Launch outer seam: wrap apply_spawn_effort_env in apply_effort_to_spawn_command
returning an EffortApplied sentinel. The sentinel binding in spawn_agent_child is
a compile-time requirement — deleting the call is an error. Deleting
apply_spawn_effort_env inside the wrapper turns the production-sequence tests RED.

EnvVarGuard OsString: prior value stored as OsString (var_os) so non-Unicode
values are restored exactly on Drop rather than silently lost.

Crate-wide ENV_MUTEX in managed_agents/mod.rs: mirrors PATH_MUTEX. Effort tests
use lock_env_mutex() instead of a file-local mutex, preventing races with other
env-mutating test files compiled into the same binary.

Dead-code clippy: mark strip_effort_keys_from_command #[cfg(test)] since it is
only called from test code. Fix the import path in effort_cmd_tests.rs
(super::super:: to reach effort.rs past the tests module boundary).

Model cleanup (Carl P2): the orphan-model cleanup effect's isHarnessNativeEffort
early-return was skipping the model clear on provider->Custom transitions. Refined
to: return early only when model is already null; clear model once while preserving
the native-effort key. Adds a mounted regression in effortAutoClear.test.mjs.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…enericize

* origin/main:
  fix(desktop): preserve keyring identity during recovery (#7203)
  feat(mobile): prepare `buzz-push-gateway` for deployment (#7158)
  ci: relax file-size ceilings by surface (#6485)
  fix(mobile): isolate extension linker flags; complete iOS build in CI (#7187)
  chore(ci): lower Codex security review effort (#7179)
  fix(dev-mcp): extend shell timeout cap to 20 minutes and align outer budgets (#7185)
  fix(dev): keep the canonical profile when launching from desktop/ (#7143)
  feat(buzz-auth): add production NIP-FI federated assertion runtime (#7109)
  Hide download action on voice notes (#7182)

Signed-off-by: Hayt <9e1c23a3fd83f61da34420e4e88ff1b16e45cafcc0cd9019eb07d4ecfa8ca9b0@buzz.block.builderlab.xyz>
Hayt and others added 2 commits September 1, 2026 17:08
…y tests

Launch seam (runtime.rs): add #[must_use] to EffortApplied, rename call
site from `let _effort` to `let effort`, add `spawn_with_effort_proof` that
consumes the token. Deleting the apply_effort_to_spawn_command call from
spawn_agent_child is now a compile error (effort undefined at spawn site).
Verified: cargo check --lib with line deleted → error[E0425] cannot find
value `effort`.

Update seam (agent_models_update.rs): add #[must_use] + RecordFieldsApplied
token already in place. stamp_record_updated_at consumes it. Deleting the
apply_record_field_updates call from update_managed_agent is a compile error
(applied undefined at stamp site). Verified: same error[E0425] pattern.

update_managed_agent_writes_effort_via_production_command test: drives the
full production sequence (load → apply_record_field_updates →
stamp_record_updated_at → save → load-from-disk) via mock AppHandle +
tempdir HOME/XDG override. Asserts effort_level persists to disk. #[cfg(not
(target_os = "windows"))] to avoid Windows HOME path differences.

Comment corrections: removed false claim ("deleting the call turns that test
RED" was incorrect since the test calls apply_record_field_updates directly
not via update_managed_agent) and replaced excuse comment in effort.rs with
accurate compile-error description.

Frontend (agentDefaultsEditor.test.mjs): two mounted parent journey tests.
- AgentDefaultsEditor: mount with QueryClientProvider + seeded Goose
  runtime, get_global_agent_config stub returning GOOSE_THINKING_EFFORT=off,
  assert effort custom trigger shows data-value="off" and text "Off".
- DefaultConfigStep: same config with provider populated, assert same
  trigger invariant through the onboarding surface.
Both tests go RED when isHarnessNativeEffort branch is removed from
AgentConfigFields (trigger shows "Select" instead of "Off"). QueryClient
cancelQueries+clear in afterEach prevents gcTime timer stall.

Mutation proofs run and confirmed:
- Deleting let effort = apply_effort_to_spawn_command(...) → E0425
- Deleting let applied = apply_record_field_updates(...) → E0425
- Removing isHarnessNativeEffort branch in AgentConfigFields → both
  agentDefaultsEditor tests fail with "got: \"Select\""

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
now_iso is used in agent_models_update.rs (stamp_record_updated_at) but
not in agent_models.rs. Remove the dead import so clippy -D warnings
does not fail.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Hayt and others added 2 commits September 1, 2026 17:55
The two comments claimed that
update_managed_agent_writes_effort_via_production_command would go RED
on a token-pattern revert (removing apply_record_field_updates from
update_managed_agent). That claim is false: the test calls
apply_record_field_updates directly in its own body, so deleting the
production invocation does not affect it.

The actual outer-seam proof for the production call is the compile
error: apply_record_field_updates returns a #[must_use]
RecordFieldsApplied token consumed by stamp_record_updated_at, so
removing the call leaves applied undefined at the stamp site.

Update both comment sites to describe exactly what each proof covers:
- agent_models_update.rs inline comment: sole proof is the compile error
- agent_models_update_tests.rs section header: compile error is the
  outer-seam gate; integration test proves disk-persistence of the
  function itself (called directly), not the production invocation
- Integration test comment block: reframed as disk-persistence contract
  with clear scope boundary (does not gate production invocation)

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
- AGENTS.md item 14: update from deleted persistAgentEffortLevel
  direct-write contract to the shipped Save-gated update_managed_agent
  effortLevel path. Rewrite is consistent with EffortPickerField's own
  doc comment.

- runtime.rs EffortApplied: add private field (()) so no crate-local
  code can construct the token outside apply_effort_to_spawn_command.
  Same shape as RecordFieldsApplied(()).

- mod.rs: merge PATH_MUTEX and ENV_MUTEX into a single PROCESS_ENV_MUTEX
  static. Both lock_path_mutex() and lock_env_mutex() delegate to it so
  there is exactly one process-env lock domain in the crate; PATH and
  all env-key mutations are mutually exclusive.

- agent_models_update_tests.rs: replace manual HOME/XDG_DATA_HOME
  restore with RAII EnvVarGuard (OsString, panic-safe Drop); update
  comment to name lock_path_mutex as the single-domain gate. Rename
  test from update_managed_agent_writes_effort_via_production_command
  to record_field_updates_persist_effort_to_disk — the test drives
  apply_record_field_updates directly and does not independently gate
  the production update_managed_agent invocation. Update outer-seam
  comment to reflect the rename.

- agentDefaultsEditor.test.mjs: extend both tests from mount+assert to
  full Save/Next journey tests through the real parent trees.
  AgentDefaultsEditor: mount → assert 0 writes + Off shown → dirty via
  Advanced env-var editor → assert still 0 writes → click real Save
  defaults button → assert 1 write → unmount/remount fresh → assert 0
  additional writes + Off still shown.
  DefaultConfigStep: same contract via real Next button (commit() fires
  because isDirty=true in draft).

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
… journey tests

Thufir p3 IMPORTANT 1: the save→reread linkage was fabricated — neither test
operated the effort control, neither captured the submitted payload, and the
fresh-remount fixture hand-wrote GOOSE_THINKING_EFFORT: "off" back regardless
of what was actually saved.

Changes:
- Both tests now start with GOOSE_THINKING_EFFORT: "low" and select "off"
  via the real Popover trigger + option click (AgentDropdownSelect exposes
  options as <button data-testid="{testId}-option-{value}">, so fireEvent.click
  works without pointer-event emulation).
- The set_global_agent_config stub captures the submitted payload and stores
  the canonical response from it; the fresh parent's get_global_agent_config
  returns that stored object, not a hand-written fixture.
- Each test now asserts raw GOOSE_THINKING_EFFORT: "off" in the captured
  payload. Mutation check confirmed: dropping the key from the payload turns
  the assertion RED (undefined !== "off").
- DefaultConfigStep test removes isDirty: true — the real-control effort
  selection calls onConfigChange → updateDraft → isDirtyRef=true, so commit()
  fires on Next without pre-seeding.
- Wording fix in managed_agents/mod.rs: narrows the mutex comment to its
  actual scope (callers of lock_path_mutex/lock_env_mutex only; independent
  locks in app_state_tests/agent_config_tests/reader_tests are NOT covered).
- AGENTS.md item 14: removes the stale "does not thread new props through the
  dialog / keep effort state inside section component" sentence (code stores
  effortLevel in AgentInstanceEditDialog state and passes value/onChange props).

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96 wpfleger96 changed the title feat(agents): harness-agnostic effort write-side + spawn bridge for Goose feat(agents): harness-agnostic effort write path and spawn bridge Sep 2, 2026

@kalvinnchau kalvinnchau left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Approved at 4caa6117322ac0f1caf20d7e8976a93b1cbf1bf1. No findings in the final delta. The parent journeys now bind real effort selection to zero pre-save writes, the exact Save/Next payload, and a fresh reread of the stored response; the mutex and agent guidance now state only what the implementation guarantees. Independent targeted run: 7/7 passed. Scores: minimalness 9/10, elegance 9/10, correctness 9/10. Residual risk: no installed-Goose end-to-end run at this head; real child-process environment coverage remains the terminal automated boundary.

@wpfleger96
wpfleger96 merged commit 0dbd036 into main Sep 2, 2026
77 of 78 checks passed
@wpfleger96
wpfleger96 deleted the duncan/effort-write-genericize branch September 2, 2026 18:25
wpfleger96 pushed a commit that referenced this pull request Sep 2, 2026
* origin/main:
  feat(agents): harness-agnostic effort write path and spawn bridge (#4625)
  chore(db): drop Phase-A NIP-FI relay-side authority ledger (#7221)
  fix(acp): replace real user name in base prompt mention example (#7250)
  ci: split CI into reusable workflows (#7168)
  fix(desktop): retain automatic mentions only in threads (#7144)
  feat: add databricks fable 5.1 model capabilities (#7213)
  docs(nip-fi): rewrite NIP-FI as stateless OSS Buzz spec v2 (#7214)
  feat(relay): add detailed readiness metrics (#7149)
  feat(desktop): add Pi agent preset (#7208)

Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>

# Conflicts:
#	desktop/src-tauri/src/commands/agent_models_update.rs
#	desktop/src-tauri/src/commands/agents_deploy.rs
#	desktop/src-tauri/src/managed_agents/types/requests.rs
#	desktop/src/features/agents/ui/AgentInstanceEditDialog.tsx
#	desktop/src/shared/api/types.ts
wpfleger96 pushed a commit that referenced this pull request Sep 2, 2026
…agent-edit

* origin/main:
  feat(desktop): add persistent Bestie experience (#7223)
  fix(desktop): harden profile batch and thread-reply fetches against relay slowness (#7188)
  docs(nip-fi): adopt deny-until-TTL and extend enforcement to HTTP ingress (#7254)
  fix(composer): align wrapped inline chip fragments (#7242)
  Add operation-aware database pool acquisition metrics (#7195)
  fix(desktop): keep explicit agent profiles bound to their exact key (#7131)
  fix(desktop): discover authenticated owned relay agents (#7122)
  feat(agents): harness-agnostic effort write path and spawn bridge (#4625)
  chore(db): drop Phase-A NIP-FI relay-side authority ledger (#7221)
  fix(acp): replace real user name in base prompt mention example (#7250)
  ci: split CI into reusable workflows (#7168)
  fix(desktop): retain automatic mentions only in threads (#7144)
  feat: add databricks fable 5.1 model capabilities (#7213)
  docs(nip-fi): rewrite NIP-FI as stateless OSS Buzz spec v2 (#7214)
  feat(relay): add detailed readiness metrics (#7149)
  feat(desktop): add Pi agent preset (#7208)

Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>

# Conflicts:
#	desktop/src/features/agents/ui/AgentInstanceEditDialog.tsx
#	desktop/src/features/agents/ui/agentInstanceEditPinning.test.mjs
wpfleger96 added a commit that referenced this pull request Sep 2, 2026
EffortPickerField gained required disabled/value/onChange props in #4625.
Wire them through the merged dialog using the same Save-gated pattern as the
now-deleted AgentInstanceEditDialog: effortLevel state + effortTouched ref in
AgentEditMergedDialog, passed as effortValue/onEffortChange to the instance
section, which forwards to EffortPickerField.

In useAgentEditMergedSubmit, add effortLevel/effortTouched to AgentEditSubmitState
and include effortLevel in the locked update_managed_agent call when touched.
When no other I-fields changed (agentInput would be null), synthesise a
minimal input from inst.pubkey so the effort write is still atomic.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
wpfleger96 added a commit that referenced this pull request Sep 3, 2026
…runtime-switch reset

IMPORTANT-1: Add effortLevel comparison to observedStateMatchesAgentInput
(agentSaveCoordinator.ts) with tri-state semantics — absent submission skips,
null clears, string compares against observed column. ManagedAgent now exposes
effortLevel from ManagedAgentSummary (Rust types.rs + runtime.rs) so the
comparator can detect a backend-rejected effort write rather than closing as
success. Regressions: 4 family-14 coordinator tests; mutation (remove compare)
turns the 2 rejected-edit tests RED while the 2 settled tests stay GREEN.

IMPORTANT-2: Replace raw effortLevel include in useAgentEditMergedSubmit with
resolveEffortSubmission — suppresses effort on pin→inherit (agentCommand:"")
and on unchanged selections, matching the deleted AgentInstanceEditDialog's
PR #4625 semantics exactly. originalEffortLevel threaded from
configSurfaceQuery into AgentEditSubmitState.

IMPORTANT-3: Add setEffortLevel and effortTouched to RuntimeHandlersInput;
call setEffortLevel(null) and effortTouched.current = false in
handleRuntimeDropdownChange (useAgentEditRuntimeHandlers.ts) so runtime
switches do not carry stale vocabulary values into the next Save. Port the
6 load-bearing mounted production-seam tests from the deleted
agentInstanceEditCancelSafety.test.mjs to agentEditMergedCancelSafety.test.mjs
against AgentEditMergedDialog: selection+Cancel zero writes, ordinary Save
carries effortLevel, rejected Save stays open, pin→inherit suppresses effort,
access+effort atomic. Runtime-switch reset covered by hook-level test in
agentEditMergedRuntimeReset.test.mjs.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
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.

3 participants